2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.yangtools.yang.parser.repo;
11 import static org.hamcrest.CoreMatchers.both;
12 import static org.hamcrest.CoreMatchers.hasItem;
13 import static org.hamcrest.MatcherAssert.assertThat;
14 import static org.junit.Assert.assertEquals;
15 import static org.junit.Assert.assertFalse;
16 import static org.junit.Assert.assertNotNull;
17 import static org.junit.Assert.assertSame;
18 import static org.junit.Assert.assertTrue;
19 import static org.junit.Assert.fail;
20 import static org.mockito.Mockito.spy;
21 import static org.mockito.Mockito.times;
22 import static org.mockito.Mockito.verify;
23 import static org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter.ALWAYS_ACCEPT;
25 import com.google.common.base.Function;
26 import com.google.common.base.MoreObjects.ToStringHelper;
27 import com.google.common.base.Optional;
28 import com.google.common.collect.Collections2;
29 import com.google.common.collect.ImmutableList;
30 import com.google.common.io.Files;
31 import com.google.common.util.concurrent.CheckedFuture;
32 import com.google.common.util.concurrent.FutureCallback;
33 import com.google.common.util.concurrent.Futures;
34 import java.io.ByteArrayInputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.nio.charset.StandardCharsets;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.concurrent.ExecutionException;
43 import javax.annotation.Nonnull;
44 import org.junit.Test;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
47 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
48 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
49 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
50 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
51 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
52 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
53 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
54 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
55 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
56 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
57 import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
58 import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
60 public class SharedSchemaRepositoryTest {
63 public void testSourceWithAndWithoutRevision() throws Exception {
64 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
66 final SourceIdentifier idNoRevision = loadAndRegisterSource(sharedSchemaRepository,
67 "/no-revision/imported.yang");
68 final SourceIdentifier id2 = loadAndRegisterSource(sharedSchemaRepository,
69 "/no-revision/imported@2012-12-12.yang");
71 CheckedFuture<ASTSchemaSource, SchemaSourceException> source = sharedSchemaRepository.getSchemaSource(
72 idNoRevision, ASTSchemaSource.class);
73 assertEquals(idNoRevision, source.checkedGet().getIdentifier());
74 source = sharedSchemaRepository.getSchemaSource(id2, ASTSchemaSource.class);
75 assertEquals(id2, source.checkedGet().getIdentifier());
78 private static SourceIdentifier loadAndRegisterSource(final SharedSchemaRepository sharedSchemaRepository,
79 final String resourceName) throws Exception {
80 final SettableSchemaProvider<ASTSchemaSource> sourceProvider = getImmediateYangSourceProviderFromResource(
82 sourceProvider.setResult();
83 final SourceIdentifier idNoRevision = sourceProvider.getId();
84 sourceProvider.register(sharedSchemaRepository);
89 public void testSimpleSchemaContext() throws Exception {
90 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
92 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
93 "/ietf/ietf-inet-types@2010-09-24.yang");
94 remoteInetTypesYang.register(sharedSchemaRepository);
95 final CheckedFuture<ASTSchemaSource, SchemaSourceException> registeredSourceFuture = sharedSchemaRepository
96 .getSchemaSource(remoteInetTypesYang.getId(), ASTSchemaSource.class);
97 assertFalse(registeredSourceFuture.isDone());
99 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT);
100 final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
101 = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId()));
103 assertFalse(schemaContextFuture.isDone());
105 // Make source appear
106 remoteInetTypesYang.setResult();
107 assertEquals(remoteInetTypesYang.getSchemaSourceRepresentation(), registeredSourceFuture.get());
109 // Verify schema created successfully
110 assertTrue(schemaContextFuture.isDone());
111 final SchemaContext firstSchemaContext = schemaContextFuture.checkedGet();
112 assertSchemaContext(firstSchemaContext, 1);
114 // Try same schema second time
115 final CheckedFuture<SchemaContext, SchemaResolutionException> secondSchemaFuture =
116 sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT)
117 .createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId()));
119 // Verify second schema created successfully immediately
120 assertTrue(secondSchemaFuture.isDone());
121 // Assert same context instance is returned from first and second attempt
122 assertSame(firstSchemaContext, secondSchemaFuture.checkedGet());
126 public void testTwoSchemaContextsSharingSource() throws Exception {
127 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
129 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
130 "/ietf/ietf-inet-types@2010-09-24.yang");
131 remoteInetTypesYang.register(sharedSchemaRepository);
132 remoteInetTypesYang.setResult();
133 final SettableSchemaProvider<ASTSchemaSource> remoteTopologyYang = getImmediateYangSourceProviderFromResource(
134 "/ietf/network-topology@2013-10-21.yang");
135 remoteTopologyYang.register(sharedSchemaRepository);
136 remoteTopologyYang.setResult();
137 final SettableSchemaProvider<ASTSchemaSource> remoteModuleNoRevYang = getImmediateYangSourceProviderFromResource(
138 "/no-revision/module-without-revision.yang");
139 remoteModuleNoRevYang.register(sharedSchemaRepository);
141 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT);
142 final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndTopologySchemaContextFuture
143 = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId(), remoteTopologyYang.getId()));
144 assertTrue(inetAndTopologySchemaContextFuture.isDone());
145 assertSchemaContext(inetAndTopologySchemaContextFuture.checkedGet(), 2);
147 final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndNoRevSchemaContextFuture
148 = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId(),
149 remoteModuleNoRevYang.getId()));
150 assertFalse(inetAndNoRevSchemaContextFuture.isDone());
152 remoteModuleNoRevYang.setResult();
153 assertTrue(inetAndNoRevSchemaContextFuture.isDone());
154 assertSchemaContext(inetAndNoRevSchemaContextFuture.checkedGet(), 2);
158 public void testFailedSchemaContext() throws Exception {
159 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
161 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
162 "/ietf/ietf-inet-types@2010-09-24.yang");
163 remoteInetTypesYang.register(sharedSchemaRepository);
165 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT);
167 // Make source appear
168 final Throwable ex = new IllegalStateException("failed schema");
169 remoteInetTypesYang.setException(ex);
171 final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
172 = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId()));
175 schemaContextFuture.checkedGet();
176 } catch (final SchemaResolutionException e) {
177 assertNotNull(e.getCause());
178 assertNotNull(e.getCause().getCause());
179 assertSame(ex, e.getCause().getCause());
183 fail("Schema context creation should have failed");
187 public void testDifferentCosts() throws Exception {
188 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
190 final SettableSchemaProvider<ASTSchemaSource> immediateInetTypesYang = spy(
191 getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
192 immediateInetTypesYang.register(sharedSchemaRepository);
193 immediateInetTypesYang.setResult();
195 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = spy(
196 getRemoteYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
197 remoteInetTypesYang.register(sharedSchemaRepository);
198 remoteInetTypesYang.setResult();
200 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT);
202 final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
203 = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId()));
205 assertSchemaContext(schemaContextFuture.checkedGet(), 1);
207 final SourceIdentifier id = immediateInetTypesYang.getId();
208 verify(remoteInetTypesYang, times(0)).getSource(id);
209 verify(immediateInetTypesYang).getSource(id);
213 public void testWithCacheStartup() throws Exception {
214 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
216 class CountingSchemaListener implements SchemaSourceListener {
217 List<PotentialSchemaSource<?>> registeredSources = new ArrayList<>();
220 public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
224 public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
225 for (final PotentialSchemaSource<?> source : sources) {
226 registeredSources.add(source);
231 public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
235 final File storageDir = Files.createTempDir();
237 final CountingSchemaListener listener = new CountingSchemaListener();
238 sharedSchemaRepository.registerSchemaSourceListener(listener);
240 final File test = new File(storageDir, "test.yang");
241 Files.write("content-test", test, StandardCharsets.UTF_8);
243 final File test2 = new File(storageDir, "test@2012-12-12.yang");
244 Files.write("content-test-2012", test2, StandardCharsets.UTF_8);
246 final File test3 = new File(storageDir, "test@2013-12-12.yang");
247 Files.write("content-test-2013", test3, StandardCharsets.UTF_8);
249 final File test4 = new File(storageDir, "module@2010-12-12.yang");
250 Files.write("content-module-2010", test4, StandardCharsets.UTF_8);
253 final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(
254 sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
255 sharedSchemaRepository.registerSchemaSourceListener(cache);
257 assertEquals(4, listener.registeredSources.size());
259 final Function<PotentialSchemaSource<?>, SourceIdentifier> potSourceToSID =
260 PotentialSchemaSource::getSourceIdentifier;
261 assertThat(Collections2.transform(listener.registeredSources, potSourceToSID),
262 both(hasItem(RevisionSourceIdentifier.create("test", Optional.absent())))
263 .and(hasItem(RevisionSourceIdentifier.create("test", Optional.of("2012-12-12"))))
264 .and(hasItem(RevisionSourceIdentifier.create("test", Optional.of("2013-12-12"))))
265 .and(hasItem(RevisionSourceIdentifier.create("module", Optional.of("2010-12-12"))))
270 public void testWithCacheRunning() throws Exception {
271 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
273 final File storageDir = Files.createTempDir();
275 final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(
276 sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
277 sharedSchemaRepository.registerSchemaSourceListener(cache);
279 final SourceIdentifier runningId = RevisionSourceIdentifier.create("running", Optional.of("2012-12-12"));
281 sharedSchemaRepository.registerSchemaSource(sourceIdentifier -> Futures.immediateCheckedFuture(
282 new YangTextSchemaSource(runningId) {
284 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
285 return toStringHelper;
289 public InputStream openStream() throws IOException {
290 return new ByteArrayInputStream("running".getBytes(StandardCharsets.UTF_8));
292 }), PotentialSchemaSource.create(runningId, YangTextSchemaSource.class,
293 PotentialSchemaSource.Costs.REMOTE_IO.getValue()));
295 final TextToASTTransformer transformer = TextToASTTransformer.create(sharedSchemaRepository,
296 sharedSchemaRepository);
297 sharedSchemaRepository.registerSchemaSourceListener(transformer);
299 // Request schema to make repository notify the cache
300 final CheckedFuture<SchemaContext, SchemaResolutionException> schemaFuture = sharedSchemaRepository
301 .createSchemaContextFactory(ALWAYS_ACCEPT).createSchemaContext(ImmutableList.of(runningId));
302 Futures.addCallback(schemaFuture, new FutureCallback<SchemaContext>() {
304 public void onSuccess(final SchemaContext result) {
305 fail("Creation of schema context should fail from non-regular sources");
309 public void onFailure(@Nonnull final Throwable t) {
310 // Creation of schema context fails, since we do not provide regular sources, but we just want
312 final List<File> cachedSchemas = Arrays.asList(storageDir.listFiles());
313 assertEquals(1, cachedSchemas.size());
314 assertEquals(Files.getNameWithoutExtension(cachedSchemas.get(0).getName()), "running@2012-12-12");
320 } catch (final ExecutionException e) {
321 assertNotNull(e.getCause());
322 assertEquals(MissingSchemaSourceException.class, e.getCause().getClass());
326 fail("Creation of schema context should fail from non-regular sources");
329 private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) {
330 assertNotNull(schemaContext);
331 assertEquals(moduleSize, schemaContext.getModules().size());
334 static SettableSchemaProvider<ASTSchemaSource> getRemoteYangSourceProviderFromResource(final String resourceName)
336 final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
337 return SettableSchemaProvider.createRemote(TextToASTTransformer.transformText(yangSource),
338 ASTSchemaSource.class);
341 static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(final String resourceName)
343 final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
344 return SettableSchemaProvider.createImmediate(TextToASTTransformer.transformText(yangSource),
345 ASTSchemaSource.class);