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
8 package org.opendaylight.yangtools.yang.parser.repo;
10 import static org.hamcrest.CoreMatchers.both;
11 import static org.hamcrest.CoreMatchers.hasItem;
12 import static org.hamcrest.MatcherAssert.assertThat;
13 import static org.junit.Assert.assertEquals;
14 import static org.junit.Assert.assertFalse;
15 import static org.junit.Assert.assertNotNull;
16 import static org.junit.Assert.assertSame;
17 import static org.junit.Assert.assertTrue;
18 import static org.junit.Assert.fail;
19 import static org.mockito.Mockito.spy;
20 import static org.mockito.Mockito.times;
21 import static org.mockito.Mockito.verify;
22 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
24 import com.google.common.base.MoreObjects.ToStringHelper;
25 import com.google.common.collect.Lists;
26 import com.google.common.io.Files;
27 import com.google.common.util.concurrent.FutureCallback;
28 import com.google.common.util.concurrent.Futures;
29 import com.google.common.util.concurrent.ListenableFuture;
30 import com.google.common.util.concurrent.MoreExecutors;
31 import java.io.ByteArrayInputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.nio.charset.StandardCharsets;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.Optional;
40 import java.util.concurrent.ExecutionException;
41 import org.junit.Test;
42 import org.opendaylight.yangtools.yang.common.Revision;
43 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
45 import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
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.SchemaSourceRepresentation;
49 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
50 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
51 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
52 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
53 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
54 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
55 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
57 public class SharedSchemaRepositoryTest {
60 public void testSourceWithAndWithoutRevision() throws Exception {
61 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
63 final SourceIdentifier idNoRevision = loadAndRegisterSource(sharedSchemaRepository,
64 "/no-revision/imported.yang");
65 final SourceIdentifier id2 = loadAndRegisterSource(sharedSchemaRepository,
66 "/no-revision/imported@2012-12-12.yang");
68 ListenableFuture<ASTSchemaSource> source = sharedSchemaRepository.getSchemaSource(idNoRevision,
69 ASTSchemaSource.class);
70 assertEquals(idNoRevision, source.get().getIdentifier());
71 source = sharedSchemaRepository.getSchemaSource(id2, ASTSchemaSource.class);
72 assertEquals(id2, source.get().getIdentifier());
75 private static SourceIdentifier loadAndRegisterSource(final SharedSchemaRepository sharedSchemaRepository,
76 final String resourceName) throws Exception {
77 final SettableSchemaProvider<ASTSchemaSource> sourceProvider = getImmediateYangSourceProviderFromResource(
79 sourceProvider.setResult();
80 final SourceIdentifier idNoRevision = sourceProvider.getId();
81 sourceProvider.register(sharedSchemaRepository);
86 public void testSimpleSchemaContext() throws Exception {
87 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
89 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
90 "/ietf/ietf-inet-types@2010-09-24.yang");
91 remoteInetTypesYang.register(sharedSchemaRepository);
92 final ListenableFuture<ASTSchemaSource> registeredSourceFuture = sharedSchemaRepository.getSchemaSource(
93 remoteInetTypesYang.getId(), ASTSchemaSource.class);
94 assertFalse(registeredSourceFuture.isDone());
96 final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
97 final ListenableFuture<EffectiveModelContext> schemaContextFuture =
98 fact.createEffectiveModelContext(remoteInetTypesYang.getId());
100 assertFalse(schemaContextFuture.isDone());
102 // Make source appear
103 remoteInetTypesYang.setResult();
104 assertEquals(remoteInetTypesYang.getSchemaSourceRepresentation(), registeredSourceFuture.get());
106 // Verify schema created successfully
107 assertTrue(schemaContextFuture.isDone());
108 final SchemaContext firstSchemaContext = schemaContextFuture.get();
109 assertSchemaContext(firstSchemaContext, 1);
111 // Try same schema second time
112 final ListenableFuture<EffectiveModelContext> secondSchemaFuture =
113 sharedSchemaRepository.createEffectiveModelContextFactory().createEffectiveModelContext(
114 remoteInetTypesYang.getId());
116 // Verify second schema created successfully immediately
117 assertTrue(secondSchemaFuture.isDone());
118 // Assert same context instance is returned from first and second attempt
119 assertSame(firstSchemaContext, secondSchemaFuture.get());
123 public void testTwoSchemaContextsSharingSource() throws Exception {
124 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
126 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
127 "/ietf/ietf-inet-types@2010-09-24.yang");
128 remoteInetTypesYang.register(sharedSchemaRepository);
129 remoteInetTypesYang.setResult();
130 final SettableSchemaProvider<ASTSchemaSource> remoteTopologyYang = getImmediateYangSourceProviderFromResource(
131 "/ietf/network-topology@2013-10-21.yang");
132 remoteTopologyYang.register(sharedSchemaRepository);
133 remoteTopologyYang.setResult();
134 final SettableSchemaProvider<ASTSchemaSource> remoteModuleNoRevYang =
135 getImmediateYangSourceProviderFromResource("/no-revision/module-without-revision.yang");
136 remoteModuleNoRevYang.register(sharedSchemaRepository);
138 final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
139 final ListenableFuture<EffectiveModelContext> inetAndTopologySchemaContextFuture = fact
140 .createEffectiveModelContext(remoteInetTypesYang.getId(), remoteTopologyYang.getId());
141 assertTrue(inetAndTopologySchemaContextFuture.isDone());
142 assertSchemaContext(inetAndTopologySchemaContextFuture.get(), 2);
144 final ListenableFuture<EffectiveModelContext> inetAndNoRevSchemaContextFuture =
145 fact.createEffectiveModelContext(remoteInetTypesYang.getId(), remoteModuleNoRevYang.getId());
146 assertFalse(inetAndNoRevSchemaContextFuture.isDone());
148 remoteModuleNoRevYang.setResult();
149 assertTrue(inetAndNoRevSchemaContextFuture.isDone());
150 assertSchemaContext(inetAndNoRevSchemaContextFuture.get(), 2);
154 public void testFailedSchemaContext() throws Exception {
155 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
157 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
158 "/ietf/ietf-inet-types@2010-09-24.yang");
159 remoteInetTypesYang.register(sharedSchemaRepository);
161 final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
163 // Make source appear
164 final Throwable ex = new IllegalStateException("failed schema");
165 remoteInetTypesYang.setException(ex);
167 final ListenableFuture<EffectiveModelContext> schemaContextFuture = fact.createEffectiveModelContext(
168 remoteInetTypesYang.getId());
171 schemaContextFuture.get();
172 } catch (final ExecutionException e) {
173 assertNotNull(e.getCause());
174 assertNotNull(e.getCause().getCause());
175 assertSame(ex, e.getCause().getCause());
179 fail("Schema context creation should have failed");
183 public void testDifferentCosts() throws Exception {
184 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
186 final SettableSchemaProvider<ASTSchemaSource> immediateInetTypesYang = spy(
187 getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
188 immediateInetTypesYang.register(sharedSchemaRepository);
189 immediateInetTypesYang.setResult();
191 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = spy(
192 getRemoteYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
193 remoteInetTypesYang.register(sharedSchemaRepository);
194 remoteInetTypesYang.setResult();
196 final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
197 final ListenableFuture<EffectiveModelContext> schemaContextFuture =
198 fact.createEffectiveModelContext(remoteInetTypesYang.getId());
200 assertSchemaContext(schemaContextFuture.get(), 1);
202 final SourceIdentifier id = immediateInetTypesYang.getId();
203 verify(remoteInetTypesYang, times(0)).getSource(id);
204 verify(immediateInetTypesYang).getSource(id);
208 public void testWithCacheStartup() throws Exception {
209 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
211 class CountingSchemaListener implements SchemaSourceListener {
212 List<PotentialSchemaSource<?>> registeredSources = new ArrayList<>();
215 public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
219 public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
220 for (final PotentialSchemaSource<?> source : sources) {
221 registeredSources.add(source);
226 public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
230 final File storageDir = Files.createTempDir();
232 final CountingSchemaListener listener = new CountingSchemaListener();
233 sharedSchemaRepository.registerSchemaSourceListener(listener);
235 final File test = new File(storageDir, "test.yang");
236 Files.asCharSink(test, StandardCharsets.UTF_8).write("content-test");
238 final File test2 = new File(storageDir, "test@2012-12-12.yang");
239 Files.asCharSink(test2, StandardCharsets.UTF_8).write("content-test-2012");
241 final File test3 = new File(storageDir, "test@2013-12-12.yang");
242 Files.asCharSink(test3, StandardCharsets.UTF_8).write("content-test-2013");
244 final File test4 = new File(storageDir, "module@2010-12-12.yang");
245 Files.asCharSink(test4, StandardCharsets.UTF_8).write("content-module-2010");
247 final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(
248 sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
249 sharedSchemaRepository.registerSchemaSourceListener(cache);
251 assertEquals(4, listener.registeredSources.size());
253 assertThat(Lists.transform(listener.registeredSources, PotentialSchemaSource::getSourceIdentifier),
254 both(hasItem(RevisionSourceIdentifier.create("test", Optional.empty())))
255 .and(hasItem(RevisionSourceIdentifier.create("test", Revision.of("2012-12-12"))))
256 .and(hasItem(RevisionSourceIdentifier.create("test", Revision.of("2013-12-12"))))
257 .and(hasItem(RevisionSourceIdentifier.create("module", Revision.of("2010-12-12"))))
262 public void testWithCacheRunning() throws Exception {
263 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
265 final File storageDir = Files.createTempDir();
267 final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(
268 sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
269 sharedSchemaRepository.registerSchemaSourceListener(cache);
271 final SourceIdentifier runningId = RevisionSourceIdentifier.create("running", Revision.of("2012-12-12"));
273 sharedSchemaRepository.registerSchemaSource(sourceIdentifier -> immediateFluentFuture(
274 new YangTextSchemaSource(runningId) {
276 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
277 return toStringHelper;
281 public InputStream openStream() throws IOException {
282 return new ByteArrayInputStream("running".getBytes(StandardCharsets.UTF_8));
284 }), PotentialSchemaSource.create(runningId, YangTextSchemaSource.class,
285 PotentialSchemaSource.Costs.REMOTE_IO.getValue()));
287 final TextToASTTransformer transformer = TextToASTTransformer.create(sharedSchemaRepository,
288 sharedSchemaRepository);
289 sharedSchemaRepository.registerSchemaSourceListener(transformer);
291 // Request schema to make repository notify the cache
292 final ListenableFuture<EffectiveModelContext> schemaFuture = sharedSchemaRepository
293 .createEffectiveModelContextFactory()
294 .createEffectiveModelContext(runningId);
295 Futures.addCallback(schemaFuture, new FutureCallback<SchemaContext>() {
297 public void onSuccess(final SchemaContext result) {
298 fail("Creation of schema context should fail from non-regular sources");
302 public void onFailure(final Throwable cause) {
303 // Creation of schema context fails, since we do not provide regular sources, but we just want
305 final List<File> cachedSchemas = Arrays.asList(storageDir.listFiles());
306 assertEquals(1, cachedSchemas.size());
307 assertEquals(Files.getNameWithoutExtension(cachedSchemas.get(0).getName()), "running@2012-12-12");
309 }, MoreExecutors.directExecutor());
313 } catch (final ExecutionException e) {
314 assertNotNull(e.getCause());
315 assertEquals(MissingSchemaSourceException.class, e.getCause().getClass());
319 fail("Creation of schema context should fail from non-regular sources");
322 private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) {
323 assertNotNull(schemaContext);
324 assertEquals(moduleSize, schemaContext.getModules().size());
327 static SettableSchemaProvider<ASTSchemaSource> getRemoteYangSourceProviderFromResource(final String resourceName)
329 final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
330 return SettableSchemaProvider.createRemote(TextToASTTransformer.transformText(yangSource),
331 ASTSchemaSource.class);
334 static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(final String resourceName)
336 final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
337 return SettableSchemaProvider.createImmediate(TextToASTTransformer.transformText(yangSource),
338 ASTSchemaSource.class);