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.MoreObjects.ToStringHelper;
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.collect.Lists;
28 import com.google.common.io.Files;
29 import com.google.common.util.concurrent.FutureCallback;
30 import com.google.common.util.concurrent.Futures;
31 import com.google.common.util.concurrent.ListenableFuture;
32 import com.google.common.util.concurrent.MoreExecutors;
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.ArrayList;
39 import java.util.Arrays;
40 import java.util.List;
41 import java.util.Optional;
42 import java.util.concurrent.ExecutionException;
43 import javax.annotation.Nonnull;
44 import org.junit.Test;
45 import org.opendaylight.yangtools.yang.common.Revision;
46 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
47 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
48 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
49 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
50 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
51 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
52 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
53 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
54 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
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,
66 "/no-revision/imported.yang");
67 final SourceIdentifier id2 = loadAndRegisterSource(sharedSchemaRepository,
68 "/no-revision/imported@2012-12-12.yang");
70 ListenableFuture<ASTSchemaSource> source = sharedSchemaRepository.getSchemaSource(idNoRevision,
71 ASTSchemaSource.class);
72 assertEquals(idNoRevision, source.get().getIdentifier());
73 source = sharedSchemaRepository.getSchemaSource(id2, ASTSchemaSource.class);
74 assertEquals(id2, source.get().getIdentifier());
77 private static SourceIdentifier loadAndRegisterSource(final SharedSchemaRepository sharedSchemaRepository,
78 final String resourceName) throws Exception {
79 final SettableSchemaProvider<ASTSchemaSource> sourceProvider = getImmediateYangSourceProviderFromResource(
81 sourceProvider.setResult();
82 final SourceIdentifier idNoRevision = sourceProvider.getId();
83 sourceProvider.register(sharedSchemaRepository);
88 public void testSimpleSchemaContext() throws Exception {
89 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
91 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
92 "/ietf/ietf-inet-types@2010-09-24.yang");
93 remoteInetTypesYang.register(sharedSchemaRepository);
94 final ListenableFuture<ASTSchemaSource> registeredSourceFuture = sharedSchemaRepository.getSchemaSource(
95 remoteInetTypesYang.getId(), ASTSchemaSource.class);
96 assertFalse(registeredSourceFuture.isDone());
98 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT);
99 final ListenableFuture<SchemaContext> schemaContextFuture =
100 fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId()));
102 assertFalse(schemaContextFuture.isDone());
104 // Make source appear
105 remoteInetTypesYang.setResult();
106 assertEquals(remoteInetTypesYang.getSchemaSourceRepresentation(), registeredSourceFuture.get());
108 // Verify schema created successfully
109 assertTrue(schemaContextFuture.isDone());
110 final SchemaContext firstSchemaContext = schemaContextFuture.get();
111 assertSchemaContext(firstSchemaContext, 1);
113 // Try same schema second time
114 final ListenableFuture<SchemaContext> secondSchemaFuture = sharedSchemaRepository
115 .createSchemaContextFactory(ALWAYS_ACCEPT)
116 .createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId()));
118 // Verify second schema created successfully immediately
119 assertTrue(secondSchemaFuture.isDone());
120 // Assert same context instance is returned from first and second attempt
121 assertSame(firstSchemaContext, secondSchemaFuture.get());
125 public void testTwoSchemaContextsSharingSource() throws Exception {
126 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
128 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
129 "/ietf/ietf-inet-types@2010-09-24.yang");
130 remoteInetTypesYang.register(sharedSchemaRepository);
131 remoteInetTypesYang.setResult();
132 final SettableSchemaProvider<ASTSchemaSource> remoteTopologyYang = getImmediateYangSourceProviderFromResource(
133 "/ietf/network-topology@2013-10-21.yang");
134 remoteTopologyYang.register(sharedSchemaRepository);
135 remoteTopologyYang.setResult();
136 final SettableSchemaProvider<ASTSchemaSource> remoteModuleNoRevYang =
137 getImmediateYangSourceProviderFromResource("/no-revision/module-without-revision.yang");
138 remoteModuleNoRevYang.register(sharedSchemaRepository);
140 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT);
141 final ListenableFuture<SchemaContext> inetAndTopologySchemaContextFuture = fact
142 .createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId(), remoteTopologyYang.getId()));
143 assertTrue(inetAndTopologySchemaContextFuture.isDone());
144 assertSchemaContext(inetAndTopologySchemaContextFuture.get(), 2);
146 final ListenableFuture<SchemaContext> inetAndNoRevSchemaContextFuture =
147 fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId(), remoteModuleNoRevYang.getId()));
148 assertFalse(inetAndNoRevSchemaContextFuture.isDone());
150 remoteModuleNoRevYang.setResult();
151 assertTrue(inetAndNoRevSchemaContextFuture.isDone());
152 assertSchemaContext(inetAndNoRevSchemaContextFuture.get(), 2);
156 public void testFailedSchemaContext() throws Exception {
157 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
159 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
160 "/ietf/ietf-inet-types@2010-09-24.yang");
161 remoteInetTypesYang.register(sharedSchemaRepository);
163 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT);
165 // Make source appear
166 final Throwable ex = new IllegalStateException("failed schema");
167 remoteInetTypesYang.setException(ex);
169 final ListenableFuture<SchemaContext> schemaContextFuture = fact.createSchemaContext(
170 ImmutableList.of(remoteInetTypesYang.getId()));
173 schemaContextFuture.get();
174 } catch (final ExecutionException e) {
175 assertNotNull(e.getCause());
176 assertNotNull(e.getCause().getCause());
177 assertSame(ex, e.getCause().getCause());
181 fail("Schema context creation should have failed");
185 public void testDifferentCosts() throws Exception {
186 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
188 final SettableSchemaProvider<ASTSchemaSource> immediateInetTypesYang = spy(
189 getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
190 immediateInetTypesYang.register(sharedSchemaRepository);
191 immediateInetTypesYang.setResult();
193 final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = spy(
194 getRemoteYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
195 remoteInetTypesYang.register(sharedSchemaRepository);
196 remoteInetTypesYang.setResult();
198 final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT);
200 final ListenableFuture<SchemaContext> schemaContextFuture =
201 fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId()));
203 assertSchemaContext(schemaContextFuture.get(), 1);
205 final SourceIdentifier id = immediateInetTypesYang.getId();
206 verify(remoteInetTypesYang, times(0)).getSource(id);
207 verify(immediateInetTypesYang).getSource(id);
211 public void testWithCacheStartup() throws Exception {
212 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
214 class CountingSchemaListener implements SchemaSourceListener {
215 List<PotentialSchemaSource<?>> registeredSources = new ArrayList<>();
218 public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
222 public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
223 for (final PotentialSchemaSource<?> source : sources) {
224 registeredSources.add(source);
229 public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
233 final File storageDir = Files.createTempDir();
235 final CountingSchemaListener listener = new CountingSchemaListener();
236 sharedSchemaRepository.registerSchemaSourceListener(listener);
238 final File test = new File(storageDir, "test.yang");
239 Files.asCharSink(test, StandardCharsets.UTF_8).write("content-test");
241 final File test2 = new File(storageDir, "test@2012-12-12.yang");
242 Files.asCharSink(test2, StandardCharsets.UTF_8).write("content-test-2012");
244 final File test3 = new File(storageDir, "test@2013-12-12.yang");
245 Files.asCharSink(test3, StandardCharsets.UTF_8).write("content-test-2013");
247 final File test4 = new File(storageDir, "module@2010-12-12.yang");
248 Files.asCharSink(test4, StandardCharsets.UTF_8).write("content-module-2010");
250 final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(
251 sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
252 sharedSchemaRepository.registerSchemaSourceListener(cache);
254 assertEquals(4, listener.registeredSources.size());
256 assertThat(Lists.transform(listener.registeredSources, PotentialSchemaSource::getSourceIdentifier),
257 both(hasItem(RevisionSourceIdentifier.create("test", Optional.empty())))
258 .and(hasItem(RevisionSourceIdentifier.create("test", Revision.valueOf("2012-12-12"))))
259 .and(hasItem(RevisionSourceIdentifier.create("test", Revision.valueOf("2013-12-12"))))
260 .and(hasItem(RevisionSourceIdentifier.create("module", Revision.valueOf("2010-12-12"))))
265 public void testWithCacheRunning() throws Exception {
266 final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
268 final File storageDir = Files.createTempDir();
270 final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(
271 sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
272 sharedSchemaRepository.registerSchemaSourceListener(cache);
274 final SourceIdentifier runningId = RevisionSourceIdentifier.create("running", Revision.valueOf("2012-12-12"));
276 sharedSchemaRepository.registerSchemaSource(sourceIdentifier -> Futures.immediateFuture(
277 new YangTextSchemaSource(runningId) {
279 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
280 return toStringHelper;
284 public InputStream openStream() throws IOException {
285 return new ByteArrayInputStream("running".getBytes(StandardCharsets.UTF_8));
287 }), PotentialSchemaSource.create(runningId, YangTextSchemaSource.class,
288 PotentialSchemaSource.Costs.REMOTE_IO.getValue()));
290 final TextToASTTransformer transformer = TextToASTTransformer.create(sharedSchemaRepository,
291 sharedSchemaRepository);
292 sharedSchemaRepository.registerSchemaSourceListener(transformer);
294 // Request schema to make repository notify the cache
295 final ListenableFuture<SchemaContext> schemaFuture = sharedSchemaRepository
296 .createSchemaContextFactory(ALWAYS_ACCEPT).createSchemaContext(ImmutableList.of(runningId));
297 Futures.addCallback(schemaFuture, new FutureCallback<SchemaContext>() {
299 public void onSuccess(final SchemaContext result) {
300 fail("Creation of schema context should fail from non-regular sources");
304 public void onFailure(@Nonnull final Throwable cause) {
305 // Creation of schema context fails, since we do not provide regular sources, but we just want
307 final List<File> cachedSchemas = Arrays.asList(storageDir.listFiles());
308 assertEquals(1, cachedSchemas.size());
309 assertEquals(Files.getNameWithoutExtension(cachedSchemas.get(0).getName()), "running@2012-12-12");
311 }, MoreExecutors.directExecutor());
315 } catch (final ExecutionException e) {
316 assertNotNull(e.getCause());
317 assertEquals(MissingSchemaSourceException.class, e.getCause().getClass());
321 fail("Creation of schema context should fail from non-regular sources");
324 private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) {
325 assertNotNull(schemaContext);
326 assertEquals(moduleSize, schemaContext.getModules().size());
329 static SettableSchemaProvider<ASTSchemaSource> getRemoteYangSourceProviderFromResource(final String resourceName)
331 final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
332 return SettableSchemaProvider.createRemote(TextToASTTransformer.transformText(yangSource),
333 ASTSchemaSource.class);
336 static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(final String resourceName)
338 final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
339 return SettableSchemaProvider.createImmediate(TextToASTTransformer.transformText(yangSource),
340 ASTSchemaSource.class);