| /******************************************************************************* |
| * Copyright (c) 2018 Aston University. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the Eclipse |
| * Public License, v. 2.0 are satisfied: GNU General Public License, version 3. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-3.0 |
| * |
| * Contributors: |
| * Antonio Garcia-Dominguez - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.hawk.timeaware.tests; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.io.File; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.hawk.backend.tests.factories.IGraphDatabaseFactory; |
| import org.eclipse.hawk.core.graph.timeaware.ITimeAwareGraphNode; |
| import org.eclipse.hawk.epsilon.emc.EOLQueryEngine; |
| import org.eclipse.hawk.epsilon.emc.EOLQueryEngine.GraphNodeWrapper; |
| import org.eclipse.hawk.graph.Slot; |
| import org.eclipse.hawk.integration.tests.emf.EMFModelSupportFactory; |
| import org.eclipse.hawk.integration.tests.mm.Tree.Tree; |
| import org.eclipse.hawk.integration.tests.mm.Tree.TreeFactory; |
| import org.eclipse.hawk.integration.tests.mm.Tree.TreePackage; |
| import org.eclipse.hawk.svn.tests.rules.TemporarySVNRepository; |
| import org.eclipse.hawk.timeaware.queries.TimeAwareEOLQueryEngine.TimeAwareGraphNodeWrapper; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.Parameterized.Parameters; |
| |
| /** |
| * Tests for the time-aware indexing of model element nodes, using Subversion. |
| */ |
| @RunWith(Parameterized.class) |
| public class SubversionNodeHistoryTest extends AbstractTimeAwareModelIndexingTest { |
| @Rule |
| public TemporarySVNRepository svnRepository = new TemporarySVNRepository(); |
| |
| @Parameters(name = "{1}") |
| public static Iterable<Object[]> params() { |
| return TimeAwareTestSuite.caseParams(); |
| } |
| |
| public SubversionNodeHistoryTest(File baseDir, IGraphDatabaseFactory dbFactory) { |
| super(baseDir, dbFactory, new EMFModelSupportFactory()); |
| } |
| |
| @Override |
| protected void setUpMetamodels() throws Exception { |
| // Let Hawk pull in the metamodel through the registry |
| TreePackage.eINSTANCE.getName(); |
| } |
| |
| @Test |
| public void travelToMissingTimepointReturnsNull() throws Throwable { |
| twoCommitTree(); |
| scheduleAndWait(() -> { |
| GraphNodeWrapper gnw = (GraphNodeWrapper) timeAwareEOL("return Tree.latest.prev.all.first;"); |
| assertNotNull(gnw.getNode()); |
| assertNull(((ITimeAwareGraphNode) gnw.getNode()).travelInTime(ITimeAwareGraphNode.NO_SUCH_INSTANT)); |
| return null; |
| }); |
| } |
| |
| @Test |
| public void createDeleteNode() throws Throwable { |
| twoCommitTree(); |
| scheduleAndWait(() -> { |
| // .all works on revision 0 |
| assertEquals(0, timeAwareEOL("return Tree.all.size;")); |
| |
| // We also deleted everything in the latest revision |
| assertEquals(0, timeAwareEOL("return Tree.latest.all.size;")); |
| |
| // .created can return instances that have been created from a certain moment in |
| // time (even if not alive anymore) |
| assertEquals(1, timeAwareEOL("return Tree.latest.prev.size;")); |
| |
| assertEquals("xy", timeAwareEOL("return Tree.latest.prev.all.first.label;")); |
| |
| return null; |
| }); |
| } |
| |
| private void twoCommitTree() throws Exception { |
| final File fTree = new File(svnRepository.getCheckoutDirectory(), "root.xmi"); |
| Resource rTree = rsTree.createResource(URI.createFileURI(fTree.getAbsolutePath())); |
| |
| Tree t = treeFactory.createTree(); |
| t.setLabel("xy"); |
| rTree.getContents().add(t); |
| rTree.save(null); |
| |
| svnRepository.add(fTree); |
| svnRepository.commit("First commit"); |
| svnRepository.remove(fTree); |
| svnRepository.commit("Second commit - remove file"); |
| |
| requestSVNIndex(); |
| } |
| |
| @Test |
| public void countInstancesFromModelTypes() throws Throwable { |
| twoCommitTree(); |
| scheduleAndWait(() -> { |
| assertEquals(0, timeAwareEOL("return Model.types.selectOne(t|t.name='Tree').all.size;")); |
| assertEquals(1, timeAwareEOL("return Model.types.selectOne(t|t.name='Tree').latest.prev.all.size;")); |
| assertEquals(0, timeAwareEOL("return Model.types.selectOne(t|t.name='Tree').latest.prev.prev.all.size;")); |
| assertEquals(0, timeAwareEOL("return Model.types.selectOne(t|t.name='Tree').earliest.all.size;")); |
| return null; |
| }); |
| } |
| |
| @Test |
| public void countInstancesFromModel() throws Throwable { |
| twoCommitTree(); |
| scheduleAndWait(() -> { |
| assertEquals(0, timeAwareEOL("return Model.types.selectOne(t|t.name='Tree').all.size;")); |
| assertEquals(1, timeAwareEOL("return Model.types.selectOne(t|t.name='Tree').latest.prev.all.size;")); |
| assertEquals(0, timeAwareEOL("return Model.types.selectOne(t|t.name='Tree').latest.prev.prev.all.size;")); |
| assertEquals(0, timeAwareEOL("return Model.types.selectOne(t|t.name='Tree').earliest.all.size;")); |
| return null; |
| }); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void countInstancesTimeline() throws Throwable { |
| twoCommitTree(); |
| scheduleAndWait(() -> { |
| List<List<Object>> results = (List<List<Object>>) timelineEOL("return Tree.all.size;"); |
| assertEquals(0, results.get(0).get(1)); |
| assertEquals(0, (int) timeAwareEOL("return Model.allInstancesAt(t).size;", "t", results.get(0).get(0))); |
| |
| /* |
| * This is required since Epsilon does not consider type promotions for |
| * reflective operation access (from int to long). |
| */ |
| assertEquals(0, (int) timeAwareEOL("return Model.allInstancesAt(" + results.get(0).get(0) + ").size;")); |
| |
| assertEquals(1, results.get(1).get(1)); |
| assertEquals(1, (int) timeAwareEOL("return Model.allInstancesAt(t).size;", "t", results.get(1).get(0))); |
| |
| assertEquals(0, results.get(2).get(1)); |
| assertEquals(0, (int) timeAwareEOL("return Model.allInstancesAt(t).size;", "t", results.get(2).get(0))); |
| |
| return null; |
| }); |
| } |
| |
| @Test |
| public void countInstancesModelAll() throws Throwable { |
| final File fTree = new File(svnRepository.getCheckoutDirectory(), "root.xmi"); |
| Resource rTree = rsTree.createResource(URI.createFileURI(fTree.getAbsolutePath())); |
| |
| Tree t = treeFactory.createTree(); |
| t.setLabel("xy"); |
| rTree.getContents().add(t); |
| rTree.save(null); |
| |
| svnRepository.add(fTree); |
| svnRepository.commit("First commit"); |
| requestSVNIndex(); |
| |
| scheduleAndWait(() -> { |
| assertEquals(0, timeAwareEOL("return Model.allInstances.collect(t|t.label).size;")); |
| assertEquals(0, timeAwareEOL("return Model.allInstances.size;")); |
| assertEquals(1, timeAwareEOL("return Model.allInstancesNow.size;")); |
| return null; |
| }); |
| } |
| |
| private Tree keepAddingChildren() throws Exception { |
| final File fTree = new File(svnRepository.getCheckoutDirectory(), "m.xmi"); |
| Resource rTree = rsTree.createResource(URI.createFileURI(fTree.getAbsolutePath())); |
| |
| Tree tRoot = treeFactory.createTree(); |
| tRoot.setLabel("Root"); |
| rTree.getContents().add(tRoot); |
| rTree.save(null); |
| svnRepository.add(fTree); |
| svnRepository.commit("Create root"); |
| |
| for (String childLabel : Arrays.asList("T1", "T2", "T3")) { |
| Tree t1 = createChild(tRoot, childLabel); |
| rTree.save(null); |
| svnRepository.commit("Add " + childLabel); |
| } |
| |
| requestSVNIndex(); |
| return tRoot; |
| } |
| |
| @Test |
| public void commitMessages() throws Throwable { |
| keepAddingChildren(); |
| scheduleAndWait(() -> { |
| assertEquals("Create root", |
| timeAwareEOL("return Model.getRepository(Tree.latest.all.selectOne(t|t.label='Root').earliest).message;")); |
| assertEquals("Add T1", |
| timeAwareEOL("return Model.getRepository(Tree.latest.all.selectOne(t|t.label='T1').earliest).message;")); |
| assertEquals("Add T2", |
| timeAwareEOL("return Model.getRepository(Tree.latest.all.selectOne(t|t.label='T2').earliest).message;")); |
| assertEquals("Add T3", |
| timeAwareEOL("return Model.getRepository(Tree.latest.all.selectOne(t|t.label='T3').earliest).message;")); |
| |
| return null; |
| }); |
| } |
| |
| @Test |
| public void followReferences() throws Throwable { |
| keepAddingChildren(); |
| scheduleAndWait(() -> { |
| Object taGNW = timeAwareEOL("return Tree.latest.all.selectOne(t|t.label='Root').earliest.next.children.first;"); |
| assertTrue("Following a reference in a time-aware backend should result in a time-aware graph node wrapper", |
| taGNW instanceof TimeAwareGraphNodeWrapper); |
| |
| return null; |
| }); |
| } |
| |
| @Test |
| public void rangesAreBothInclusive() throws Throwable { |
| keepAddingChildren(); |
| scheduleAndWait(() -> { |
| GraphNodeWrapper gnw = (GraphNodeWrapper) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.latest.label = 'Root');" |
| ); |
| |
| ITimeAwareGraphNode taNode = (ITimeAwareGraphNode) gnw.getNode(); |
| final long earliestInstant = taNode.getEarliestInstant(); |
| final long latestInstant = taNode.getLatestInstant(); |
| |
| final List<ITimeAwareGraphNode> allVersions = taNode.getAllVersions(); |
| assertEquals(earliestInstant, allVersions.get(allVersions.size() - 1).getTime()); |
| assertEquals(latestInstant, allVersions.get(0).getTime()); |
| |
| final List<ITimeAwareGraphNode> versionsUpTo = taNode.getVersionsUpTo(latestInstant); |
| assertEquals(earliestInstant, versionsUpTo.get(versionsUpTo.size() - 1).getTime()); |
| assertEquals(latestInstant, versionsUpTo.get(0).getTime()); |
| |
| final List<ITimeAwareGraphNode> versionsFrom = taNode.getVersionsFrom(earliestInstant); |
| assertEquals(earliestInstant, versionsFrom.get(versionsFrom.size() - 1).getTime()); |
| assertEquals(latestInstant, versionsFrom.get(0).getTime()); |
| |
| final List<ITimeAwareGraphNode> versionsBW = taNode.getVersionsBetween(earliestInstant, latestInstant); |
| assertEquals(earliestInstant, versionsBW.get(versionsFrom.size() - 1).getTime()); |
| assertEquals(latestInstant, versionsBW.get(0).getTime()); |
| |
| return null; |
| }); |
| |
| } |
| |
| @Test |
| public void alwaysTrue() throws Throwable { |
| keepAddingChildren(); |
| scheduleAndWait(() -> { |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').always(v|v.label = 'Root');" |
| )); |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').never(v|v.label <> 'Root');" |
| )); |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').eventually(v|v.children.size > 2);" |
| )); |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').eventually(v|v.children.size > 3);" |
| )); |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').eventuallyAtMost(v | v.children.size > 2, 2);" |
| )); |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').eventuallyAtMost(v | v.children.size > 0, 2);" |
| )); |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').eventuallyAtLeast(v | v.children.size > 2, 2);" |
| )); |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').eventuallyAtLeast(v | v.children.size > 0, 2);" |
| )); |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').since(v|v.children.size > 1).always(v | v.children.size>1);" |
| )); |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').since(v|v.children.size > 1).eventually(v | v.children.size<1);" |
| )); |
| |
| return null; |
| }); |
| } |
| |
| @Test |
| public void after() throws Throwable { |
| keepAddingChildren(); |
| scheduleAndWait(() -> { |
| assertEquals(".after is an open left range, i.e. excludes matching version", 2, (int) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').earliest.after(v|v.children.size > 0).children.size;" |
| )); |
| assertNull(".after with no match returns null", timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').after(v|v.children.size > 5);" |
| )); |
| return null; |
| }); |
| } |
| |
| @Test |
| public void until() throws Throwable { |
| Tree tRoot = keepAddingChildren(); |
| Tree tFourthChild = TreeFactory.eINSTANCE.createTree(); |
| tFourthChild.setLabel("T4"); |
| tRoot.getChildren().add(tFourthChild); |
| tRoot.eResource().save(null); |
| svnRepository.commit("Added fourth child"); |
| indexer.requestImmediateSync(); |
| |
| scheduleAndWait(() -> { |
| assertEquals(".until is a closed end range, i.e. includes matching version", 2, (int) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').until(v|v.children.size > 1).latest.children.size;" |
| )); |
| assertNull(".until with no match returns null", timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').until(v|v.children.size > 5);" |
| )); |
| assertEquals(".since + .until works", 2, (int) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').since(v|v.children.size > 1).until(v|v.children.size > 2).versions.size;" |
| )); |
| return null; |
| }); |
| } |
| |
| @Test |
| public void before() throws Throwable { |
| Tree tRoot = keepAddingChildren(); |
| Tree tFourthChild = TreeFactory.eINSTANCE.createTree(); |
| tFourthChild.setLabel("T4"); |
| tRoot.getChildren().add(tFourthChild); |
| tRoot.eResource().save(null); |
| svnRepository.commit("Added fourth child"); |
| indexer.requestImmediateSync(); |
| |
| scheduleAndWait(() -> { |
| assertEquals(".before is a open end range, i.e. excludes matching version", 1, (int) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').before(v|v.children.size > 1).latest.children.size;" |
| )); |
| assertNull(".before with no match returns null", timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').before(v|v.children.size > 5);" |
| )); |
| assertEquals(".after + .before works", 1, (int) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').after(v|v.children.size.println('after for ' + v.time + ': ') > 0).before(v|v.children.size.println('before for ' + v.time + ': ') > 2).versions.size;" |
| )); |
| assertFalse(".after + .before can give an undefined node", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').after(v|v.children.size > 0).before(v|v.children.size > 1).isDefined();" |
| )); |
| |
| return null; |
| }); |
| } |
| |
| @Test |
| public void sinceThen() throws Throwable { |
| keepAddingChildren(); |
| |
| scheduleAndWait(() -> { |
| assertFalse("Type node - Without .sinceThen, always uses all versions", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.always(v|v.all.size > 0);" |
| )); |
| assertTrue("Type node - With .sinceThen, scope is limited to that version onwards", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.sinceThen.always(v|v.all.size > 0);" |
| )); |
| |
| assertFalse("Model element - Without .sinceThen, always uses all versions", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label = 'Root').next.always(v|v.children.size > 0);" |
| )); |
| assertTrue("Model element - With .sinceThen, scope is limited to that version onwards", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label = 'Root').next.sinceThen.always(v|v.children.size > 0);" |
| )); |
| |
| return null; |
| }); |
| } |
| |
| @Test |
| public void afterThen() throws Throwable { |
| keepAddingChildren(); |
| |
| scheduleAndWait(() -> { |
| assertFalse("Type node - Without .afterThen, always uses all versions", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.always(v|v.all.size > 1);" |
| )); |
| assertTrue("Type node - With .afterThen, scope is limited to that version onwards", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.afterThen.always(v|v.all.size > 1);" |
| )); |
| |
| assertFalse("Model element - Without .afterThen, always uses all versions", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label = 'Root').next.always(v|v.children.size > 1);" |
| )); |
| assertTrue("Model element - With .sinceThen, scope is limited to that version onwards", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label = 'Root').next.afterThen.always(v|v.children.size > 1);" |
| )); |
| |
| return null; |
| }); |
| } |
| |
| @Test |
| public void untilThen() throws Throwable { |
| keepAddingChildren(); |
| |
| scheduleAndWait(() -> { |
| assertTrue("Positive combination of .sinceThen + .untilThen", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.sinceThen.latest.prev.untilThen.always(v|v.all.size > 0 and v.all.size < 4);" |
| )); |
| assertFalse("Negative combination of .sinceThen + .untilThen", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.sinceThen.latest.untilThen.always(v|v.all.size > 0 and v.all.size < 4);" |
| )); |
| return null; |
| }); |
| } |
| |
| @Test |
| public void beforeThen() throws Throwable { |
| keepAddingChildren(); |
| |
| scheduleAndWait(() -> { |
| assertTrue("Positive combination of .afterThen + .beforeThen", (boolean) timeAwareEOL( |
| "return Tree.earliest.afterThen.latest.beforeThen.always(v|v.all.size > 0 and v.all.size < 4);" |
| )); |
| assertFalse("Negative combination of .sinceThen + .beforeThen", (boolean) timeAwareEOL( |
| "return Tree.earliest.sinceThen.latest.beforeThen.always(v|v.all.size > 0 and v.all.size < 4);" |
| )); |
| return null; |
| }); |
| } |
| |
| @Test |
| public void whenPoints() throws Throwable { |
| Tree tRoot = keepAddingChildren(); |
| tRoot.getChildren().remove(2); |
| tRoot.eResource().save(null); |
| svnRepository.commit("Removed third child"); |
| indexer.requestImmediateSync(); |
| |
| scheduleAndWait(() -> { |
| assertEquals(".when with all versions", 5, (int) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').when(v|v.children.size >= 0).versions.size;" |
| )); |
| assertFalse(".when with no versions returns null", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').when(v|v.children.size > 5).isDefined();" |
| )); |
| assertEquals(".when with some contiguous versions", 2, (int) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').when(v|v.children.size < 2).versions.size;" |
| )); |
| assertEquals(".when with one version", 1, (int) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').when(v|v.children.size = 3).versions.size;" |
| )); |
| assertEquals(".when with some non-contiguous versions", 2, (int) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').when(v|v.children.size = 2).versions.size;" |
| )); |
| assertEquals(".when with non-contiguous versions + back and forth", 2, (int) timeAwareEOL( |
| "return Tree.earliest.next.all.selectOne(t|t.label='Root').when(v|v.children.size = 2).next.prev.children.size;" |
| )); |
| |
| return null; |
| }); |
| } |
| |
| @Test |
| public void unscope() throws Throwable { |
| /* |
| * This test uses a combination of .when, .afterThen, .untilThen |
| * and .unscoped to check properties inside all intervals that match |
| * a condition. |
| */ |
| final File fTree = new File(svnRepository.getCheckoutDirectory(), "m.xmi"); |
| Resource rTree = rsTree.createResource(URI.createFileURI(fTree.getAbsolutePath())); |
| |
| Tree tRoot = treeFactory.createTree(); |
| tRoot.setLabel("Empty"); |
| rTree.getContents().add(tRoot); |
| rTree.save(null); |
| svnRepository.add(fTree); |
| svnRepository.commit("Create root"); |
| |
| // First interval |
| Tree t1A = treeFactory.createTree(); t1A.setLabel("T1A"); |
| tRoot.setLabel("NotEmpty"); |
| tRoot.getChildren().add(t1A); |
| rTree.save(null); |
| svnRepository.commit("First non-empty interval, commit 1"); |
| Tree t1B = treeFactory.createTree(); t1B.setLabel("T1B"); |
| tRoot.getChildren().add(t1B); |
| rTree.save(null); |
| svnRepository.commit("First non-empty interval, commit 2"); |
| |
| // Intermediate gap, empty node |
| tRoot.setLabel("Empty"); |
| tRoot.getChildren().clear(); |
| rTree.save(null); |
| svnRepository.commit("Gap revision, empty root"); |
| |
| // Second interval |
| tRoot.setLabel("NotEmpty"); |
| Tree t1C = treeFactory.createTree(); t1C.setLabel("T1C"); |
| tRoot.getChildren().add(t1C); |
| rTree.save(null); |
| svnRepository.commit("Second non-empty interval, commit 1"); |
| tRoot.getChildren().add(t1A); |
| tRoot.getChildren().remove(t1C); |
| rTree.save(null); |
| svnRepository.commit("Third non-empty interval, commit 2"); |
| |
| /* |
| * Final gap, empty node (would need .weakBefore/.weakUntil or tweaked .until |
| * condition without it). |
| */ |
| tRoot.setLabel("Empty"); |
| tRoot.getChildren().clear(); |
| rTree.save(null); |
| svnRepository.commit("Final gap revision, empty root"); |
| |
| requestSVNIndex(); |
| |
| // END OF TEST SETUP |
| |
| scheduleAndWait(() -> { |
| assertEquals("There are three versions with an empty root node", 3, (int)timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|not t.eContainer.isDefined())" |
| + ".earliest.when(v|v.children.isEmpty).versions.size;" |
| )); |
| assertTrue("Root element labels are not empty when they say so - .when version", (boolean)timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|not t.eContainer.isDefined())" |
| + ".earliest.when(v|v.label = 'NotEmpty')" |
| + ".always(v|not v.children.isEmpty);" |
| )); |
| assertTrue("Interval version without .unscope would give null value", (boolean)timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|not t.eContainer.isDefined())" |
| + ".earliest.when(v|v.label = 'NotEmpty' and v.prev.label = 'Empty')" |
| + ".always(v | not v.sinceThen.before(v|v.label = 'Empty').isDefined());" |
| )); |
| assertTrue("Root element labels are not empty when they say so - interval version with .unscope, .sinceThen and .before", (boolean)timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|not t.eContainer.isDefined())" |
| + ".earliest.when(v|v.label.println('label: ') = 'NotEmpty' and v.prev.label = 'Empty')" |
| + ".always(v| v.unscoped.sinceThen.before(v | v.label = 'Empty')" |
| + ".always(v | v.children.size.println('Children of ' + v.label + ' at ' + v.time + ': ') > 0)" |
| + ");" |
| )); |
| assertTrue("Root element labels are not empty when they say so - interval version with .unscope, .sinceThen and .until", (boolean)timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|not t.eContainer.isDefined())" |
| + ".earliest.when(v|v.label = 'NotEmpty' and v.prev.label = 'Empty')" |
| + ".always(v| v.unscoped.sinceThen.until(v | not v.next.isDefined() or v.next.label = 'Empty')" |
| + ".always(v | v.children.size.println('Children of ' + v.label + ' at ' + v.time + ': ') > 0)" |
| + ");" |
| )); |
| |
| assertTrue("Type node - scoped .always returns true", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.sinceThen.always(v|v.all.size > 0);" |
| )); |
| assertFalse("Type node - unscoped .always returns true", (boolean) timeAwareEOL( |
| "return Tree.earliest.next.sinceThen.unscoped.always(v|v.all.size > 0);" |
| )); |
| |
| return null; |
| }); |
| |
| } |
| |
| @Test |
| public void onceFalse() throws Throwable { |
| Tree tRoot = keepAddingChildren(); |
| tRoot.setLabel("SomethingElse"); |
| tRoot.eResource().save(null); |
| svnRepository.commit("Changed label"); |
| indexer.requestImmediateSync(); |
| |
| scheduleAndWait(() -> { |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.latest.label='SomethingElse').always(v|v.label = 'Root');" |
| )); |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.prev.all.selectOne(t|t.label='Root').always(v|v.label = 'Root');" |
| )); |
| |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.prev.all.selectOne(t|t.label='Root').never(v|v.label = 'Root');" |
| )); |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.prev.all.selectOne(t|t.label='Root').never(v|v.label <> 'Root');" |
| )); |
| |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.prev.all.selectOne(t|t.label='Root').eventually(v|v.label <> 'Root');" |
| )); |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.prev.all.selectOne(t|t.label='Root').eventually(v|v.label = 'Root');" |
| )); |
| |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.prev.all.selectOne(t|t.label='Root').eventuallyAtMost(v|v.label <> 'Root', 1);" |
| )); |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.prev.all.selectOne(t|t.label='Root').eventuallyAtMost(v|v.label = 'Root', 2);" |
| )); |
| |
| assertFalse((boolean) timeAwareEOL( |
| "return Tree.latest.prev.all.selectOne(t|t.label='Root').eventuallyAtLeast(v|v.label <> 'Root', 2);" |
| )); |
| assertTrue((boolean) timeAwareEOL( |
| "return Tree.latest.prev.all.selectOne(t|t.label='Root').eventuallyAtLeast(v|v.label = 'Root', 2);" |
| )); |
| |
| assertNotNull(".since by itself returns a node", timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.latest.label='SomethingElse').earliest.since(v|v.label <> 'Root');" |
| )); |
| assertFalse(".since + .eventually works", (boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').earliest.since(v|v.label <> 'Root').eventually(v|v.label = 'Root');" |
| )); |
| assertTrue(".since + .never works", (boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').earliest.since(v|v.label <> 'Root').never(v|v.label.println('Label at ' + v.time + ': ') = 'Root');" |
| )); |
| assertTrue(".since + .always works", (boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').earliest.since(v|v.label <> 'Root').always(v|v.children.size > 1);" |
| )); |
| assertFalse(".since can be chained", (boolean) timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.latest.label='SomethingElse').earliest.since(v|v.label <> 'Root').since(v|v.children.size = 0).isDefined();" |
| )); |
| |
| return null; |
| }); |
| } |
| |
| @Test |
| public void whenAnnotated() throws Throwable { |
| indexer.registerMetamodels(new File(baseDir, TREE_MM_PATH)); |
| indexer.addDerivedAttribute(TreePackage.eNS_URI , "Tree", "hasNoChildren", |
| Slot.ATTR_TYPE_TIMEANNOTATION, false, false, false, |
| EOLQueryEngine.TYPE, "return self.children.isEmpty;"); |
| |
| final File fTree = new File(svnRepository.getCheckoutDirectory(), "m.xmi"); |
| Resource rTree = rsTree.createResource(URI.createFileURI(fTree.getAbsolutePath())); |
| |
| // Version 1: no children |
| Tree tRoot = treeFactory.createTree(); |
| tRoot.setLabel("Root"); |
| rTree.getContents().add(tRoot); |
| rTree.save(null); |
| svnRepository.add(fTree); |
| svnRepository.commit("Create root"); |
| |
| // V2: has children |
| Tree t1 = createChild(tRoot, "t1"); |
| rTree.save(null); |
| svnRepository.commit("Add child"); |
| |
| // V3: no children again |
| tRoot.getChildren().remove(t1); |
| rTree.save(null); |
| svnRepository.commit("Remove child"); |
| |
| requestSVNIndex(); |
| |
| scheduleAndWait(() -> { |
| assertEquals(3, timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').versions.size;")); |
| System.out.println(timeAwareEOL("return Tree.latest.all.selectOne(t|t.label='Root').versions.collect(v|v.children.size);")); |
| |
| // .when uses the current timepoint as the start |
| assertEquals(2, timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').earliest.when(v|v.children.size = 0).versions.size;")); |
| assertEquals(1, timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').when(v|v.children.size = 0).versions.size;")); |
| |
| // .whenAnnotated should do the same |
| assertEquals(2, timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').earliest.whenAnnotated('hasNoChildren').versions.size;")); |
| assertEquals(1, timeAwareEOL( |
| "return Tree.latest.all.selectOne(t|t.label='Root').whenAnnotated('hasNoChildren').versions.size;")); |
| |
| return null; |
| }); |
| } |
| |
| private Tree createChild(Tree tRoot, String label) { |
| Tree t1 = treeFactory.createTree(); |
| t1.setLabel(label); |
| tRoot.getChildren().add(t1); |
| return t1; |
| } |
| |
| private void requestSVNIndex() throws Exception { |
| requestSVNIndex(svnRepository); |
| } |
| |
| } |