/******************************************************************************* * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro * 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 * * Contributors: * Abel Hegedus - initial API and implementation *******************************************************************************/ package org.eclipse.viatra.query.runtime.runonce.tests; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.viatra.examples.library.Book; import org.eclipse.viatra.examples.library.BookCategory; import org.eclipse.viatra.examples.library.LibraryFactory; import org.eclipse.viatra.examples.library.LibraryPackage; import org.eclipse.viatra.examples.library.Library; import org.eclipse.viatra.examples.library.Writer; import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine; import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineManager; import org.eclipse.viatra.query.runtime.api.impl.RunOnceQueryEngine; import org.eclipse.viatra.query.runtime.base.api.BaseIndexOptions; import org.eclipse.viatra.query.runtime.base.comprehension.WellbehavingDerivedFeatureRegistry; import org.eclipse.viatra.query.runtime.emf.EMFScope; import org.eclipse.viatra.query.runtime.exception.ViatraQueryException; import org.junit.Test; /** * Test cases that run different kind of derived features in run-once engine. * * @author Abel Hegedus * */ public class RunOnceTest { private ResourceSet rs; /** * Prepares a resource set and loads the example model. * * @return the library that is the root of the example model */ private Library prepareModel() { String modelPath = "/org.eclipse.viatra.query.runtime.runonce.tests/model/test.library"; rs = new ResourceSetImpl(); URI modelUri = URI.createPlatformPluginURI(modelPath, true); Resource resource = rs.getResource(modelUri, true); return (Library) resource.getContents().get(0); } /** * Test whether a run-once engine returns matches for regular queries. */ @Test public void testRegularQuery() { Library library = prepareModel(); try { //RunOnceQueryEngine engine = new RunOnceQueryEngine(rs); ViatraQueryEngine engine = ViatraQueryEngineManager.getInstance().getQueryEngine(new EMFScope(rs)); // Collection<BooksWithMultipleAuthorsMatch> allMatches = engine.getAllMatches(BooksWithMultipleAuthorsMatcher.querySpecification()); Collection<BooksWithMultipleAuthorsMatch> allMatches = engine.getMatcher(BooksWithMultipleAuthorsMatcher.querySpecification()).getAllMatches(); assertTrue(allMatches.size() == 2); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } @Test public void testSimpleAttribute() { Library library = prepareModel(); try { RunOnceQueryEngine engine = new RunOnceQueryEngine(rs); Collection<SumOfPagesInLibraryMatch> allMatches = engine.getAllMatches(SumOfPagesInLibraryMatcher.querySpecification()); assertTrue(allMatches.size() == 1); SumOfPagesInLibraryMatch match = allMatches.iterator().next(); assertTrue(match.getLibrary().equals(library)); assertTrue(match.getSumOfPages() == 222); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } @Test public void testSingleReference() { Library library = prepareModel(); try { RunOnceQueryEngine engine = new RunOnceQueryEngine(rs); Collection<SingleAuthoredFirstBooksMatch> allMatches = engine.getAllMatches(SingleAuthoredFirstBooksMatcher.querySpecification()); assertTrue(allMatches.size() == 1); SingleAuthoredFirstBooksMatch match = allMatches.iterator().next(); assertTrue(match.getLibrary().equals(library)); assertTrue(match.getFirstBook().getTitle().equals("Other SciFi")); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } @Test public void testManyReference() { Library library = prepareModel(); try { RunOnceQueryEngine engine = new RunOnceQueryEngine(rs); Collection<LongSciFiBooksOfAuthorMatch> allMatches = engine.getAllMatches(LongSciFiBooksOfAuthorMatcher.querySpecification()); assertTrue(allMatches.size() == 1); LongSciFiBooksOfAuthorMatch match = allMatches.iterator().next(); assertTrue(match.getAuthor().getName().equals("Third Author")); assertTrue(match.getBook().getTitle().equals("Other SciFi")); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } /** * This test uses a derived feature that returns different values on subsequent requests. * We could use this to test how the engine responds in such cases (e.g. during disposal). */ @Test public void testNonDeterministicAttribute() { Library library = prepareModel(); try { RunOnceQueryEngine engine = new RunOnceQueryEngine(rs); Collection<RequestCountOfLibraryMatch> allMatches = engine.getAllMatches(RequestCountOfLibraryMatcher.querySpecification()); assertTrue(allMatches.size() == 1); RequestCountOfLibraryMatch match = allMatches.iterator().next(); assertTrue(match.getLibrary().equals(library)); assertTrue(match.getReqCount() == 2); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } /** * Similar to {@link #testNonDeterministicAttribute()} but with a many reference. */ @Test public void testNonDeterministicFeature() { Library library = prepareModel(); try { RunOnceQueryEngine engine = new RunOnceQueryEngine(rs); Collection<SomeBooksWithTwoAuthorsMatch> allMatches = engine.getAllMatches(SomeBooksWithTwoAuthorsMatcher.querySpecification()); assertTrue(allMatches.size() == 1); SomeBooksWithTwoAuthorsMatch match = allMatches.iterator().next(); assertTrue(match.getLibrary().equals(library)); assertTrue(match.getBook().getTitle().equals("Twin life")); allMatches = engine.getAllMatches(SomeBooksWithTwoAuthorsMatcher.querySpecification()); assertTrue(allMatches.isEmpty()); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } /** * The test shows that using an incremental engine with not well-behaving derived features will return * incorrect values if the model changes. * @throws ViatraQueryException */ @Test public void testModelModification() throws ViatraQueryException { // the results of incremental engine will not be correct Library library = prepareModel(); try { WellbehavingDerivedFeatureRegistry.registerWellbehavingDerivedPackage(LibraryPackage.eINSTANCE); AdvancedViatraQueryEngine engine = AdvancedViatraQueryEngine.createUnmanagedEngine(new EMFScope(rs)); // this is to allow the normal engine to traverse feature LongSciFiBooksOfAuthorMatcher matcher = engine.getMatcher(LongSciFiBooksOfAuthorMatcher.querySpecification()); Collection<LongSciFiBooksOfAuthorMatch> allMatches = matcher.getAllMatches(); RunOnceQueryEngine roengine = new RunOnceQueryEngine(rs); Collection<LongSciFiBooksOfAuthorMatch> allROMatches = roengine.getAllMatches(LongSciFiBooksOfAuthorMatcher.querySpecification()); assertTrue(allMatches.size() == allROMatches.size()); LongSciFiBooksOfAuthorMatch match = allMatches.iterator().next(); LongSciFiBooksOfAuthorMatch romatch = allROMatches.iterator().next(); assertTrue(match.getAuthor() == romatch.getAuthor()); Book longBook = romatch.getBook(); assertTrue(match.getBook() == longBook); Book b = LibraryFactory.eINSTANCE.createBook(); b.setTitle("Long book"); b.setPages(120); b.getCategory().add(BookCategory.SCI_FI); b.getAuthors().add(library.getWriters().get(0)); library.getBooks().add(b); allROMatches = roengine.getAllMatches(LongSciFiBooksOfAuthorMatcher.querySpecification()); allMatches = matcher.getAllMatches(); assertTrue(allMatches.size() != allROMatches.size()); assertTrue(allROMatches.size() == 2); Set<Book> longScifiBooks = new HashSet<Book>(); for (LongSciFiBooksOfAuthorMatch m : allROMatches) { longScifiBooks.add(m.getBook()); } assertTrue(longScifiBooks.contains(b)); assertTrue(longScifiBooks.contains(longBook)); } finally { WellbehavingDerivedFeatureRegistry.initRegistry(); } } @Test public void testSamplingModelModification() { // the results of incremental engine will be correct after resampling Library library = prepareModel(); try { RunOnceQueryEngine roengine = new RunOnceQueryEngine(rs); AdvancedViatraQueryEngine engine = AdvancedViatraQueryEngine.createUnmanagedEngine(new EMFScope(rs,roengine.getBaseIndexOptions())); // TODO remove this once such EClasses are automatically identified //engine.getBaseIndex().registerEClasses(Sets.newHashSet(EIQLibraryPackage.eINSTANCE.getBook(), EIQLibraryPackage.eINSTANCE.getWriter())); runModelModification(library, roengine, engine); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } @Test public void testSamplingModelModificationInWildCardMode() { // the results of incremental engine will be correct after resampling Library library = prepareModel(); try { RunOnceQueryEngine roengine = new RunOnceQueryEngine(rs); BaseIndexOptions baseIndexOptions = roengine.getBaseIndexOptions(); baseIndexOptions.withWildcardMode(true); // ensure all types are indexed AdvancedViatraQueryEngine engine = AdvancedViatraQueryEngine.createUnmanagedEngine(new EMFScope(rs,baseIndexOptions)); runModelModification(library, roengine, engine); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } private void runModelModification(Library library, RunOnceQueryEngine roengine, AdvancedViatraQueryEngine engine) throws ViatraQueryException { LongSciFiBooksOfAuthorMatcher matcher = engine.getMatcher(LongSciFiBooksOfAuthorMatcher.querySpecification()); Collection<LongSciFiBooksOfAuthorMatch> allMatches = matcher.getAllMatches(); Collection<LongSciFiBooksOfAuthorMatch> allROMatches = roengine.getAllMatches(LongSciFiBooksOfAuthorMatcher.querySpecification()); assertTrue(allMatches.size() == allROMatches.size()); LongSciFiBooksOfAuthorMatch match = allMatches.iterator().next(); LongSciFiBooksOfAuthorMatch romatch = allROMatches.iterator().next(); assertTrue(match.getAuthor() == romatch.getAuthor()); Book longBook = romatch.getBook(); assertTrue(match.getBook() == longBook); Book b = LibraryFactory.eINSTANCE.createBook(); b.setTitle("Long book"); b.setPages(120); b.getCategory().add(BookCategory.SCI_FI); b.getAuthors().add(library.getWriters().get(0)); library.getBooks().add(b); allROMatches = roengine.getAllMatches(LongSciFiBooksOfAuthorMatcher.querySpecification()); allMatches = matcher.getAllMatches(); assertTrue(allMatches.size() != allROMatches.size()); // manually resample values engine.getBaseIndex().resampleDerivedFeatures(); allMatches = matcher.getAllMatches(); assertTrue(allMatches.size() == allROMatches.size()); assertTrue(allROMatches.size() == 2); Set<Book> longScifiBooks = new HashSet<Book>(); for (LongSciFiBooksOfAuthorMatch m : allROMatches) { longScifiBooks.add(m.getBook()); } assertTrue(longScifiBooks.contains(b)); assertTrue(longScifiBooks.contains(longBook)); } @Test public void testSamplingModelModificationChangeVisitor() { // the results of incremental engine will be correct after resampling String address = "test"; Library library = createLibrary(address); String title = "test"; Book book = createBook(library, title, 150, BookCategory.SCI_FI); try { RunOnceQueryEngine roengine = new RunOnceQueryEngine(library); AdvancedViatraQueryEngine engine = AdvancedViatraQueryEngine.createUnmanagedEngine(new EMFScope(library,roengine.getBaseIndexOptions())); LongSciFiBooksOfAuthorMatcher matcher = engine.getMatcher(LongSciFiBooksOfAuthorMatcher.querySpecification()); Collection<LongSciFiBooksOfAuthorMatch> allMatches = matcher.getAllMatches(); assertTrue(allMatches.isEmpty()); Writer writer = createWriter(library, "test"); book.getAuthors().add(writer); engine.getBaseIndex().resampleDerivedFeatures(); allMatches = matcher.getAllMatches(); assertTrue(!allMatches.isEmpty()); LongSciFiBooksOfAuthorMatch match = allMatches.iterator().next(); assertTrue(match.getAuthor().equals(writer)); assertTrue(match.getBook().equals(book)); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } @Test public void testAutomaticSamplingModelModification() { // the results of run-once engine will be correct after automatic resampling String address = "test"; Library library = createLibrary(address); String title = "test"; Book book = createBook(library, title, 150, BookCategory.SCI_FI); try { RunOnceQueryEngine roengine = new RunOnceQueryEngine(library); roengine.setAutomaticResampling(true); Collection<LongSciFiBooksOfAuthorMatch> allMatches = roengine.getAllMatches(LongSciFiBooksOfAuthorMatcher.querySpecification()); assertTrue(allMatches.isEmpty()); Writer writer = createWriter(library, "test"); book.getAuthors().add(writer); allMatches = roengine.getAllMatches(LongSciFiBooksOfAuthorMatcher.querySpecification()); assertTrue(!allMatches.isEmpty()); LongSciFiBooksOfAuthorMatch match = allMatches.iterator().next(); assertTrue(match.getAuthor().equals(writer)); assertTrue(match.getBook().equals(book)); roengine.setAutomaticResampling(false); Writer writer2 = createWriter(library, "test2"); allMatches = roengine.getAllMatches(LongSciFiBooksOfAuthorMatcher.querySpecification()); assertTrue(!allMatches.isEmpty()); } catch (ViatraQueryException e) { e.printStackTrace(); fail(e.getShortMessage()); } } private Writer createWriter(Library library, String name) { Writer writer = LibraryFactory.eINSTANCE.createWriter(); writer.setName(name); library.getWriters().add(writer); return writer; } private Book createBook(Library library, String title, int pages, BookCategory cat) { Book book = LibraryFactory.eINSTANCE.createBook(); book.setTitle(title); book.setPages(pages); book.getCategory().add(cat); library.getBooks().add(book); return book; } private Library createLibrary(String address) { Library library = LibraryFactory.eINSTANCE.createLibrary(); library.setAddress(address); return library; } }