2a2fd60f11e88cbfd3eeb18914c5bc0990f3568d
[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 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;
58
59 public class SharedSchemaRepositoryTest {
60
61     @Test
62     public void testSourceWithAndWithoutRevision() throws Exception {
63         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
64
65         final SourceIdentifier idNoRevision = loadAndRegisterSource(sharedSchemaRepository, "/no-revision/imported.yang");
66         final SourceIdentifier id2 = loadAndRegisterSource(sharedSchemaRepository, "/no-revision/imported@2012-12-12.yang");
67
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());
72     }
73
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);
79         return idNoRevision;
80     }
81
82     @Test
83     public void testSimpleSchemaContext() throws Exception {
84         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
85
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());
90
91         final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
92
93         final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
94                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
95
96         assertFalse(schemaContextFuture.isDone());
97
98         // Make source appear
99         remoteInetTypesYang.setResult();
100         assertEquals(remoteInetTypesYang.getSchemaSourceRepresentation(), registeredSourceFuture.get());
101
102         // Verify schema created successfully
103         assertTrue(schemaContextFuture.isDone());
104         final SchemaContext firstSchemaContext = schemaContextFuture.checkedGet();
105         assertSchemaContext(firstSchemaContext, 1);
106
107         // Try same schema second time
108         final CheckedFuture<SchemaContext, SchemaResolutionException> secondSchemaFuture =
109                 sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT)
110                         .createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
111
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());
116     }
117
118     @Test
119     public void testTwoSchemaContextsSharingSource() throws Exception {
120         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
121
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);
130
131         final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
132
133         final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndTopologySchemaContextFuture
134                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId(), remoteTopologyYang.getId()));
135         assertTrue(inetAndTopologySchemaContextFuture.isDone());
136         assertSchemaContext(inetAndTopologySchemaContextFuture.checkedGet(), 2);
137
138         final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndNoRevSchemaContextFuture
139                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId(), remoteModuleNoRevYang.getId()));
140         assertFalse(inetAndNoRevSchemaContextFuture.isDone());
141
142         remoteModuleNoRevYang.setResult();
143         assertTrue(inetAndNoRevSchemaContextFuture.isDone());
144         assertSchemaContext(inetAndNoRevSchemaContextFuture.checkedGet(), 2);
145     }
146
147     @Test
148     public void testFailedSchemaContext() throws Exception {
149         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
150
151         final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang");
152         remoteInetTypesYang.register(sharedSchemaRepository);
153
154         final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
155
156         // Make source appear
157         final Throwable ex = new IllegalStateException("failed schema");
158         remoteInetTypesYang.setException(ex);
159
160         final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
161                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
162
163         try {
164             schemaContextFuture.checkedGet();
165         } catch (final SchemaResolutionException e) {
166             assertNotNull(e.getCause());
167             assertNotNull(e.getCause().getCause());
168             assertSame(ex, e.getCause().getCause());
169             return;
170         }
171
172         fail("Schema context creation should have failed");
173     }
174
175     @Test
176     public void testDifferentCosts() throws Exception {
177         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
178
179         final SettableSchemaProvider<ASTSchemaSource> immediateInetTypesYang = spy(getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
180         immediateInetTypesYang.register(sharedSchemaRepository);
181         immediateInetTypesYang.setResult();
182
183         final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = spy(getRemoteYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
184         remoteInetTypesYang.register(sharedSchemaRepository);
185         remoteInetTypesYang.setResult();
186
187         final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
188
189         final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture
190                 = fact.createSchemaContext(Lists.newArrayList(remoteInetTypesYang.getId()));
191
192         assertSchemaContext(schemaContextFuture.checkedGet(), 1);
193
194         final SourceIdentifier id = immediateInetTypesYang.getId();
195         verify(remoteInetTypesYang, times(0)).getSource(id);
196         verify(immediateInetTypesYang).getSource(id);
197     }
198
199     @Test
200     public void testWithCacheStartup() throws Exception {
201         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
202
203         class CountingSchemaListener implements SchemaSourceListener {
204             List<PotentialSchemaSource<?>> registeredSources = Lists.newArrayList();
205
206             @Override
207             public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
208             }
209
210             @Override
211             public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
212                 for (final PotentialSchemaSource<?> source : sources) {
213                     registeredSources.add(source);
214                 }
215             }
216
217             @Override
218             public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
219             }
220         }
221
222         final File storageDir = Files.createTempDir();
223
224         final CountingSchemaListener listener = new CountingSchemaListener();
225         sharedSchemaRepository.registerSchemaSourceListener(listener);
226
227         final File test = new File(storageDir, "test.yang");
228         Files.write("content-test", test, StandardCharsets.UTF_8);
229
230         final File test2 = new File(storageDir, "test@2012-12-12.yang");
231         Files.write("content-test-2012", test2, StandardCharsets.UTF_8);
232
233         final File test3 = new File(storageDir, "test@2013-12-12.yang");
234         Files.write("content-test-2013", test3, StandardCharsets.UTF_8);
235
236         final File test4 = new File(storageDir, "module@2010-12-12.yang");
237         Files.write("content-module-2010", test4, StandardCharsets.UTF_8);
238
239
240         final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
241         sharedSchemaRepository.registerSchemaSourceListener(cache);
242
243         assertEquals(4, listener.registeredSources.size());
244
245         final Function<PotentialSchemaSource<?>, SourceIdentifier> potSourceToSID = new Function<PotentialSchemaSource<?>, SourceIdentifier>() {
246             @Override
247             public SourceIdentifier apply(final PotentialSchemaSource<?> input) {
248                 return input.getSourceIdentifier();
249             }
250         };
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"))))
256         );
257     }
258
259     @Test
260     public void testWithCacheRunning() throws Exception {
261         final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
262
263         final File storageDir = Files.createTempDir();
264
265         final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
266         sharedSchemaRepository.registerSchemaSourceListener(cache);
267
268         final SourceIdentifier runningId = RevisionSourceIdentifier.create("running", Optional.of("2012-12-12"));
269
270         sharedSchemaRepository.registerSchemaSource(new SchemaSourceProvider<YangTextSchemaSource>() {
271             @Override
272             public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
273                 return Futures.immediateCheckedFuture(new YangTextSchemaSource(runningId) {
274                     @Override
275                     protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
276                         return toStringHelper;
277                     }
278
279                     @Override
280                     public InputStream openStream() throws IOException {
281                         return new ByteArrayInputStream("running".getBytes(StandardCharsets.UTF_8));
282                     }
283                 });
284             }
285         }, PotentialSchemaSource.create(runningId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue()));
286
287         final TextToASTTransformer transformer = TextToASTTransformer.create(sharedSchemaRepository, sharedSchemaRepository);
288         sharedSchemaRepository.registerSchemaSourceListener(transformer);
289
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>() {
293             @Override
294             public void onSuccess(final SchemaContext result) {
295                 fail("Creation of schema context should fail from non-regular sources");
296             }
297
298             @Override
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");
304             }
305         });
306
307         try {
308             schemaFuture.get();
309         } catch (final ExecutionException e) {
310             assertNotNull(e.getCause());
311             assertEquals(MissingSchemaSourceException.class, e.getCause().getClass());
312             return;
313         }
314
315         fail("Creation of schema context should fail from non-regular sources");
316     }
317
318     private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) {
319         assertNotNull(schemaContext);
320         assertEquals(moduleSize, schemaContext.getModules().size());
321     }
322
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);
327     }
328
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);
333     }
334 }