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