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