diff options
21 files changed, 2402 insertions, 22 deletions
diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/ecore/XcoreEcoreTest.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/ecore/XcoreEcoreTest.java index 6ef526e07..1e7523be1 100644 --- a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/ecore/XcoreEcoreTest.java +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/ecore/XcoreEcoreTest.java @@ -14,12 +14,12 @@ import org.eclipse.emf.ecore.xcore.XNamedElement; import org.eclipse.emf.ecore.xcore.XcoreInjectorProvider; import org.eclipse.emf.ecore.xcore.mappings.XcoreMapper; import org.eclipse.emf.test.ecore.xcore.GenModelFormatter; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.InjectParameter; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.Offset; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ParameterizedXtextRunner; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ResourceURIs; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.XpectString; import org.eclipse.xtext.junit4.InjectWith; -import org.eclipse.xtext.junit4.parameterized.InjectParameter; -import org.eclipse.xtext.junit4.parameterized.Offset; -import org.eclipse.xtext.junit4.parameterized.ParameterizedXtextRunner; -import org.eclipse.xtext.junit4.parameterized.ResourceURIs; -import org.eclipse.xtext.junit4.parameterized.XpectString; import org.eclipse.xtext.junit4.validation.ValidationTestHelper; import org.eclipse.xtext.resource.XtextResource; import org.junit.Test; diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/genmodel/XcoreGenModelTest.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/genmodel/XcoreGenModelTest.java index 15d384d6d..25293d23a 100644 --- a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/genmodel/XcoreGenModelTest.java +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/genmodel/XcoreGenModelTest.java @@ -14,13 +14,13 @@ import org.eclipse.emf.ecore.xcore.XNamedElement; import org.eclipse.emf.ecore.xcore.XcoreInjectorProvider; import org.eclipse.emf.ecore.xcore.mappings.XcoreMapper; import org.eclipse.emf.test.ecore.xcore.GenModelFormatter; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.InjectParameter; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.Offset; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ParameterSyntax; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ParameterizedXtextRunner; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ResourceURIs; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.XpectString; import org.eclipse.xtext.junit4.InjectWith; -import org.eclipse.xtext.junit4.parameterized.InjectParameter; -import org.eclipse.xtext.junit4.parameterized.Offset; -import org.eclipse.xtext.junit4.parameterized.ParameterSyntax; -import org.eclipse.xtext.junit4.parameterized.ParameterizedXtextRunner; -import org.eclipse.xtext.junit4.parameterized.ResourceURIs; -import org.eclipse.xtext.junit4.parameterized.XpectString; import org.eclipse.xtext.junit4.validation.ValidationTestHelper; import org.eclipse.xtext.resource.XtextResource; import org.junit.Test; diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ActualCollection.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ActualCollection.java new file mode 100644 index 000000000..1a8dfe4b5 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ActualCollection.java @@ -0,0 +1,47 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ActualCollection.ActualItem; +import org.eclipse.xtext.util.Exceptions; + +import com.google.common.base.Function; + +/** + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +public class ActualCollection extends StringCollection<ActualItem> { + + public static class ToString implements Function<Object, String> { + public String apply(Object from) { + return from == null ? "null" : from.toString(); + } + } + + public class ActualItem extends StringCollection<ActualItem>.Item { + public ActualItem(String pure) { + super(pure); + } + } + + public void init(String... actual) { + items = createCollection(); + for (String obj : actual) + items.add(new ActualItem(obj)); + } + + public void init(Iterable<Object> actual, + Class<? extends Function<Object, String>> functionClass) { + items = createCollection(); + try { + Function<Object, String> func = functionClass.newInstance(); + for (Object obj : actual) + items.add(new ActualItem(func.apply(obj))); + } catch (InstantiationException e) { + Exceptions.throwUncheckedException(e); + } catch (IllegalAccessException e) { + Exceptions.throwUncheckedException(e); + } + } +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ExpectationCollection.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ExpectationCollection.java new file mode 100644 index 000000000..4e214da53 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ExpectationCollection.java @@ -0,0 +1,354 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ActualCollection.ActualItem; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ExpectationCollection.ExpectationItem; +import org.eclipse.xtext.util.Pair; +import org.eclipse.xtext.util.Tuples; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; + +/** + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +public class ExpectationCollection extends StringCollection<ExpectationItem> { + public class ExpectationItem extends StringCollection<ExpectationItem>.Item { + protected boolean negated; + protected boolean wildcard; + + public ExpectationItem(String pure, boolean negated, boolean wildcard) { + super(pure); + this.negated = negated; + this.wildcard = wildcard; + } + + public boolean isNegated() { + return negated; + } + + public boolean isWildcard() { + return wildcard; + } + + public boolean isPure() { + return !negated && !wildcard; + } + + } + + final protected static String WILDCARD = "..."; + + public boolean isPure() { + for (ExpectationItem i : this) + if (i.isNegated() || i.isWildcard()) + return false; + return true; + } + + public boolean matches(ActualCollection actual) { + if (!isOrdered() || !actual.isOrdered()) + return matchesUnordered(actual); + else + return matchesOrdered(actual); + } + + public List<Pair<Collection<ExpectationItem>, ActualItem>> map( + ActualCollection actual) { + if (!isOrdered() || !actual.isOrdered()) + return mapUnordered(actual); + else + return mapOrdered(actual); + } + + protected List<Pair<Collection<ExpectationItem>, ActualItem>> mapUnordered( + ActualCollection actual) { + Map<ExpectationItem, List<ExpectationItem>> remainingExpectations = Maps + .newHashMap(); + Set<ExpectationItem> negatedExpectations = Sets.newLinkedHashSet(); + List<ExpectationItem> wildcardExpectations = Lists.newArrayList(); + for (ExpectationItem exp : this) + if (exp.isWildcard()) + wildcardExpectations.add(exp); + else if (exp.isNegated()) + negatedExpectations.add(exp); + else { + List<ExpectationItem> items = remainingExpectations.get(exp); + if (items == null) + remainingExpectations + .put(exp, items = Lists.newArrayList()); + items.add(exp); + } + List<ActualItem> sortedActual = Lists.newArrayList(actual); + Collections.sort(sortedActual); + List<Pair<Collection<ExpectationItem>, ActualItem>> result = Lists + .newArrayList(); + for (ActualItem act : sortedActual) { + List<ExpectationItem> exp = remainingExpectations.get(act); + if (exp != null && !exp.isEmpty()) { + ExpectationItem ei = exp.get(exp.size() - 1); + exp.remove(exp.size() - 1); + result.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + Collections.singleton(ei), act)); + } else if (!negatedExpectations.contains(act)) { + if (wildcardExpectations.isEmpty()) + result.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + negatedExpectations, act)); + else + result.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + wildcardExpectations, act)); + } else + result.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create(null, + act)); + } + List<ExpectationItem> remainingExpectationsSorted = Lists + .newArrayList(); + for (List<ExpectationItem> x : remainingExpectations.values()) + remainingExpectationsSorted.addAll(x); + Collections.sort(remainingExpectationsSorted); + for (ExpectationItem ei : remainingExpectationsSorted) + if (ei.isPure()) + result.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + Collections.singleton(ei), null)); + return result; + } + + protected List<Pair<Collection<ExpectationItem>, ActualItem>> mapOrdered( + ActualCollection actual) { + List<Pair<Collection<ExpectationItem>, ActualItem>> r = Lists + .newArrayList(); + List<ExpectationItem> exIt = Lists.newArrayList(this.iterator()); + List<ActualItem> actIt = Lists.newArrayList(actual.iterator()); + int exp = 0, act = 0; + while (exp < exIt.size() && act < actIt.size()) { + if (exIt.get(exp).isNegated() || exIt.get(exp).isWildcard()) { + Set<ExpectationItem> expectedNegated = Sets.newLinkedHashSet(); + Set<ExpectationItem> expectedWildcard = Sets.newLinkedHashSet(); + while (exp < exIt.size() && !exIt.get(exp).isPure()) { + if (exIt.get(exp).isNegated()) + expectedNegated.add(exIt.get(exp)); + else if (exIt.get(exp).isWildcard()) + expectedWildcard.add(exIt.get(exp)); + exp++; + } + while (act < actIt.size()) { + if (exp < exIt.size() && exIt.get(exp).isPure() + && exIt.get(exp).equals(actIt.get(act))) { + r.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + Collections.singleton(exIt.get(exp)), + actIt.get(act))); + exp++; + act++; + break; + } + if (!expectedNegated.contains(actIt.get(act))) { + if (expectedWildcard.isEmpty()) + r.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + expectedNegated, actIt.get(act))); + else + r.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + expectedWildcard, actIt.get(act))); + } else + r.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + null, actIt.get(act))); + act++; + } + } else if (exIt.get(exp).equals(actIt.get(act))) { + r.add(Tuples.<Collection<ExpectationItem>, ActualItem> create( + Collections.singleton(exIt.get(exp)), actIt.get(act))); + exp++; + act++; + } else { + int lact = act + 1; + while (lact < actIt.size() + && !exIt.get(exp).equals(actIt.get(lact))) + lact++; + if (lact < actIt.size()) { + while (act < lact) { + r.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + null, actIt.get(act))); + act++; + } + r.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + Collections.singleton(exIt.get(exp)), + actIt.get(lact))); + exp++; + act++; + } else { + r.add(Tuples + .<Collection<ExpectationItem>, ActualItem> create( + Collections.singleton(exIt.get(exp)), null)); + exp++; + } + } + } + while (act < actIt.size()) { + r.add(Tuples.<Collection<ExpectationItem>, ActualItem> create(null, + actIt.get(act))); + act++; + } + while (exp < exIt.size()) { + if (exIt.get(exp).isPure()) + r.add(Tuples.<Collection<ExpectationItem>, ActualItem> create( + Collections.singleton(exIt.get(exp)), null)); + exp++; + } + return r; + } + + protected boolean matchesOrdered(ActualCollection actual) { + if (isPure()) + return items.equals(actual.items); + Iterator<ExpectationItem> exIt = this.iterator(); + Iterator<ActualItem> actIt = actual.iterator(); + while (exIt.hasNext() && actIt.hasNext()) { + ExpectationItem exp = exIt.next(); + ActualItem act = actIt.next(); + if (exp.isNegated() || exp.isWildcard()) { + List<ExpectationItem> expectedNegated = Lists.newArrayList(); + if (exp.isNegated()) + expectedNegated.add(exp); + while (exIt.hasNext()) { + exp = exIt.next(); + if (exp.isNegated()) + expectedNegated.add(exp); + else if (exp.isPure()) + break; + } + Set<ActualItem> actualSkipped = Sets.newHashSet(); + if (exp.isPure()) { + if (!exp.equals(act)) { + actualSkipped.add(act); + while (actIt.hasNext()) { + act = actIt.next(); + if (exp.equals(act)) + break; + else + actualSkipped.add(act); + } + } + if (!exp.equals(act)) + return false; + } else { + actualSkipped.add(act); + while (actIt.hasNext()) + actualSkipped.add(actIt.next()); + } + for (ExpectationItem e : expectedNegated) + if (actualSkipped.contains(e)) + return false; + } else if (!exp.equals(act)) + return false; + } + if (actIt.hasNext()) + return false; + while (exIt.hasNext()) + if (exIt.next().isPure()) + return false; + return true; + } + + protected boolean matchesUnordered(ActualCollection actual) { + if (isPure()) + return items.equals(actual.items); + Multiset<ActualItem> act = HashMultiset.create(actual); + for (ExpectationItem item : this) { + if (item.isWildcard()) + continue; + if (item.isNegated()) { + if (act.contains(item)) + return false; + } else if (!act.contains(item)) + return false; + } + return true; + } + + public void init(String expectation) { + items = createCollection(); + boolean esc = false, escaped = true, quote = false, quoted = false, neg = false; + StringBuilder item = new StringBuilder(); + StringBuilder ws = new StringBuilder(); + for (int i = 0; i < expectation.length(); i++) { + char c = expectation.charAt(i); + if (!esc) { + if (!quote) { + if (c == separator) { + if (item.length() > 0) + items.add(createItem(item.toString(), neg, quoted, + escaped)); + neg = quoted = escaped = false; + item = new StringBuilder(); + ws = new StringBuilder(); + continue; + } else if (Character.isWhitespace(c)) { + ws.append(c); + continue; + } else if (c == '!' && item.length() == 0) { + neg = true; + continue; + } + } + if (this.quoted && c == this.quote) { + if (quote) + item.append(ws); + ws = new StringBuilder(); + quote = !quote; + quoted = true; + continue; + } else if (c == '\\') { + escaped = esc = true; + continue; + } + } else { + esc = false; + switch (c) { + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + } + } + if (ws.length() > 0) { + if (item.length() > 0) + item.append(ws); + ws = new StringBuilder(); + } + item.append(c); + } + if (item.length() > 0) + items.add(createItem(item.toString(), neg, quoted, escaped)); + } + + protected ExpectationItem createItem(String item, boolean negated, + boolean quoted, boolean escaped) { + if (!escaped && !negated && !quoted && WILDCARD.equals(item)) + return new ExpectationItem(item.toString(), false, true); + return new ExpectationItem(item.toString(), negated, false); + } +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/IParameterProvider.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/IParameterProvider.java new file mode 100644 index 000000000..929b313f0 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/IParameterProvider.java @@ -0,0 +1,61 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Strings; + +import com.google.common.collect.Multimap; +import com.google.inject.ImplementedBy; + +/** + * use org.xpect.parameter.IParameterProvider from www.xpect-tests.org instead. + * + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@ImplementedBy(XpectParameterProvider.class) +public interface IParameterProvider { + + public interface IExpectation { + public class Util { + public static String replace(XtextResource res, IExpectation exp, + String value) { + String indented; + if (!Strings.isEmpty(exp.getIndentation())) + indented = exp.getIndentation() + + value.replace("\n", "\n" + exp.getIndentation()); + else + indented = value; + String document = res.getParseResult().getRootNode().getText(); + String before = document.substring(0, exp.getOffset()); + String after = document.substring( + exp.getOffset() + exp.getLength(), document.length()); + return before + indented + after; + } + } + + String getExpectation(); + + String getIndentation(); + + int getLength(); + + int getOffset(); + } + + public interface IParameterAcceptor { + void acceptImportURI(URI uri); + + void acceptTest(String title, String method, + Multimap<String, Object> params, IExpectation expectation, + boolean ignore); + + // void acceptTestClass(Class<?> clazz); + } + + void collectParameters(Class<?> testClass, XtextResource resource, + IParameterAcceptor acceptor); + +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/InjectParameter.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/InjectParameter.java new file mode 100644 index 000000000..52a920334 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/InjectParameter.java @@ -0,0 +1,21 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * use www.xpect-tests.org insted. In Xpect, thre is no direct counterpart for + * this annotation since each kind of injected parameter has its own annotation. + * + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface InjectParameter { + String value() default ""; +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/Offset.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/Offset.java new file mode 100644 index 000000000..abc92d493 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/Offset.java @@ -0,0 +1,138 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.nodemodel.ILeafNode; +import org.eclipse.xtext.nodemodel.INode; +import org.eclipse.xtext.nodemodel.util.NodeModelUtils; +import org.eclipse.xtext.parsetree.reconstr.impl.NodeIterator; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Pair; +import org.eclipse.xtext.util.Tuples; +import org.junit.Assert; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +/** + * use org.xpect.xtext.lib.setup.ThisOffset or + * org.xpect.parameter.ParameterParser with token rule OFFSET from + * www.xpect-tests.org instead. ThisOffset is an annotation that can be used for + * method parameters. ParameterParser is a an annotation to define syntax for + * test-parameters that can be specified in the DSL file (example: XPECT + * myTestMethod myparam1 myparam2). The method parameter type can be any of the + * following types int, INode, EObject, ICrossEReferenceAndEObject, + * IEAttributeAndEObject, IEReferenceAndEObject, IEStructuralFeatureAndEObject, + * etc. + * + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +public class Offset { + protected int offset; + protected XtextResource resource; + + public Offset(XtextResource resource, int offset) { + super(); + this.resource = resource; + this.offset = offset; + } + + public EObject getEObject() { + EObject object = NodeModelUtils + .findActualSemanticObjectFor(getLeafNodeAtOffset()); + Assert.assertNotNull("No EObject found at offset " + offset, object); + return object; + } + + public Pair<EObject, EStructuralFeature> getEStructuralFeatureByOffset() { + return getEStructuralFeatureByOffset(Predicates + .<EStructuralFeature> alwaysTrue()); + } + + public Pair<EObject, EStructuralFeature> getEStructuralFeatureByOffset( + Predicate<EStructuralFeature> matches) { + INode leaf = getLeafNodeAtOffset(); + NodeIterator ni = null; + while (ni == null || ni.hasNext()) { + INode next = ni == null ? leaf : ni.next(); + if (ni == null) + ni = new NodeIterator(leaf); + Assignment ass = GrammarUtil.containingAssignment(next + .getGrammarElement()); + if (ass != null) { + EObject object = NodeModelUtils + .findActualSemanticObjectFor(next); + EStructuralFeature feat = object.eClass() + .getEStructuralFeature(ass.getFeature()); + if (feat != null && matches.apply(feat)) + return Tuples.create(object, feat); + } + } + Assert.fail("No EStructuralFeature found at offset " + offset); + return null; + } + + public Pair<EObject, EStructuralFeature> getEStructuralFeatureByParent() { + INode leaf = getLeafNodeAtOffset(); + EObject object = NodeModelUtils.findActualSemanticObjectFor(leaf); + Assert.assertNotNull("No EObject found at offset " + offset, object); + Assignment ass = GrammarUtil.containingAssignment(leaf + .getGrammarElement()); + while (ass == null && leaf.getParent() != null) { + leaf = leaf.getParent(); + ass = GrammarUtil.containingAssignment(leaf.getGrammarElement()); + } + Assert.assertNotNull("No Assignment found at offset " + offset, ass); + EStructuralFeature feature = object.eClass().getEStructuralFeature( + ass.getFeature()); + return Tuples.create(object, feature); + } + + public ILeafNode getLeafNodeAtOffset() { + ILeafNode node = NodeModelUtils.findLeafNodeAtOffset(resource + .getParseResult().getRootNode(), offset); + Assert.assertNotNull("No Leaf Node found at offset " + offset, node); + return node; + } + + @Override + public int hashCode() { + return offset * (resource != null ? resource.hashCode() : 1); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) + return false; + Offset off = (Offset) obj; + return resource == off.resource && offset == off.offset; + } + + public int getOffset() { + return offset; + } + + public XtextResource getResource() { + return resource; + } + + @Override + public String toString() { + if (resource == null) + return "(resource is null)"; + if (resource.getParseResult() == null + || resource.getParseResult().getRootNode() == null) + return "(resource hs no parse result)"; + String text = resource.getParseResult().getRootNode().getText(); + if (offset < 0 || offset > text.length()) + return "(offset out of range)"; + int from = Math.max(0, offset - 5); + int to = Math.min(text.length(), offset + 5); + return text.substring(from, offset) + "!" + text.substring(offset, to); + } +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ParameterSyntax.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ParameterSyntax.java new file mode 100644 index 000000000..af3523a5e --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ParameterSyntax.java @@ -0,0 +1,21 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * use org.xpect.parameter.ParameterParser from www.xpect-tests.org instead. + * + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ParameterSyntax { + String value() default ""; + +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ParameterizedXtextRunner.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ParameterizedXtextRunner.java new file mode 100644 index 000000000..68d68277d --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ParameterizedXtextRunner.java @@ -0,0 +1,521 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import static org.eclipse.xtext.util.Exceptions.throwUncheckedException; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.IParameterProvider.IExpectation; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.IParameterProvider.IParameterAcceptor; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ParameterizedXtextRunner.ResourceRunner; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.TestExpectationValidator.ITestExpectationValidator; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.TestExpectationValidator.TestResult; +import org.eclipse.xtext.junit4.IInjectorProvider; +import org.eclipse.xtext.junit4.IRegistryConfigurator; +import org.eclipse.xtext.junit4.InjectWith; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Exceptions; +import org.eclipse.xtext.util.ReflectionUtil; +import org.eclipse.xtext.util.Strings; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.ParentRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.TestClass; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +/** + * use org.xpect.runner.XpectRunner from www.xpect-tests.org instead. + * + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +public class ParameterizedXtextRunner extends ParentRunner<ResourceRunner> { + + protected static class MethodWithExpectation { + protected Method method; + + protected ITestExpectationValidator<Object> validator; + + public MethodWithExpectation(Method method) throws Throwable { + super(); + this.method = method; + this.validator = findValidator(); + } + + protected ITestExpectationValidator<? extends Object> createValidator( + Test annotation) { + if (method.getReturnType() != void.class) + throw new RuntimeException( + "The method is expected to return void. Method: " + + method); + return new TestExpectationValidator.NullTestResultValidator( + annotation); + } + + protected ITestExpectationValidator<? extends Object> createValidator( + TestExpectationValidator trv, Annotation annotation) + throws Throwable { + Class<? extends ITestExpectationValidator<?>> validatorClass = trv + .validator(); + Class<?> expectedResultType = getExpectedResultType(validatorClass); + boolean voidExpected = expectedResultType == Void.TYPE + || expectedResultType == Void.class; + boolean returnsExpected = method.getReturnType() == Void.TYPE + || method.getReturnType() == Void.class; + if (!expectedResultType.isAssignableFrom(method.getReturnType()) + && (!voidExpected || !returnsExpected)) + throw new RuntimeException("The return type of " + method + + " is expected to be " + expectedResultType.getName()); + Constructor<? extends ITestExpectationValidator<?>> c = validatorClass + .getConstructor(annotation.annotationType()); + return c.newInstance(annotation); + } + + @SuppressWarnings("unchecked") + protected ITestExpectationValidator<Object> findValidator() + throws Throwable { + for (Annotation an : method.getAnnotations()) + if (an instanceof Test) + return (ITestExpectationValidator<Object>) createValidator((Test) an); + else { + TestExpectationValidator trv = an.annotationType() + .getAnnotation(TestExpectationValidator.class); + if (trv != null) + return (ITestExpectationValidator<Object>) createValidator( + trv, an); + } + Class<?>[] annotations = { Test.class, Xpect.class, + XpectString.class, XpectLines.class, + XpectCommaSeparatedValues.class }; + List<String> names = Lists.newArrayList(); + for (Class<?> o : annotations) + names.add("@" + o.getSimpleName()); + throw new RuntimeException("Annotation missing: " + + Joiner.on(", ").join(names) + ", etc. in: " + method); + } + + protected Class<?> getExpectedResultType( + Class<? extends ITestExpectationValidator<?>> clazz) { + for (Method meth : clazz.getMethods()) { + Annotation[][] annotations = meth.getParameterAnnotations(); + for (int i = 0; i < annotations.length; i++) + for (Annotation an : annotations[i]) + if (an instanceof TestResult) + return meth.getParameterTypes()[i]; + } + throw new RuntimeException("One of the method parameters of " + + clazz.getName() + " must be annotated with @" + + TestResult.class.getSimpleName()); + } + + public Method getMethod() { + return method; + } + + public ITestExpectationValidator<Object> getValidator() { + return validator; + } + } + + protected static class ParameterSetRunner { + protected Description description; + protected IExpectation expectation; + protected boolean ignore; + protected int index = -1; + protected String methodName; + protected Multimap<String, Object> params; + protected ResourceRunner runner; + protected String title; + + public Description getDescription() { + if (description == null) + description = Description.createTestDescription( + runner.clazz.getJavaClass(), getFullTitle()); + return description; + } + + public IExpectation getExpectation() { + return expectation; + } + + public String getFullTitle() { + StringBuilder result = new StringBuilder(); + result.append(methodName); + if (!Strings.isEmpty(title)) { + result.append(" "); + result.append(title); + } + if (index > -1) { + result.append("#"); + result.append(index); + } + result.append(" - "); + result.append(runner.resource.getURI().lastSegment()); + return result.toString(); + } + + public IInjectorProvider getInjectorProvider() { + return runner.injectorProvider; + } + + public String getMethdoName() { + return methodName; + } + + public Multimap<String, Object> getParams() { + return params; + } + + public XtextResource getResource() { + return runner.resource; + } + + public Class<?> getTestClass() { + return runner.clazz.getJavaClass(); + } + + public void init(ResourceRunner runner, String title, String method, + Multimap<String, Object> params, IExpectation expectation, + boolean ignore) { + this.runner = runner; + this.title = title; + this.methodName = method; + this.params = params; + this.expectation = expectation; + this.ignore = ignore; + } + + public boolean isIgnore() { + return ignore; + } + + } + + protected static class ResourceRunner implements IParameterAcceptor { + protected TestClass clazz; + protected Description description; + protected IInjectorProvider injectorProvider; + protected List<ParameterSetRunner> parameterSets = Lists.newArrayList(); + protected XtextResource resource; + protected ResourceSet resourceSet; + + public void acceptImportURI(URI uri) { + resourceSet.getResource(uri, true); + } + + public void acceptTest(String title, String method, + Multimap<String, Object> params, IExpectation expectation, + boolean ignore) { + ParameterSetRunner runner = injectorProvider.getInjector() + .getInstance(ParameterSetRunner.class); + runner.init(this, title, method, params, expectation, ignore); + parameterSets.add(runner); + } + + protected void collectParameters() { + IParameterProvider parameterProvider = injectorProvider + .getInjector().getInstance(IParameterProvider.class); + parameterProvider.collectParameters(clazz.getJavaClass(), resource, + this); + } + + public Description getDescription() { + if (description == null) { + description = Description.createSuiteDescription(resource + .getURI().lastSegment()); + for (ParameterSetRunner child : parameterSets) + description.addChild(child.getDescription()); + } + return description; + } + + public List<ParameterSetRunner> getParameterSets() { + return parameterSets; + } + + public void init(TestClass clazz, IInjectorProvider injector, URI uri) { + this.clazz = clazz; + this.injectorProvider = injector; + this.resourceSet = injectorProvider.getInjector().getInstance( + ResourceSet.class); + this.resource = (XtextResource) resourceSet.getResource(uri, true); + collectParameters(); + setIndex(); + } + + protected void setIndex() { + Set<String> visited = Sets.newHashSet(); + Set<String> duplicate = Sets.newHashSet(); + for (ParameterSetRunner r : getParameterSets()) + if (!visited.add(r.getFullTitle())) + duplicate.add(r.getFullTitle()); + Map<String, Integer> counter = Maps.newHashMap(); + for (ParameterSetRunner r : getParameterSets()) + if (duplicate.contains(r.getFullTitle())) { + String title = r.getFullTitle(); + Integer count = counter.get(title); + if (count == null) + count = 1; + else + count++; + counter.put(title, count); + r.index = count; + } + } + + } + + private static Map<Class<?>, IInjectorProvider> injectorProviderClassCache = Maps + .newHashMap(); + + protected List<ResourceRunner> children; + + protected Filter filter = null; + + protected Map<String, MethodWithExpectation> methods = Maps.newHashMap(); + + public ParameterizedXtextRunner(Class<?> testClass) + throws InitializationError { + super(testClass); + } + + protected IInjectorProvider createInjectorProvider() { + IInjectorProvider injectorProvider = null; + InjectWith injectWith = getTestClass().getJavaClass().getAnnotation( + InjectWith.class); + if (injectWith != null) { + try { + injectorProvider = injectWith.value().newInstance(); + } catch (Exception e) { + throwUncheckedException(e); + } + } + return injectorProvider; + } + + @Override + protected Description describeChild(ResourceRunner child) { + return child.getDescription(); + } + + @Override + public void filter(Filter filter) throws NoTestsRemainException { + super.filter(filter); + this.filter = filter; + } + + protected MethodWithExpectation findTestMethod(String name) + throws Throwable { + MethodWithExpectation result = methods.get(name); + if (result == null) { + Method method = getTestClass().getJavaClass().getMethod(name); + if (method == null) + throw new RuntimeException("Method " + name + + "() not found in " + getTestClass().getName()); + result = new MethodWithExpectation(method); + methods.put(name, result); + } + return result; + } + + @Override + protected List<ResourceRunner> getChildren() { + if (children == null) { + children = Lists.newArrayList(); + IInjectorProvider injectorProvider = getOrCreateInjectorProvider(); + for (URI uri : getURIs()) { + ResourceRunner child = injectorProvider.getInjector() + .getInstance(ResourceRunner.class); + child.init(getTestClass(), injectorProvider, uri); + children.add(child); + } + } + return children; + } + + protected IInjectorProvider getInjectorProvider() { + return injectorProviderClassCache.get(getTestClass().getJavaClass()); + } + + protected IInjectorProvider getOrCreateInjectorProvider() { + IInjectorProvider injectorProvider = getInjectorProvider(); + if (injectorProvider == null) { + injectorProvider = createInjectorProvider(); + injectorProviderClassCache.put(getTestClass().getJavaClass(), + injectorProvider); + } + return injectorProvider; + } + + protected List<URI> getURIs() { + ResourceURIs classAnnotation = getTestClass().getJavaClass() + .getAnnotation(ResourceURIs.class); + if (classAnnotation != null) + return getURIs(classAnnotation); + for (FrameworkMethod method : getTestClass().getAnnotatedMethods( + ResourceURIs.class)) { + int modifiers = method.getMethod().getModifiers(); + if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) + return getURIs(method); + } + throw new RuntimeException( + "The class " + + getTestClass().getJavaClass() + + " or one of its static public methods needs to be annotated with @" + + ResourceURIs.class.getSimpleName()); + } + + @SuppressWarnings("unchecked") + protected List<URI> getURIs(FrameworkMethod method) { + try { + return (List<URI>) method.invokeExplosively(null); + } catch (Throwable e) { + Exceptions.throwUncheckedException(e); + return Collections.emptyList(); + } + } + + protected List<URI> getURIs(ResourceURIs uris) { + List<URI> result = Lists.newArrayList(); + ResourceURICollector collector = new ResourceURICollector(); + if (uris.files().length > 0) + result.addAll(collector.collectFiles(uris.files())); + if (!Strings.isEmpty(uris.baseDir()) + || uris.fileExtensions().length > 0) { + Assert.assertFalse("@ResourceURIs needs a baseURI", + Strings.isEmpty(uris.baseDir())); + Assert.assertTrue("@ResourceURIs needs at least one fileExtension", + uris.fileExtensions().length > 0); + result.addAll(collector.collectFiles(uris.baseDir(), + uris.fileExtensions())); + } + return result; + } + + protected Object newTestInstance() throws SecurityException, + NoSuchMethodException, IllegalArgumentException, + InstantiationException, IllegalAccessException, + InvocationTargetException { + Constructor<?> constructor = getTestClass().getJavaClass() + .getConstructor(); + return constructor.newInstance(); + } + + protected void injectParameters(Object test, Multimap<String, Object> params) { + List<Field> fields = Lists.newArrayList(); + Class<?> clazz = test.getClass(); + while (clazz != null && clazz != Object.class) { + fields.addAll(Lists.newArrayList(clazz.getDeclaredFields())); + clazz = clazz.getSuperclass(); + } + for (Field field : fields) { + InjectParameter annotation = field + .getAnnotation(InjectParameter.class); + if (annotation != null) { + String name = Strings.isEmpty(annotation.value()) ? field + .getName() : annotation.value(); + Class<?> fieldType = ReflectionUtil.getObjectType(field + .getType()); + for (Object value : params.get(name)) + if (fieldType.isInstance(value)) { + field.setAccessible(true); + try { + field.set(test, value); + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + } + } + } + + } + + // protected Object newTestInstance(Object[][] allParams) throws + // IllegalArgumentException, InstantiationException, + // IllegalAccessException, InvocationTargetException { + // for (Object[] params : allParams) + // ROOT: for (Constructor<?> candidate : + // getTestClass().getJavaClass().getConstructors()) + // if (candidate.getParameterTypes().length == params.length) { + // for (int i = 0; i < params.length; i++) + // if (params[i] != null + // && !ReflectionUtil.getObjectType(candidate.getParameterTypes()[i]) + // .isInstance(params[i])) + // continue ROOT; + // return candidate.newInstance(params); + // } + // List<String> alternatives = Lists.newArrayList(); + // for (Object[] params : allParams) { + // List<String> types = Lists.newArrayList(); + // for (Object p : params) + // types.add(p == null ? "?" : p.getClass().getName()); + // alternatives.add(Joiner.on(", ").join(types)); + // } + // throw new RuntimeException("No valid constructor found in class " + + // getTestClass().getJavaClass().getName() + // + " for types " + Joiner.on(" or ").join(alternatives)); + // } + + protected void runChild(ParameterSetRunner ps) throws Throwable { + MethodWithExpectation method = findTestMethod(ps.getMethdoName()); + Object test = newTestInstance(); + if (ps.getInjectorProvider() instanceof IRegistryConfigurator) + ((IRegistryConfigurator) ps.getInjectorProvider()).setupRegistry(); + try { + injectParameters(test, ps.getParams()); + ps.getInjectorProvider().getInjector().injectMembers(test); + Object result = method.getMethod().invoke(test); + method.getValidator().validate(ps.getResource(), + ps.getExpectation(), result); + } catch (InvocationTargetException e) { + throw e.getCause(); + } finally { + if (ps.getInjectorProvider() instanceof IRegistryConfigurator) + ((IRegistryConfigurator) ps.getInjectorProvider()) + .restoreRegistry(); + } + } + + @Override + protected void runChild(ResourceRunner arg0, RunNotifier notifier) { + for (ParameterSetRunner ps : arg0.getParameterSets()) + if (filter == null || filter.shouldRun(ps.getDescription())) { + notifier.fireTestStarted(ps.getDescription()); + if (ps.isIgnore()) + notifier.fireTestIgnored(ps.getDescription()); + else + try { + runChild(ps); + } catch (Throwable e) { + notifier.fireTestFailure(new Failure(ps + .getDescription(), e)); + } + notifier.fireTestFinished(ps.getDescription()); + } + } + +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ResourceURICollector.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ResourceURICollector.java new file mode 100644 index 000000000..18af84d9a --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ResourceURICollector.java @@ -0,0 +1,71 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.io.File; +import java.util.List; + +import org.eclipse.emf.common.util.URI; + +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; + +/** + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +public class ResourceURICollector { + public static class FilePredicate implements Predicate<File> { + private final String[] fileExtensions; + + public FilePredicate(String... fileExts) { + this.fileExtensions = fileExts; + } + + public boolean apply(File input) { + for (String ext : fileExtensions) + if (input.getName().endsWith("." + ext)) + return true; + return false; + } + } + + protected void collectFiles(File dir, List<URI> result, + Predicate<File> shouldCollect) { + for (File child : dir.listFiles()) { + if (shouldCollect.apply(child)) + result.add(createURI(child)); + if (child.isDirectory()) + collectFiles(child, result, shouldCollect); + } + } + + public List<URI> collectFiles(String directory, + Predicate<File> shouldCollect) { + File dir = new File(directory); + if (!dir.isDirectory()) + throw new RuntimeException("Directory not found: " + directory); + List<URI> result = Lists.newArrayList(); + collectFiles(new File(directory), result, shouldCollect); + return result; + } + + public List<URI> collectFiles(String directory, String... fileExtensions) { + return collectFiles(directory, new FilePredicate(fileExtensions)); + } + + public List<URI> collectFiles(String... files) { + List<URI> result = Lists.newArrayList(); + for (String file : files) { + File f = new File(file); + if (!f.exists()) + throw new RuntimeException("File not found: " + file); + result.add(createURI(f)); + } + return result; + } + + protected URI createURI(File file) { + return URI.createFileURI(file.getAbsolutePath()); + } +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ResourceURIs.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ResourceURIs.java new file mode 100644 index 000000000..90b5e1307 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/ResourceURIs.java @@ -0,0 +1,26 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Use org.xpect.runner.XpectTestFiles from www.xpect-tests.org instead. + * + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface ResourceURIs { + String baseDir() default ""; + + String[] fileExtensions() default {}; + + String[] files() default {}; +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/StringCollection.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/StringCollection.java new file mode 100644 index 000000000..18aba9cbe --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/StringCollection.java @@ -0,0 +1,166 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.util.Collection; +import java.util.Iterator; +import java.util.regex.Pattern; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Lists; + +/** + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +public class StringCollection<T extends StringCollection<T>.Item> implements + Iterable<T> { + public class Item implements Comparable<Item> { + protected String escaped = null; + protected String normalized = null; + protected String pure; + + public Item(String pure) { + super(); + this.pure = pure; + } + + @Override + @SuppressWarnings("unchecked") + public boolean equals(Object obj) { + if (obj == null || !Item.class.isInstance(obj)) + return false; + return getNormalized().equals(((Item) obj).getNormalized()); + } + + public String getEscaped() { + if (escaped == null) + escaped = escape(pure); + return escaped; + } + + public String getNormalized() { + if (normalized == null) + normalized = normalize(pure); + return normalized; + } + + public String getPure() { + return pure; + } + + @Override + public int hashCode() { + return getNormalized().hashCode(); + } + + @Override + public String toString() { + return pure; + } + + public int compareTo(Item o) { + if (normalized == null) + return -1; + if (o == null || o.normalized == null) + return 1; + return normalized.compareTo(o.normalized); + } + + } + + protected static final Pattern WS = Pattern.compile("\\s+", + Pattern.MULTILINE); + protected boolean caseSensitive = true; + protected Collection<T> items; + protected boolean ordered = true; + protected char quote = '\"'; + protected boolean quoted = true; + protected char separator = ','; + protected boolean whitespaceSensitive = true; + + protected Collection<T> createCollection() { + return ordered ? Lists.<T> newArrayList() : HashMultiset.<T> create(); + } + + protected String escape(String str) { + str = str.replace("\\", "\\\\").replace("\n", "\\n") + .replace("\r", "\\r"); + if (quoted) + return quote + str.replace(String.valueOf(quote), "\\" + quote) + + quote; + if (str.startsWith("!") || str.equals("...")) + str = "\\" + str; + str = str.replace(String.valueOf(separator), "\\" + separator); + if (whitespaceSensitive) + return str; + return str.trim(); + } + + protected String escapeUnquoted(String str) { + return str.replaceAll("\\s+", " "); + } + + public char getQuote() { + return quote; + } + + public char getSeparator() { + return separator; + } + + public boolean isCaseSensitive() { + return caseSensitive; + } + + public boolean isOrdered() { + return ordered; + } + + public boolean isQuoted() { + return quoted; + } + + public boolean isWhitespaceSensitive() { + return whitespaceSensitive; + } + + public Iterator<T> iterator() { + return items.iterator(); + } + + protected String normalize(String item) { + if (!quoted) + item = item.trim(); + if (!whitespaceSensitive) + item = WS.matcher(item).replaceAll(""); + if (!caseSensitive) + item = item.toLowerCase(); + return item; + } + + public void setCaseSensitive(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + } + + public void setOrdered(boolean ordered) { + this.ordered = ordered; + } + + public void setQuote(char quote) { + this.quote = quote; + } + + public void setQuoted(boolean quoted) { + this.quoted = quoted; + } + + public void setSeparator(char separator) { + this.separator = separator; + } + + public void setWhitespaceSensitive(boolean whitespaceSensitive) { + this.whitespaceSensitive = whitespaceSensitive; + } + +}
\ No newline at end of file diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/TestExpectationValidator.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/TestExpectationValidator.java new file mode 100644 index 000000000..5cba6a539 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/TestExpectationValidator.java @@ -0,0 +1,52 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.IParameterProvider.IExpectation; +import org.eclipse.xtext.resource.XtextResource; +import org.junit.Assert; +import org.junit.Test; + +/** + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface TestExpectationValidator { + + public interface ITestExpectationValidator<RESULT> { + + void validate(XtextResource resource, IExpectation expectation, + RESULT actual); + } + + public class NullTestResultValidator implements + ITestExpectationValidator<Void> { + public NullTestResultValidator(Test config) { + } + + public NullTestResultValidator(Xpect config) { + } + + public void validate(XtextResource resource, IExpectation expectation, + @TestResult Void actual) { + if (expectation != null && expectation.getExpectation() != null + && expectation.getExpectation().length() > 0) + Assert.fail("This test should not have an expectation. Expectation: '" + + expectation + "'."); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.PARAMETER) + public @interface TestResult { + } + + Class<? extends ITestExpectationValidator<?>> validator(); +}
\ No newline at end of file diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/Xpect.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/Xpect.java new file mode 100644 index 000000000..bc85f5055 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/Xpect.java @@ -0,0 +1,20 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This class will be removed in the next release after 2.4.2 + * + * use org.xpect.runner.Xpect from www.xpect-tests.org instead. + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@TestExpectationValidator(validator = TestExpectationValidator.NullTestResultValidator.class) +public @interface Xpect { +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectCommaSeparatedValues.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectCommaSeparatedValues.java new file mode 100644 index 000000000..106fc4055 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectCommaSeparatedValues.java @@ -0,0 +1,158 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Collection; + +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ActualCollection.ActualItem; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ActualCollection.ToString; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ExpectationCollection.ExpectationItem; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.IParameterProvider.IExpectation; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.TestExpectationValidator.ITestExpectationValidator; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.TestExpectationValidator.TestResult; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.XpectCommaSeparatedValues.CSVResultValidator; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Pair; +import org.junit.Assert; +import org.junit.ComparisonFailure; + +import com.google.common.base.Function; + +/** + * use org.xpect.runner.Xpect from www.xpect-tests.org instead. In Xpect, test + * methods don't have return values anymore. Instead, the test expectation is + * passed in as method parameter. To handle a method with a CommaSeparatedValues + * expectation you can use a method declaration such as + * + * <code> @Xpect public void scope(@CommaSeparatedValuesExpectation ICommaSeparatedValuesExpectation expectation) { } </code> + * + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@TestExpectationValidator(validator = CSVResultValidator.class) +public @interface XpectCommaSeparatedValues { + + public class CSVResultValidator implements + ITestExpectationValidator<Iterable<Object>> { + protected XpectCommaSeparatedValues cfg; + + public CSVResultValidator(XpectCommaSeparatedValues cfg) { + this.cfg = cfg; + } + + protected String str(int length) { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < length; i++) + b.append(" "); + return b.toString(); + } + + public void validate(XtextResource res, IExpectation expectation, + @TestResult Iterable<Object> actual) { + Assert.assertNotNull(res); + Assert.assertNotNull(expectation); + Assert.assertNotNull(expectation.getExpectation()); + Assert.assertNotNull(actual); + + ExpectationCollection exp = new ExpectationCollection(); + exp.setCaseSensitive(cfg.caseSensitive()); + exp.setOrdered(cfg.ordered()); + exp.setQuoted(cfg.quoted()); + exp.setSeparator(','); + exp.setWhitespaceSensitive(cfg.whitespaceSensitive()); + exp.init(expectation.getExpectation()); + + ActualCollection act = new ActualCollection(); + act.setCaseSensitive(cfg.caseSensitive()); + act.setOrdered(cfg.ordered()); + act.setQuoted(cfg.quoted()); + act.setSeparator(','); + act.setWhitespaceSensitive(cfg.whitespaceSensitive()); + act.init(actual, cfg.itemFormatter()); + + if (!exp.matches(act)) { + StringBuilder expString = new StringBuilder(); + StringBuilder actString = new StringBuilder(); + boolean expWrap = false; + boolean expEmpty = false; + boolean actWrap = false; + int lineLength = 0, lineCount = 0; + for (Pair<Collection<ExpectationItem>, ActualItem> pair : exp + .map(act)) { + String expItem = null; + String actItem = null; + if (pair.getFirst() != null && !pair.getFirst().isEmpty()) { + if (pair.getSecond() != null) + expItem = pair.getSecond().getEscaped(); + else + expItem = pair.getFirst().iterator().next() + .getEscaped(); + } else { + if (pair.getSecond() != null) + expItem = str(pair.getSecond().getEscaped() + .length()); + } + if (pair.getSecond() != null) { + actItem = pair.getSecond().getEscaped(); + lineCount++; + lineLength += actItem.length() + 2; + boolean count = cfg.maxItemsPerLine() > 0 + && lineCount > cfg.maxItemsPerLine(); + boolean width = cfg.maxLineWidth() > 0 + && lineLength > cfg.maxLineWidth(); + if (count || width) + expWrap = actWrap = true; + } + if (expItem != null && expString.length() > 0) { + if (expWrap) { + expString.append(expEmpty ? "\n" : ",\n"); + expWrap = false; + } else + expString.append(expEmpty ? " " : ", "); + } + if (actItem != null && actString.length() > 0) { + if (actWrap) { + actString.append(",\n"); + actWrap = false; + lineCount = 0; + lineLength = 0; + } else + actString.append(", "); + } + if (expItem != null) { + expString.append(expItem); + expEmpty = expItem.trim().length() == 0; + } + if (actItem != null) + actString.append(actItem); + } + String expDoc = IExpectation.Util.replace(res, expectation, + expString.toString()); + String actDoc = IExpectation.Util.replace(res, expectation, + actString.toString()); + throw new ComparisonFailure("", expDoc, actDoc); + } + } + } + + boolean caseSensitive() default true; + + Class<? extends Function<Object, String>> itemFormatter() default ToString.class; + + int maxItemsPerLine() default -1; + + int maxLineWidth() default 80; + + boolean ordered() default false; + + boolean quoted() default false; + + boolean whitespaceSensitive() default false; + +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectLines.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectLines.java new file mode 100644 index 000000000..fada91d20 --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectLines.java @@ -0,0 +1,111 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Collection; +import java.util.List; + +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ActualCollection.ActualItem; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ActualCollection.ToString; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ExpectationCollection.ExpectationItem; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.IParameterProvider.IExpectation; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.TestExpectationValidator.ITestExpectationValidator; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.TestExpectationValidator.TestResult; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.XpectLines.LinesResultValidator; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Pair; +import org.junit.Assert; +import org.junit.ComparisonFailure; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +/** + * use org.xpect.runner.Xpect from www.xpect-tests.org instead. In Xpect, test + * methods don't have return values anymore. Instead, the test expectation is + * passed in as method parameter. To handle a method with a Lines expectation + * you can use a method declaration such as + * + * <code>@Xpect public void exportedObjects(@LinesExpectation ILinesExpectation expectation) { }</code> + * + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@TestExpectationValidator(validator = LinesResultValidator.class) +public @interface XpectLines { + + public class LinesResultValidator implements + ITestExpectationValidator<Iterable<Object>> { + + protected XpectLines cfg; + + public LinesResultValidator(XpectLines cfg) { + this.cfg = cfg; + } + + public void validate(XtextResource res, IExpectation expectation, + @TestResult Iterable<Object> actual) { + Assert.assertNotNull(res); + Assert.assertNotNull(expectation); + Assert.assertNotNull(expectation.getExpectation()); + Assert.assertNotNull(actual); + + ExpectationCollection exp = new ExpectationCollection(); + exp.setCaseSensitive(cfg.caseSensitive()); + exp.setOrdered(cfg.ordered()); + exp.setQuoted(cfg.quoted()); + exp.setSeparator('\n'); + exp.setWhitespaceSensitive(cfg.whitespaceSensitive()); + exp.init(expectation.getExpectation()); + + ActualCollection act = new ActualCollection(); + act.setCaseSensitive(cfg.caseSensitive()); + act.setOrdered(cfg.ordered()); + act.setQuoted(cfg.quoted()); + act.setSeparator('\n'); + act.setWhitespaceSensitive(cfg.whitespaceSensitive()); + act.init(actual, cfg.itemFormatter()); + + if (!exp.matches(act)) { + List<String> expString = Lists.newArrayList(); + List<String> actString = Lists.newArrayList(); + for (Pair<Collection<ExpectationItem>, ActualItem> pair : exp + .map(act)) { + if (pair.getFirst() != null && !pair.getFirst().isEmpty()) { + if (pair.getSecond() != null) + expString.add(pair.getSecond().getEscaped()); + else + expString.add(pair.getFirst().iterator().next() + .getEscaped()); + } + if (pair.getSecond() != null) + actString.add(pair.getSecond().getEscaped()); + } + String expDoc = IExpectation.Util.replace(res, expectation, + Joiner.on('\n').join(expString)); + String actDoc = IExpectation.Util.replace(res, expectation, + Joiner.on('\n').join(actString)); + throw new ComparisonFailure("", expDoc, actDoc); + } + + } + } + + boolean caseSensitive() default true; + + boolean whitespaceSensitive() default false; + + boolean quoted() default false; + + Class<? extends Function<Object, String>> itemFormatter() default ToString.class; + + boolean ordered() default false; + +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectParameterProvider.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectParameterProvider.java new file mode 100644 index 000000000..5cb385bdb --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectParameterProvider.java @@ -0,0 +1,541 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.math.BigInteger; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.xtext.nodemodel.ILeafNode; +import org.eclipse.xtext.nodemodel.INode; +import org.eclipse.xtext.parsetree.reconstr.impl.NodeIterator; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Exceptions; +import org.eclipse.xtext.util.Pair; +import org.eclipse.xtext.util.Strings; +import org.eclipse.xtext.util.Wrapper; +import org.eclipse.xtext.util.formallang.FollowerFunctionImpl; +import org.eclipse.xtext.util.formallang.Nfa; +import org.eclipse.xtext.util.formallang.NfaUtil; +import org.eclipse.xtext.util.formallang.NfaUtil.BacktrackHandler; +import org.eclipse.xtext.util.formallang.StringProduction; +import org.eclipse.xtext.util.formallang.StringProduction.ElementType; +import org.eclipse.xtext.util.formallang.StringProduction.ProdElement; +import org.junit.Test; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; + +/** + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@SuppressWarnings("restriction") +public class XpectParameterProvider implements IParameterProvider { + + protected static class AssignedProduction extends StringProduction { + + public AssignedProduction(String production) { + super(production); + } + + @Override + protected ProdElement parsePrim(Stack<Pair<Token, String>> tokens) { + Pair<Token, String> current = tokens.pop(); + switch (current.getFirst()) { + case PL: + ProdElement result1 = parseAlt(tokens); + if (tokens.peek().getFirst().equals(Token.PR)) + tokens.pop(); + else + throw new RuntimeException("')' expected, but " + + tokens.peek().getFirst() + " found"); + parseCardinality(tokens, result1); + return result1; + case STRING: + ProdElement result2 = createElement(ElementType.TOKEN); + result2.setValue(current.getSecond()); + parseCardinality(tokens, result2); + return result2; + case ID: + ProdElement result3 = createElement(ElementType.TOKEN); + result3.setName(current.getSecond()); + Pair<Token, String> eq = tokens.pop(); + if (eq.getFirst() == Token.EQ) { + Pair<Token, String> val = tokens.pop(); + switch (val.getFirst()) { + case ID: + case STRING: + result3.setValue(val.getSecond()); + break; + default: + throw new RuntimeException("Unexpected token " + + current.getFirst()); + } + } else + throw new RuntimeException("Unexpected token " + + eq.getFirst() + ", expected '='"); + parseCardinality(tokens, result3); + return result3; + default: + throw new RuntimeException("Unexpected token " + + current.getFirst()); + } + } + } + + protected static class BacktrackItem { + protected int offset; + protected ProdElement token; + protected String value; + + public BacktrackItem(int offset) { + super(); + this.offset = offset; + } + + public BacktrackItem(int offset, ProdElement token, String value) { + super(); + this.offset = offset; + this.token = token; + this.value = value; + } + + @Override + public String toString() { + return token + ":" + value; + } + } + + protected static class Expectation implements IExpectation { + protected String expectation; + protected String indentation = null; + protected int lenght; + protected int offset; + + public Expectation(int offset, int lenght, String expectation) { + super(); + this.offset = offset; + this.lenght = lenght; + this.expectation = expectation; + } + + public Expectation(int offset, int lenght, String expectation, + String indentation) { + super(); + this.offset = offset; + this.lenght = lenght; + this.expectation = expectation; + this.indentation = indentation; + } + + public String getExpectation() { + return expectation; + } + + public String getIndentation() { + return indentation; + } + + public int getLength() { + return lenght; + } + + public int getOffset() { + return offset; + } + + } + + protected enum Token { + ID("[a-zA-Z][a-zA-Z0-9_]*"), // + INT("[0-9]+"), // + OFFSET("'([^']*)'|[^\\s]+"), // + STRING("'([^']*)'"), // + TEXT("[^\\s]+"); + + public final Pattern pattern; + + private Token(String regex) { + this.pattern = Pattern.compile("^" + regex); + } + } + + public final static String PARAM_OFFSET = "offset"; + public final static String PARAM_RESOURCE = "resource"; + + protected static final Pattern WS = Pattern.compile("^[\\s]+"); + + protected static Pattern XPECT_PATTERN = Pattern + .compile("(\\S)?XPECT(_CLASS|_IMPORT)?\\s+([a-zA-Z0-9]*)"); + + public void collectParameters(Class<?> testClass, XtextResource resource, + IParameterAcceptor acceptor) { + collectTestMethods(testClass, resource, acceptor); + for (ILeafNode leaf : resource.getParseResult().getRootNode() + .getLeafNodes()) + if (leaf.isHidden() && leaf.getText().contains("XPECT")) + parseLeaf(testClass, resource, leaf, acceptor); + + } + + protected void collectTestMethods(Class<?> testClass, XtextResource res, + IParameterAcceptor acceptor) { + for (Method meth : testClass.getMethods()) { + if (Modifier.isPublic(meth.getModifiers()) + && !Modifier.isStatic(meth.getModifiers())) { + Test annotation = meth.getAnnotation(Test.class); + if (annotation != null) + acceptor.acceptTest(null, meth.getName(), + getDefaultParams(res, 0), null, false); + } + } + } + + protected Iterable<Object> convertValue(XtextResource res, INode ctx, + int offset, Token token, String value) { + switch (token) { + case OFFSET: + int add = value.indexOf('|'); + if (add >= 0) + value = value.substring(0, add) + value.substring(add + 1); + else + add = 0; + String text = ctx.getRootNode().getText(); + int result = text.indexOf(value, offset); + if (result >= 0) { + int off = result + add; + return Lists.newArrayList(off, new Offset(res, off)); + } else + throw new RuntimeException("OFFSET '" + value + "' not found"); + case INT: + List<Object> r = Lists.newArrayList(); + try { + r.add(Integer.valueOf(value)); + } catch (NumberFormatException e) { + } + try { + r.add(new BigInteger(value)); + } catch (NumberFormatException e) { + } + r.add(value); + return r; + case ID: + case STRING: + case TEXT: + return Collections.<Object> singleton(value); + } + return Collections.<Object> singleton(value); + } + + protected Multimap<String, Object> getDefaultParams(XtextResource res, + int offset) { + Multimap<String, Object> result = HashMultimap.create(); + result.put(PARAM_RESOURCE, res); + result.put(PARAM_OFFSET, offset); + result.put(PARAM_OFFSET, new Offset(res, offset)); + return result; + } + + protected String getIndentation(INode ctx, int offset) { + String text = ctx.getRootNode().getText(); + int nl = text.lastIndexOf("\n", offset); + if (nl < 0) + nl = 0; + StringBuilder result = new StringBuilder(); + for (int i = nl + 1; i < text.length() + && Character.isWhitespace(text.charAt(i)); i++) + result.append(text.charAt(i)); + return result.toString(); + } + + protected int getOffsetOfNextSemanticNode(INode node) { + Iterator<INode> it = new NodeIterator(node); + while (it.hasNext()) { + INode n = it.next(); + if (n instanceof ILeafNode && !((ILeafNode) n).isHidden()) + return n.getOffset(); + } + return node.getEndOffset(); + } + + protected Nfa<ProdElement> getParameterNfa(String syntax) { + AssignedProduction prod = new AssignedProduction(syntax); + FollowerFunctionImpl<ProdElement, String> ff = new FollowerFunctionImpl<ProdElement, String>( + prod); + ProdElement start = prod.new ProdElement(ElementType.TOKEN); + ProdElement stop = prod.new ProdElement(ElementType.TOKEN); + Nfa<ProdElement> result = new NfaUtil().create(prod, ff, start, stop); + return result; + } + + protected String getParameterSyntax(Class<?> testClass, String methodName) { + try { + Method method = testClass.getMethod(methodName); + ParameterSyntax annotation = method + .getAnnotation(ParameterSyntax.class); + if (annotation != null) + return annotation.value(); + } catch (SecurityException e) { + Exceptions.throwUncheckedException(e); + } catch (NoSuchMethodException e) { + Exceptions.throwUncheckedException(e); + } + return null; + } + + protected void parseLeaf(Class<?> testClass, XtextResource resource, + ILeafNode leaf, IParameterAcceptor acceptor) { + String text = leaf.getText(); + Matcher matcher = XPECT_PATTERN.matcher(text); + int offset = 0; + while (offset < text.length() && matcher.find(offset)) { + if (matcher.group(2) == null) { + int newOffset; + if ((newOffset = parseXpect(testClass, resource, leaf, text, + matcher.group(3), matcher.end(), acceptor, + matcher.group(1) != null)) >= 0) + offset = newOffset; + else + offset = matcher.end(); + } else if ("_IMPORT".equals(matcher.group(2))) { + offset = parseXpectImport(resource, text, matcher.end(2), + acceptor); + } + // } else { + // int newOffset; + // if ((newOffset = parseXpectTest(testClass, text, matcher.end(), + // acceptor)) >= 0) + // offset = newOffset; + // else + // offset = matcher.end(); + // } + } + } + + protected int parseString(String text, int offset, Wrapper<String> value) { + if (offset < text.length() && text.charAt(offset) == '"') { + int i = offset + 1; + while (offset < text.length() && text.charAt(i - 1) == '\\' + || text.charAt(i) != '"') + i++; + if (text.charAt(i) == '"') { + value.set(text.substring(offset + 1, i - 1)); + return i; + } + } + return -1; + } + + protected int parseStringOrText(String text, int offset, + Wrapper<String> value) { + int newOffset = parseString(text, offset, value); + if (newOffset < 0) + newOffset = parseText(text, offset, value); + return newOffset; + } + + protected int parseText(String text, int offset, Wrapper<String> value) { + int i = offset; + while (i < text.length()) + switch (text.charAt(i)) { + case ' ': + case '\t': + case '\r': + case '\n': + value.set(text.substring(offset, i)); + return i; + default: + i++; + } + value.set(text.substring(offset, i)); + return i; + } + + protected int parseXpect(Class<?> testClass, XtextResource res, INode ctx, + String text, String method, int offset, + IParameterAcceptor acceptor, boolean ignore) { + int newOffset; + Multimap<String, Object> params = HashMultimap.create(); + Wrapper<Expectation> expectation = new Wrapper<Expectation>(null); + offset = skipWhitespace(text, offset); + if ((newOffset = parseXpectParams(testClass, res, ctx, method, text, + offset, params)) >= 0) + offset = newOffset; + offset = skipWhitespace(text, offset); + if ((newOffset = parseXpectSLExpectation(ctx, text, offset, expectation)) >= 0) + offset = newOffset; + else if ((newOffset = parseXpectMLExpectation(ctx, text, offset, + expectation)) >= 0) + offset = newOffset; + acceptor.acceptTest(null, method, params, expectation.get(), ignore); + return offset; + } + + protected int parseXpectImport(XtextResource res, String text, int offset, + IParameterAcceptor acceptor) { + offset = skipWhitespace(text, offset); + int end = text.indexOf("\n", offset); + String fileName = text.substring(offset, end).trim(); + URI uri = URI.createURI(fileName); + if (uri.isRelative() && !res.getURI().isRelative()) + uri = uri.resolve(res.getURI()); + acceptor.acceptImportURI(uri); + return end; + } + + protected int parseXpectMLExpectation(INode node, String text, int offset, + Wrapper<Expectation> expectation) { + if (offset + 3 < text.length() + && text.substring(offset, offset + 3).equals("---")) { + String indentation = getIndentation(node, node.getOffset() + offset); + int start = text.indexOf('\n', offset + 3); + int end = text.indexOf("---", offset + 3); + if (start >= 0 && end >= 0) { + String substring = text.substring(start + 1, end); + end = substring.lastIndexOf('\n'); + if (end >= 0) { + String exp = substring.substring(0, end); + int len = exp.length(); + if (exp.startsWith(indentation)) + exp = exp.substring(indentation.length()); + exp = exp.replace("\n" + indentation, "\n"); + expectation.set(new Expectation(node.getOffset() + start + + 1, len, exp, indentation)); + return end + start + 1; + } + } + } + return -1; + } + + protected int parseXpectParams(Class<?> testClass, XtextResource res, + INode node, String methodName, final String text, int offset, + Multimap<String, Object> params) { + int semanticOffset = getOffsetOfNextSemanticNode(node); + params.putAll(getDefaultParams(res, semanticOffset)); + String paramSyntax = getParameterSyntax(testClass, methodName); + if (Strings.isEmpty(paramSyntax)) + return -1; + Nfa<ProdElement> nfa = getParameterNfa(paramSyntax); + List<BacktrackItem> trace = new NfaUtil().backtrack(nfa, + new BacktrackItem(offset), + new BacktrackHandler<ProdElement, BacktrackItem>() { + public BacktrackItem handle(ProdElement state, + BacktrackItem previous) { + if (Strings.isEmpty(state.getValue())) + return previous; + if (Strings.isEmpty(state.getName())) { + if (text.regionMatches(previous.offset, state + .getValue(), 0, state.getValue().length())) { + int newOffset = previous.offset + + state.getValue().length(); + Matcher ws = WS.matcher(text).region(newOffset, + text.length()); + int childOffset = ws.find() ? ws.end() + : newOffset; + return new BacktrackItem(childOffset, state, + state.getValue()); + } + } else { + Token t = Token.valueOf(state.getValue()); + Matcher matcher = t.pattern.matcher(text).region( + previous.offset, text.length()); + if (matcher.find()) { + Matcher ws = WS.matcher(text).region( + matcher.end(), text.length()); + int childOffset = ws.find() ? ws.end() + : matcher.end(); + String value = matcher.groupCount() > 0 + && matcher.group(1) != null ? matcher + .group(1) : matcher.group(0); + return new BacktrackItem(childOffset, state, + value); + } + } + return null; + } + + public boolean isSolution(BacktrackItem result) { + return true; + } + + public Iterable<ProdElement> sortFollowers( + BacktrackItem result, + Iterable<ProdElement> followers) { + return followers; + } + }); + if (trace != null && !trace.isEmpty()) { + for (BacktrackItem item : trace) + if (item.token != null && item.token.getName() != null) { + String key = item.token.getName(); + params.removeAll(key); + params.putAll( + key, + convertValue(res, node, semanticOffset, + Token.valueOf(item.token.getValue()), + item.value)); + } + return trace.get(trace.size() - 1).offset; + } + return -1; + } + + protected int parseXpectSLExpectation(INode node, String text, int offset, + Wrapper<Expectation> expectation) { + if (offset + 3 < text.length() + && text.substring(offset, offset + 3).equals("-->")) { + int begin = offset + 3; + if (text.charAt(begin) == '\r' || text.charAt(begin) == '\n') { + expectation + .set(new Expectation(node.getOffset() + begin, 0, "")); + return begin; + } else if (Character.isWhitespace(text.charAt(begin))) + begin++; + int end = text.indexOf('\n', begin); + if (end < 0) + end = text.length(); + String exp = text.substring(begin, end); + expectation.set(new Expectation(node.getOffset() + begin, exp + .length(), exp)); + return end; + } + return -1; + } + + // protected int parseXpectTest(Wrapper<Class<?>> test, String text, int + // offset, IParameterAcceptor acceptor) { + // int index = text.indexOf("\n", offset); + // if (index > offset) { + // String name = text.substring(offset, index).trim(); + // try { + // Class<?> clazz = Class.forName(name); + // acceptor.acceptTestClass(clazz); + // } catch (ClassNotFoundException e) { + // Exceptions.throwUncheckedException(e); + // } + // return index; + // } + // return -1; + // } + + protected int skipWhitespace(String text, int offset) { + int i = offset; + while (i < text.length()) + if (Character.isWhitespace(text.charAt(i))) + i++; + else + return i; + return i; + } + +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectString.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectString.java new file mode 100644 index 000000000..c3350a74a --- /dev/null +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/legacy_xpect_runner/XpectString.java @@ -0,0 +1,73 @@ +package org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.IParameterProvider.IExpectation; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.TestExpectationValidator.ITestExpectationValidator; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.TestExpectationValidator.TestResult; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.XpectString.StringResultValidator; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.internal.FormattingMigrator; +import org.junit.Assert; +import org.junit.ComparisonFailure; + +/** + * use org.xpect.runner.Xpect from www.xpect-tests.org instead. In Xpect, test + * methods don't have return values anymore. Instead, the test expectation is + * passed in as method parameter. To handle a method with a String expectation + * you can use a method declaration such as + * + * <code>@Xpect public void linkedName(@StringExpectation IStringExpectation expectation) { }</code> + * + * This class will be removed in the next release after 2.4.2 + * + * @author Moritz Eysholdt - Initial contribution and API + */ +@Deprecated +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@SuppressWarnings("restriction") +@TestExpectationValidator(validator = StringResultValidator.class) +public @interface XpectString { + + public class StringResultValidator implements + ITestExpectationValidator<String> { + + protected XpectString config; + + public StringResultValidator(XpectString config) { + this.config = config; + } + + public void validate(XtextResource resource, IExpectation expectation, + @TestResult String actual) { + Assert.assertNotNull(resource); + Assert.assertNotNull(expectation); + Assert.assertNotNull(expectation.getExpectation()); + Assert.assertNotNull(actual); + String exp; + if (!config.whitespaceSensitive()) { + FormattingMigrator migrator = new FormattingMigrator(); + exp = migrator.migrate(actual, expectation.getExpectation()); + } else + exp = expectation.getExpectation(); + + if ((config.caseSensitive() && !exp.equals(actual)) + || (!config.caseSensitive() && !exp + .equalsIgnoreCase(actual))) { + String expDoc = IExpectation.Util.replace(resource, + expectation, exp); + String actDoc = IExpectation.Util.replace(resource, + expectation, actual); + throw new ComparisonFailure("", expDoc, actDoc); + } + } + } + + boolean caseSensitive() default true; + + boolean whitespaceSensitive() default false; +} diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/scoping/XcoreScopingTest.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/scoping/XcoreScopingTest.java index 8e5c2c71d..b2b88e7ee 100644 --- a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/scoping/XcoreScopingTest.java +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/scoping/XcoreScopingTest.java @@ -14,13 +14,13 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.xcore.XcoreInjectorProvider; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.InjectParameter; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.Offset; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ParameterSyntax; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ParameterizedXtextRunner; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ResourceURIs; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.XpectCommaSeparatedValues; import org.eclipse.xtext.junit4.InjectWith; -import org.eclipse.xtext.junit4.parameterized.InjectParameter; -import org.eclipse.xtext.junit4.parameterized.Offset; -import org.eclipse.xtext.junit4.parameterized.ParameterSyntax; -import org.eclipse.xtext.junit4.parameterized.ParameterizedXtextRunner; -import org.eclipse.xtext.junit4.parameterized.ResourceURIs; -import org.eclipse.xtext.junit4.parameterized.XpectCommaSeparatedValues; import org.eclipse.xtext.junit4.validation.ValidationTestHelper; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.XtextResource; diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/validation/XcoreParameterizedTestRunner.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/validation/XcoreParameterizedTestRunner.java index 0ca0512dc..707a6876d 100644 --- a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/validation/XcoreParameterizedTestRunner.java +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/validation/XcoreParameterizedTestRunner.java @@ -12,8 +12,8 @@ import java.util.List; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ParameterizedXtextRunner; import org.eclipse.xtext.junit4.IInjectorProvider; -import org.eclipse.xtext.junit4.parameterized.ParameterizedXtextRunner; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.resource.XtextResourceFactory; import org.junit.runners.model.InitializationError; @@ -23,7 +23,6 @@ import com.google.common.collect.Lists; import com.google.inject.Injector; -@SuppressWarnings("restriction") public class XcoreParameterizedTestRunner extends ParameterizedXtextRunner { diff --git a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/validation/XcoreValidationTest.java b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/validation/XcoreValidationTest.java index 88672fd77..4ef1c0b59 100644 --- a/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/validation/XcoreValidationTest.java +++ b/tests/org.eclipse.emf.test.ecore.xcore/src/org/eclipse/emf/test/ecore/xcore/validation/XcoreValidationTest.java @@ -12,10 +12,10 @@ import java.util.List; import org.eclipse.emf.ecore.util.EcoreValidator; import org.eclipse.emf.ecore.xcore.XcoreInjectorProvider; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.InjectParameter; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.ResourceURIs; +import org.eclipse.emf.test.ecore.xcore.legacy_xpect_runner.XpectLines; import org.eclipse.xtext.junit4.InjectWith; -import org.eclipse.xtext.junit4.parameterized.InjectParameter; -import org.eclipse.xtext.junit4.parameterized.ResourceURIs; -import org.eclipse.xtext.junit4.parameterized.XpectLines; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.validation.CheckMode; |