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;
24 import com.google.common.base.Function;
25 import com.google.common.base.MoreObjects;
26 import com.google.common.base.Optional;
27 import com.google.common.collect.Collections2;
28 import com.google.common.collect.Lists;
29 import com.google.common.io.Files;
30 import com.google.common.util.concurrent.CheckedFuture;
31 import com.google.common.util.concurrent.FutureCallback;
32 import com.google.common.util.concurrent.Futures;
33 import java.io.ByteArrayInputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.nio.charset.StandardCharsets;
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.concurrent.ExecutionException;
41 import org.junit.Test;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
44 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
45 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
46 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
47 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
48 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
49 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
50 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
51 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
52 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
53 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
54 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
55 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
56 import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
57 import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
59 public class SharedSchemaRepositoryTest {
62 public void testSourceWithAndWithoutRevision() throws Exception {
63 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
65 final SourceIdentifier idNoRevision = loadAndRegisterSource(sharedSchemaRepository, "/no-revision/imported.yang");
66 final SourceIdentifier id2 = loadAndRegisterSource(sharedSchemaRepository, "/no-revision/imported@2012-12-12.yang");
68 CheckedFuture<ASTSchemaSource, SchemaSourceException> source = sharedSchemaRepository.getSchemaSource(idNoRevision, ASTSchemaSource.class);
69 assertEquals(idNoRevision, source.checkedGet().getIdentifier());
70 source = sharedSchemaRepository.getSchemaSource(id2, ASTSchemaSource.class);
71 assertEquals(id2, source.checkedGet().getIdentifier());
74 private static SourceIdentifier loadAndRegisterSource(final SharedSchemaRepository sharedSchemaRepository, final String resourceName) throws Exception {
75 final SettableSchemaProvider<ASTSchemaSource> sourceProvider = getImmediateYangSourceProviderFromResource(resourceName);
76 sourceProvider.setResult();
77 final SourceIdentifier idNoRevision = sourceProvider.getId();
78 sourceProvider.register(sharedSchemaRepository);
83 public void testSimpleSchemaContext() throws Exception {
84 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
86 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang");
87 remoteInetTypesYang.register(sharedSchemaRepository);
88 final CheckedFuture<ASTSchemaSource, SchemaSourceException> registeredSourceFuture = sharedSchemaRepository.getSchemaSource(remoteInetTypesYang.getId(), ASTSchemaSource.class);
89 assertFalse(registeredSourceFuture.isDone());
91 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
93 final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
94 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
96 assertFalse(schemaContextFuture.isDone());
99 remoteInetTypesYang.setResult();
100 assertEquals(remoteInetTypesYang.getSchemaSourceRepresentation(), registeredSourceFuture.get());
102 // Verify schema created successfully
103 assertTrue(schemaContextFuture.isDone());
104 final SchemaContext firstSchemaContext = schemaContextFuture.checkedGet();
105 assertSchemaContext(firstSchemaContext, 1);
107 // Try same schema second time
108 final CheckedFuture<SchemaContext, SchemaResolutionException> secondSchemaFuture =
109 sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT)
110 .createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
112 // Verify second schema created successfully immediately
113 assertTrue(secondSchemaFuture.isDone());
114 // Assert same context instance is returned from first and second attempt
115 assertSame(firstSchemaContext, secondSchemaFuture.checkedGet());
119 public void testTwoSchemaContextsSharingSource() throws Exception {
120 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
122 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang");
123 remoteInetTypesYang.register(sharedSchemaRepository);
124 remoteInetTypesYang.setResult();
125 final SettableSchemaProvider<ASTSchemaSource> remoteTopologyYang = getImmediateYangSourceProviderFromResource("/ietf/network-topology@2013-10-21.yang");
126 remoteTopologyYang.register(sharedSchemaRepository);
127 remoteTopologyYang.setResult();
128 final SettableSchemaProvider<ASTSchemaSource> remoteModuleNoRevYang = getImmediateYangSourceProviderFromResource("/no-revision/module-without-revision.yang");
129 remoteModuleNoRevYang.register(sharedSchemaRepository);
131 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
133 final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndTopologySchemaContextFuture
134 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId(), remoteTopologyYang.getId()));
135 assertTrue(inetAndTopologySchemaContextFuture.isDone());
136 assertSchemaContext(inetAndTopologySchemaContextFuture.checkedGet(), 2);
138 final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndNoRevSchemaContextFuture
139 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId(), remoteModuleNoRevYang.getId()));
140 assertFalse(inetAndNoRevSchemaContextFuture.isDone());
142 remoteModuleNoRevYang.setResult();
143 assertTrue(inetAndNoRevSchemaContextFuture.isDone());
144 assertSchemaContext(inetAndNoRevSchemaContextFuture.checkedGet(), 2);
148 public void testFailedSchemaContext() throws Exception {
149 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
151 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang");
152 remoteInetTypesYang.register(sharedSchemaRepository);
154 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
156 // Make source appear
157 final Throwable ex = new IllegalStateException("failed schema");
158 remoteInetTypesYang.setException(ex);
160 final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
161 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
164 schemaContextFuture.checkedGet();
165 } catch (final SchemaResolutionException e) {
166 assertNotNull(e.getCause());
167 assertNotNull(e.getCause().getCause());
168 assertSame(ex, e.getCause().getCause());
172 fail("Schema context creation should have failed");
176 public void testDifferentCosts() throws Exception {
177 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
179 final SettableSchemaProvider<ASTSchemaSource> immediateInetTypesYang = spy(getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
180 immediateInetTypesYang.register(sharedSchemaRepository);
181 immediateInetTypesYang.setResult();
183 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = spy(getRemoteYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
184 remoteInetTypesYang.register(sharedSchemaRepository);
185 remoteInetTypesYang.setResult();
187 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
189 final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
190 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
192 assertSchemaContext(schemaContextFuture.checkedGet(), 1);
194 final SourceIdentifier id = immediateInetTypesYang.getId();
195 verify(remoteInetTypesYang, times(0)).getSource(id);
196 verify(immediateInetTypesYang).getSource(id);
200 public void testWithCacheStartup() throws Exception {
201 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
203 class CountingSchemaListener implements SchemaSourceListener {
204 List<PotentialSchemaSource<?>> registeredSources = Lists.newArrayList();
207 public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
211 public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
212 for (final PotentialSchemaSource<?> source : sources) {
213 registeredSources.add(source);
218 public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
222 final File storageDir = Files.createTempDir();
224 final CountingSchemaListener listener = new CountingSchemaListener();
225 sharedSchemaRepository.registerSchemaSourceListener(listener);
227 final File test = new File(storageDir, "test.yang");
228 Files.write("content-test", test, StandardCharsets.UTF_8);
230 final File test2 = new File(storageDir, "test@2012-12-12.yang");
231 Files.write("content-test-2012", test2, StandardCharsets.UTF_8);
233 final File test3 = new File(storageDir, "test@2013-12-12.yang");
234 Files.write("content-test-2013", test3, StandardCharsets.UTF_8);
236 final File test4 = new File(storageDir, "module@2010-12-12.yang");
237 Files.write("content-module-2010", test4, StandardCharsets.UTF_8);
240 final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
241 sharedSchemaRepository.registerSchemaSourceListener(cache);
243 assertEquals(4, listener.registeredSources.size());
245 final Function<PotentialSchemaSource<?>, SourceIdentifier> potSourceToSID = new Function<PotentialSchemaSource<?>, SourceIdentifier>() {
247 public SourceIdentifier apply(final PotentialSchemaSource<?> input) {
248 return input.getSourceIdentifier();
251 assertThat(Collections2.transform(listener.registeredSources, potSourceToSID),
252 both(hasItem(RevisionSourceIdentifier.create("test", Optional.absent())))
253 .and(hasItem(RevisionSourceIdentifier.create("test", Optional.of("2012-12-12"))))
254 .and(hasItem(RevisionSourceIdentifier.create("test", Optional.of("2013-12-12"))))
255 .and(hasItem(RevisionSourceIdentifier.create("module", Optional.of("2010-12-12"))))
260 public void testWithCacheRunning() throws Exception {
261 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
263 final File storageDir = Files.createTempDir();
265 final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
266 sharedSchemaRepository.registerSchemaSourceListener(cache);
268 final SourceIdentifier runningId = RevisionSourceIdentifier.create("running", Optional.of("2012-12-12"));
270 sharedSchemaRepository.registerSchemaSource(new SchemaSourceProvider<YangTextSchemaSource>() {
272 public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
273 return Futures.immediateCheckedFuture(new YangTextSchemaSource(runningId) {
275 protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
276 return toStringHelper;
280 public InputStream openStream() throws IOException {
281 return new ByteArrayInputStream("running".getBytes(StandardCharsets.UTF_8));
285 }, PotentialSchemaSource.create(runningId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue()));
287 final TextToASTTransformer transformer = TextToASTTransformer.create(sharedSchemaRepository, sharedSchemaRepository);
288 sharedSchemaRepository.registerSchemaSourceListener(transformer);
290 // Request schema to make repository notify the cache
291 final CheckedFuture<SchemaContext, SchemaResolutionException> schemaFuture = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT).createSchemaContext(Lists.newArrayList(runningId));
292 Futures.addCallback(schemaFuture, new FutureCallback<SchemaContext>() {
294 public void onSuccess(final SchemaContext result) {
295 fail("Creation of schema context should fail from non-regular sources");
299 public void onFailure(final Throwable t) {
300 // Creation of schema context fails, since we do not provide regular sources, but we just want to check cache
301 final List<File> cachedSchemas = Arrays.asList(storageDir.listFiles());
302 assertEquals(1, cachedSchemas.size());
303 assertEquals(Files.getNameWithoutExtension(cachedSchemas.get(0).getName()), "running@2012-12-12");
309 } catch (final ExecutionException e) {
310 assertNotNull(e.getCause());
311 assertEquals(MissingSchemaSourceException.class, e.getCause().getClass());
315 fail("Creation of schema context should fail from non-regular sources");
318 private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) {
319 assertNotNull(schemaContext);
320 assertEquals(moduleSize, schemaContext.getModules().size());
323 static SettableSchemaProvider<ASTSchemaSource> getRemoteYangSourceProviderFromResource(final String resourceName) throws Exception {
324 final ResourceYangSource yangSource = new ResourceYangSource(resourceName);
325 final CheckedFuture<ASTSchemaSource, SchemaSourceException> aSTSchemaSource = TextToASTTransformer.TRANSFORMATION.apply(yangSource);
326 return SettableSchemaProvider.createRemote(aSTSchemaSource.get(), ASTSchemaSource.class);
329 static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(final String resourceName) throws Exception {
330 final ResourceYangSource yangSource = new ResourceYangSource(resourceName);
331 final CheckedFuture<ASTSchemaSource, SchemaSourceException> aSTSchemaSource = TextToASTTransformer.TRANSFORMATION.apply(yangSource);
332 return SettableSchemaProvider.createImmediate(aSTSchemaSource.get(), ASTSchemaSource.class);