Propagate @Nonnull and @Nullable annotations
[yangtools.git] / yang / yang-parser-impl / src / test / java / org / opendaylight / yangtools / yang / parser / repo / SharedSchemaRepositoryTest.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.yangtools.yang.parser.repo;
10
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
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;
34 import java.io.File;
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 javax.annotation.Nonnull;
42 import org.junit.Test;
43 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
44 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
45 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
46 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
47 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
48 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
49 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
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.spi.SchemaSourceProvider;
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;
59
60 public class SharedSchemaRepositoryTest {
61
62     @Test
63     public void testSourceWithAndWithoutRevision() throws Exception {
64         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
65
66         final SourceIdentifier idNoRevision = loadAndRegisterSource(sharedSchemaRepository, "/no-revision/imported.yang");
67         final SourceIdentifier id2 = loadAndRegisterSource(sharedSchemaRepository, "/no-revision/imported@2012-12-12.yang");
68
69         CheckedFuture<ASTSchemaSource, SchemaSourceException> source = sharedSchemaRepository.getSchemaSource(idNoRevision, ASTSchemaSource.class);
70         assertEquals(idNoRevision, source.checkedGet().getIdentifier());
71         source = sharedSchemaRepository.getSchemaSource(id2, ASTSchemaSource.class);
72         assertEquals(id2, source.checkedGet().getIdentifier());
73     }
74
75     private static SourceIdentifier loadAndRegisterSource(final SharedSchemaRepository sharedSchemaRepository, final String resourceName) throws Exception {
76         final SettableSchemaProvider<ASTSchemaSource> sourceProvider = getImmediateYangSourceProviderFromResource(resourceName);
77         sourceProvider.setResult();
78         final SourceIdentifier idNoRevision = sourceProvider.getId();
79         sourceProvider.register(sharedSchemaRepository);
80         return idNoRevision;
81     }
82
83     @Test
84     public void testSimpleSchemaContext() throws Exception {
85         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
86
87         final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang");
88         remoteInetTypesYang.register(sharedSchemaRepository);
89         final CheckedFuture<ASTSchemaSource, SchemaSourceException> registeredSourceFuture = sharedSchemaRepository.getSchemaSource(remoteInetTypesYang.getId(), ASTSchemaSource.class);
90         assertFalse(registeredSourceFuture.isDone());
91
92         final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
93
94         final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
95                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
96
97         assertFalse(schemaContextFuture.isDone());
98
99         // Make source appear
100         remoteInetTypesYang.setResult();
101         assertEquals(remoteInetTypesYang.getSchemaSourceRepresentation(), registeredSourceFuture.get());
102
103         // Verify schema created successfully
104         assertTrue(schemaContextFuture.isDone());
105         final SchemaContext firstSchemaContext = schemaContextFuture.checkedGet();
106         assertSchemaContext(firstSchemaContext, 1);
107
108         // Try same schema second time
109         final CheckedFuture<SchemaContext, SchemaResolutionException> secondSchemaFuture =
110                 sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT)
111                         .createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
112
113         // Verify second schema created successfully immediately
114         assertTrue(secondSchemaFuture.isDone());
115         // Assert same context instance is returned from first and second attempt
116         assertSame(firstSchemaContext, secondSchemaFuture.checkedGet());
117     }
118
119     @Test
120     public void testTwoSchemaContextsSharingSource() throws Exception {
121         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
122
123         final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang");
124         remoteInetTypesYang.register(sharedSchemaRepository);
125         remoteInetTypesYang.setResult();
126         final SettableSchemaProvider<ASTSchemaSource> remoteTopologyYang = getImmediateYangSourceProviderFromResource("/ietf/network-topology@2013-10-21.yang");
127         remoteTopologyYang.register(sharedSchemaRepository);
128         remoteTopologyYang.setResult();
129         final SettableSchemaProvider<ASTSchemaSource> remoteModuleNoRevYang = getImmediateYangSourceProviderFromResource("/no-revision/module-without-revision.yang");
130         remoteModuleNoRevYang.register(sharedSchemaRepository);
131
132         final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
133
134         final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndTopologySchemaContextFuture
135                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId(), remoteTopologyYang.getId()));
136         assertTrue(inetAndTopologySchemaContextFuture.isDone());
137         assertSchemaContext(inetAndTopologySchemaContextFuture.checkedGet(), 2);
138
139         final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndNoRevSchemaContextFuture
140                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId(), remoteModuleNoRevYang.getId()));
141         assertFalse(inetAndNoRevSchemaContextFuture.isDone());
142
143         remoteModuleNoRevYang.setResult();
144         assertTrue(inetAndNoRevSchemaContextFuture.isDone());
145         assertSchemaContext(inetAndNoRevSchemaContextFuture.checkedGet(), 2);
146     }
147
148     @Test
149     public void testFailedSchemaContext() throws Exception {
150         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
151
152         final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang");
153         remoteInetTypesYang.register(sharedSchemaRepository);
154
155         final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
156
157         // Make source appear
158         final Throwable ex = new IllegalStateException("failed schema");
159         remoteInetTypesYang.setException(ex);
160
161         final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
162                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
163
164         try {
165             schemaContextFuture.checkedGet();
166         } catch (final SchemaResolutionException e) {
167             assertNotNull(e.getCause());
168             assertNotNull(e.getCause().getCause());
169             assertSame(ex, e.getCause().getCause());
170             return;
171         }
172
173         fail("Schema context creation should have failed");
174     }
175
176     @Test
177     public void testDifferentCosts() throws Exception {
178         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
179
180         final SettableSchemaProvider<ASTSchemaSource> immediateInetTypesYang = spy(getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
181         immediateInetTypesYang.register(sharedSchemaRepository);
182         immediateInetTypesYang.setResult();
183
184         final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = spy(getRemoteYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
185         remoteInetTypesYang.register(sharedSchemaRepository);
186         remoteInetTypesYang.setResult();
187
188         final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
189
190         final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
191                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
192
193         assertSchemaContext(schemaContextFuture.checkedGet(), 1);
194
195         final SourceIdentifier id = immediateInetTypesYang.getId();
196         verify(remoteInetTypesYang, times(0)).getSource(id);
197         verify(immediateInetTypesYang).getSource(id);
198     }
199
200     @Test
201     public void testWithCacheStartup() throws Exception {
202         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
203
204         class CountingSchemaListener implements SchemaSourceListener {
205             List<PotentialSchemaSource<?>> registeredSources = Lists.newArrayList();
206
207             @Override
208             public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
209             }
210
211             @Override
212             public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
213                 for (final PotentialSchemaSource<?> source : sources) {
214                     registeredSources.add(source);
215                 }
216             }
217
218             @Override
219             public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
220             }
221         }
222
223         final File storageDir = Files.createTempDir();
224
225         final CountingSchemaListener listener = new CountingSchemaListener();
226         sharedSchemaRepository.registerSchemaSourceListener(listener);
227
228         final File test = new File(storageDir, "test.yang");
229         Files.write("content-test", test, StandardCharsets.UTF_8);
230
231         final File test2 = new File(storageDir, "test@2012-12-12.yang");
232         Files.write("content-test-2012", test2, StandardCharsets.UTF_8);
233
234         final File test3 = new File(storageDir, "test@2013-12-12.yang");
235         Files.write("content-test-2013", test3, StandardCharsets.UTF_8);
236
237         final File test4 = new File(storageDir, "module@2010-12-12.yang");
238         Files.write("content-module-2010", test4, StandardCharsets.UTF_8);
239
240
241         final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
242         sharedSchemaRepository.registerSchemaSourceListener(cache);
243
244         assertEquals(4, listener.registeredSources.size());
245
246         final Function<PotentialSchemaSource<?>, SourceIdentifier> potSourceToSID =
247                 PotentialSchemaSource::getSourceIdentifier;
248         assertThat(Collections2.transform(listener.registeredSources, potSourceToSID),
249                 both(hasItem(RevisionSourceIdentifier.create("test", Optional.absent())))
250                         .and(hasItem(RevisionSourceIdentifier.create("test", Optional.of("2012-12-12"))))
251                         .and(hasItem(RevisionSourceIdentifier.create("test", Optional.of("2013-12-12"))))
252                         .and(hasItem(RevisionSourceIdentifier.create("module", Optional.of("2010-12-12"))))
253         );
254     }
255
256     @Test
257     public void testWithCacheRunning() throws Exception {
258         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
259
260         final File storageDir = Files.createTempDir();
261
262         final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
263         sharedSchemaRepository.registerSchemaSourceListener(cache);
264
265         final SourceIdentifier runningId = RevisionSourceIdentifier.create("running", Optional.of("2012-12-12"));
266
267         sharedSchemaRepository.registerSchemaSource(new SchemaSourceProvider<YangTextSchemaSource>() {
268             @Override
269             public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
270                 return Futures.immediateCheckedFuture(new YangTextSchemaSource(runningId) {
271                     @Override
272                     protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
273                         return toStringHelper;
274                     }
275
276                     @Override
277                     public InputStream openStream() throws IOException {
278                         return new ByteArrayInputStream("running".getBytes(StandardCharsets.UTF_8));
279                     }
280                 });
281             }
282         }, PotentialSchemaSource.create(runningId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue()));
283
284         final TextToASTTransformer transformer = TextToASTTransformer.create(sharedSchemaRepository, sharedSchemaRepository);
285         sharedSchemaRepository.registerSchemaSourceListener(transformer);
286
287         // Request schema to make repository notify the cache
288         final CheckedFuture<SchemaContext, SchemaResolutionException> schemaFuture = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT).createSchemaContext(Lists.newArrayList(runningId));
289         Futures.addCallback(schemaFuture, new FutureCallback<SchemaContext>() {
290             @Override
291             public void onSuccess(final SchemaContext result) {
292                 fail("Creation of schema context should fail from non-regular sources");
293             }
294
295             @Override
296             public void onFailure(@Nonnull final Throwable t) {
297                 // Creation of schema context fails, since we do not provide regular sources, but we just want to check cache
298                 final List<File> cachedSchemas = Arrays.asList(storageDir.listFiles());
299                 assertEquals(1, cachedSchemas.size());
300                 assertEquals(Files.getNameWithoutExtension(cachedSchemas.get(0).getName()), "running@2012-12-12");
301             }
302         });
303
304         try {
305             schemaFuture.get();
306         } catch (final ExecutionException e) {
307             assertNotNull(e.getCause());
308             assertEquals(MissingSchemaSourceException.class, e.getCause().getClass());
309             return;
310         }
311
312         fail("Creation of schema context should fail from non-regular sources");
313     }
314
315     private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) {
316         assertNotNull(schemaContext);
317         assertEquals(moduleSize, schemaContext.getModules().size());
318     }
319
320     static SettableSchemaProvider<ASTSchemaSource> getRemoteYangSourceProviderFromResource(final String resourceName) throws Exception {
321         final ResourceYangSource yangSource = new ResourceYangSource(resourceName);
322         final CheckedFuture<ASTSchemaSource, SchemaSourceException> aSTSchemaSource = TextToASTTransformer.TRANSFORMATION.apply(yangSource);
323         return SettableSchemaProvider.createRemote(aSTSchemaSource.get(), ASTSchemaSource.class);
324     }
325
326     static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(final String resourceName) throws Exception {
327         final ResourceYangSource yangSource = new ResourceYangSource(resourceName);
328         final CheckedFuture<ASTSchemaSource, SchemaSourceException> aSTSchemaSource = TextToASTTransformer.TRANSFORMATION.apply(yangSource);
329         return SettableSchemaProvider.createImmediate(aSTSchemaSource.get(), ASTSchemaSource.class);
330     }
331 }