/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.parser.repo; import static org.hamcrest.CoreMatchers.both; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter.ALWAYS_ACCEPT; import com.google.common.base.Function; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.base.Optional; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.io.Files; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import javax.annotation.Nonnull; import org.junit.Test; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener; import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource; import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer; public class SharedSchemaRepositoryTest { @Test public void testSourceWithAndWithoutRevision() throws Exception { final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts"); final SourceIdentifier idNoRevision = loadAndRegisterSource(sharedSchemaRepository, "/no-revision/imported.yang"); final SourceIdentifier id2 = loadAndRegisterSource(sharedSchemaRepository, "/no-revision/imported@2012-12-12.yang"); CheckedFuture<ASTSchemaSource, SchemaSourceException> source = sharedSchemaRepository.getSchemaSource( idNoRevision, ASTSchemaSource.class); assertEquals(idNoRevision, source.checkedGet().getIdentifier()); source = sharedSchemaRepository.getSchemaSource(id2, ASTSchemaSource.class); assertEquals(id2, source.checkedGet().getIdentifier()); } private static SourceIdentifier loadAndRegisterSource(final SharedSchemaRepository sharedSchemaRepository, final String resourceName) throws Exception { final SettableSchemaProvider<ASTSchemaSource> sourceProvider = getImmediateYangSourceProviderFromResource( resourceName); sourceProvider.setResult(); final SourceIdentifier idNoRevision = sourceProvider.getId(); sourceProvider.register(sharedSchemaRepository); return idNoRevision; } @Test public void testSimpleSchemaContext() throws Exception { final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts"); final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource( "/ietf/ietf-inet-types@2010-09-24.yang"); remoteInetTypesYang.register(sharedSchemaRepository); final CheckedFuture<ASTSchemaSource, SchemaSourceException> registeredSourceFuture = sharedSchemaRepository .getSchemaSource(remoteInetTypesYang.getId(), ASTSchemaSource.class); assertFalse(registeredSourceFuture.isDone()); final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT); final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId())); assertFalse(schemaContextFuture.isDone()); // Make source appear remoteInetTypesYang.setResult(); assertEquals(remoteInetTypesYang.getSchemaSourceRepresentation(), registeredSourceFuture.get()); // Verify schema created successfully assertTrue(schemaContextFuture.isDone()); final SchemaContext firstSchemaContext = schemaContextFuture.checkedGet(); assertSchemaContext(firstSchemaContext, 1); // Try same schema second time final CheckedFuture<SchemaContext, SchemaResolutionException> secondSchemaFuture = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT) .createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId())); // Verify second schema created successfully immediately assertTrue(secondSchemaFuture.isDone()); // Assert same context instance is returned from first and second attempt assertSame(firstSchemaContext, secondSchemaFuture.checkedGet()); } @Test public void testTwoSchemaContextsSharingSource() throws Exception { final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts"); final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource( "/ietf/ietf-inet-types@2010-09-24.yang"); remoteInetTypesYang.register(sharedSchemaRepository); remoteInetTypesYang.setResult(); final SettableSchemaProvider<ASTSchemaSource> remoteTopologyYang = getImmediateYangSourceProviderFromResource( "/ietf/network-topology@2013-10-21.yang"); remoteTopologyYang.register(sharedSchemaRepository); remoteTopologyYang.setResult(); final SettableSchemaProvider<ASTSchemaSource> remoteModuleNoRevYang = getImmediateYangSourceProviderFromResource( "/no-revision/module-without-revision.yang"); remoteModuleNoRevYang.register(sharedSchemaRepository); final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT); final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndTopologySchemaContextFuture = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId(), remoteTopologyYang.getId())); assertTrue(inetAndTopologySchemaContextFuture.isDone()); assertSchemaContext(inetAndTopologySchemaContextFuture.checkedGet(), 2); final CheckedFuture<SchemaContext, SchemaResolutionException> inetAndNoRevSchemaContextFuture = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId(), remoteModuleNoRevYang.getId())); assertFalse(inetAndNoRevSchemaContextFuture.isDone()); remoteModuleNoRevYang.setResult(); assertTrue(inetAndNoRevSchemaContextFuture.isDone()); assertSchemaContext(inetAndNoRevSchemaContextFuture.checkedGet(), 2); } @Test public void testFailedSchemaContext() throws Exception { final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts"); final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource( "/ietf/ietf-inet-types@2010-09-24.yang"); remoteInetTypesYang.register(sharedSchemaRepository); final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT); // Make source appear final Throwable ex = new IllegalStateException("failed schema"); remoteInetTypesYang.setException(ex); final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId())); try { schemaContextFuture.checkedGet(); } catch (final SchemaResolutionException e) { assertNotNull(e.getCause()); assertNotNull(e.getCause().getCause()); assertSame(ex, e.getCause().getCause()); return; } fail("Schema context creation should have failed"); } @Test public void testDifferentCosts() throws Exception { final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts"); final SettableSchemaProvider<ASTSchemaSource> immediateInetTypesYang = spy( getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang")); immediateInetTypesYang.register(sharedSchemaRepository); immediateInetTypesYang.setResult(); final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = spy( getRemoteYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang")); remoteInetTypesYang.register(sharedSchemaRepository); remoteInetTypesYang.setResult(); final SchemaContextFactory fact = sharedSchemaRepository.createSchemaContextFactory(ALWAYS_ACCEPT); final CheckedFuture<SchemaContext, SchemaResolutionException> schemaContextFuture = fact.createSchemaContext(ImmutableList.of(remoteInetTypesYang.getId())); assertSchemaContext(schemaContextFuture.checkedGet(), 1); final SourceIdentifier id = immediateInetTypesYang.getId(); verify(remoteInetTypesYang, times(0)).getSource(id); verify(immediateInetTypesYang).getSource(id); } @Test public void testWithCacheStartup() throws Exception { final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts"); class CountingSchemaListener implements SchemaSourceListener { List<PotentialSchemaSource<?>> registeredSources = new ArrayList<>(); @Override public void schemaSourceEncountered(final SchemaSourceRepresentation source) { } @Override public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) { for (final PotentialSchemaSource<?> source : sources) { registeredSources.add(source); } } @Override public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) { } } final File storageDir = Files.createTempDir(); final CountingSchemaListener listener = new CountingSchemaListener(); sharedSchemaRepository.registerSchemaSourceListener(listener); final File test = new File(storageDir, "test.yang"); Files.write("content-test", test, StandardCharsets.UTF_8); final File test2 = new File(storageDir, "test@2012-12-12.yang"); Files.write("content-test-2012", test2, StandardCharsets.UTF_8); final File test3 = new File(storageDir, "test@2013-12-12.yang"); Files.write("content-test-2013", test3, StandardCharsets.UTF_8); final File test4 = new File(storageDir, "module@2010-12-12.yang"); Files.write("content-module-2010", test4, StandardCharsets.UTF_8); final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>( sharedSchemaRepository, YangTextSchemaSource.class, storageDir); sharedSchemaRepository.registerSchemaSourceListener(cache); assertEquals(4, listener.registeredSources.size()); final Function<PotentialSchemaSource<?>, SourceIdentifier> potSourceToSID = PotentialSchemaSource::getSourceIdentifier; assertThat(Collections2.transform(listener.registeredSources, potSourceToSID), both(hasItem(RevisionSourceIdentifier.create("test", Optional.absent()))) .and(hasItem(RevisionSourceIdentifier.create("test", Optional.of("2012-12-12")))) .and(hasItem(RevisionSourceIdentifier.create("test", Optional.of("2013-12-12")))) .and(hasItem(RevisionSourceIdentifier.create("module", Optional.of("2010-12-12")))) ); } @Test public void testWithCacheRunning() throws Exception { final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts"); final File storageDir = Files.createTempDir(); final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>( sharedSchemaRepository, YangTextSchemaSource.class, storageDir); sharedSchemaRepository.registerSchemaSourceListener(cache); final SourceIdentifier runningId = RevisionSourceIdentifier.create("running", Optional.of("2012-12-12")); sharedSchemaRepository.registerSchemaSource(sourceIdentifier -> Futures.immediateCheckedFuture( new YangTextSchemaSource(runningId) { @Override protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) { return toStringHelper; } @Override public InputStream openStream() throws IOException { return new ByteArrayInputStream("running".getBytes(StandardCharsets.UTF_8)); } }), PotentialSchemaSource.create(runningId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())); final TextToASTTransformer transformer = TextToASTTransformer.create(sharedSchemaRepository, sharedSchemaRepository); sharedSchemaRepository.registerSchemaSourceListener(transformer); // Request schema to make repository notify the cache final CheckedFuture<SchemaContext, SchemaResolutionException> schemaFuture = sharedSchemaRepository .createSchemaContextFactory(ALWAYS_ACCEPT).createSchemaContext(ImmutableList.of(runningId)); Futures.addCallback(schemaFuture, new FutureCallback<SchemaContext>() { @Override public void onSuccess(final SchemaContext result) { fail("Creation of schema context should fail from non-regular sources"); } @Override public void onFailure(@Nonnull final Throwable t) { // Creation of schema context fails, since we do not provide regular sources, but we just want // to check cache final List<File> cachedSchemas = Arrays.asList(storageDir.listFiles()); assertEquals(1, cachedSchemas.size()); assertEquals(Files.getNameWithoutExtension(cachedSchemas.get(0).getName()), "running@2012-12-12"); } }); try { schemaFuture.get(); } catch (final ExecutionException e) { assertNotNull(e.getCause()); assertEquals(MissingSchemaSourceException.class, e.getCause().getClass()); return; } fail("Creation of schema context should fail from non-regular sources"); } private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) { assertNotNull(schemaContext); assertEquals(moduleSize, schemaContext.getModules().size()); } static SettableSchemaProvider<ASTSchemaSource> getRemoteYangSourceProviderFromResource(final String resourceName) throws Exception { final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName); return SettableSchemaProvider.createRemote(TextToASTTransformer.transformText(yangSource), ASTSchemaSource.class); } static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(final String resourceName) throws Exception { final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName); return SettableSchemaProvider.createImmediate(TextToASTTransformer.transformText(yangSource), ASTSchemaSource.class); } }