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