diff --git a/plugins/org.eclipse.xtext.junit4/META-INF/MANIFEST.MF b/plugins/org.eclipse.xtext.junit4/META-INF/MANIFEST.MF index 76e7031d9..d7c1b001b 100644 --- a/plugins/org.eclipse.xtext.junit4/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.xtext.junit4/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 2.1.0.qualifier Bundle-Vendor: Eclipse Modeling Project Bundle-RequiredExecutionEnvironment: J2SE-1.5 Export-Package: org.eclipse.xtext.junit4, - org.eclipse.xtext.junit4.parameterized;x-internal:=true, + org.eclipse.xtext.junit4.parameterized;x-friends:="org.eclipse.xtext.junit4.tests", org.eclipse.xtext.junit4.serializer, org.eclipse.xtext.junit4.smoketest, org.eclipse.xtext.junit4.util, diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/AbstractIterableExpectationValidator.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/AbstractIterableExpectationValidator.java deleted file mode 100644 index ff31f341f..000000000 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/AbstractIterableExpectationValidator.java +++ /dev/null @@ -1,186 +0,0 @@ -package org.eclipse.xtext.junit4.parameterized; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.regex.Pattern; - -import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.ITestExpectationValidator; -import org.eclipse.xtext.util.Exceptions; - -import com.google.common.base.Function; -import com.google.common.collect.HashMultiset; -import com.google.common.collect.LinkedHashMultiset; -import com.google.common.collect.Lists; -import com.google.common.collect.Multiset; - -public abstract class AbstractIterableExpectationValidator implements ITestExpectationValidator> { - protected class Item { - protected String escaped; - protected String normalized; - - public Item(String normalized, String escaped) { - super(); - this.normalized = normalized; - this.escaped = escaped; - } - - @Override - public boolean equals(Object obj) { - return normalized.equals(obj); - } - - @Override - public int hashCode() { - return normalized.hashCode(); - } - - @Override - public String toString() { - return normalized; - } - - } - - public static class ToString implements Function { - public String apply(Object from) { - return from == null ? "null" : from.toString(); - } - } - - protected static final Pattern WS = Pattern.compile("\\s*", Pattern.MULTILINE); - protected boolean caseSensitive; - protected boolean ordered; - - protected boolean whitespaceSensitive; - - public AbstractIterableExpectationValidator(boolean caseSensitive, boolean whitespaceSensitive) { - super(); - this.caseSensitive = caseSensitive; - this.whitespaceSensitive = whitespaceSensitive; - } - - protected Item createItem(String normalized, String escaped) { - return new Item(normalized, escaped); - } - - protected List escape(List items) { - List result = Lists.newArrayList(); - for (String item : items) - result.add(createItem(normalize(item), escape(item))); - return result; - } - - protected String escape(String str) { - if (whitespaceSensitive) - return escapeQuoted(str); - return escapeUnquoted(str); - } - - protected String escapeQuoted(String str) { - return '"' + str.replaceAll("\\\"", "\\\\\"").replaceAll("\\\\", "\\\\").replaceAll("\\n", "\\\\n") - .replaceAll("\\r", "\\\\r") + '"'; - } - - protected String escapeUnquoted(String str) { - return str.replaceAll("\\s+", " "); - } - - protected Collection normalize(List items) { - Collection result = ordered ? Lists. newArrayList() : HashMultiset. create(); - for (String item : items) - result.add(normalize(item)); - return result; - } - - protected String normalize(String item) { - if (!caseSensitive) - item = item.trim(); - if (!whitespaceSensitive) - item = WS.matcher(item).replaceAll("").toLowerCase(); - return item; - } - - protected List sort(List ordered, List items) { - List result = Lists.newArrayList(); - Multiset unsorted = LinkedHashMultiset.create(items); - for (Item i : ordered) { - if (unsorted.contains(i)) { - result.add(createItem(i.normalized, i.escaped)); - unsorted.remove(i); - } - } - for (Item i : unsorted) - result.add(createItem(i.normalized, i.escaped)); - return result; - } - - protected List toStringList(Iterable actual, Class> functionClass) { - List result = Lists.newArrayList(); - try { - Function func = functionClass.newInstance(); - for (Object obj : actual) - result.add(func.apply(obj)); - return result; - } catch (InstantiationException e) { - Exceptions.throwUncheckedException(e); - } catch (IllegalAccessException e) { - Exceptions.throwUncheckedException(e); - } - return Collections.emptyList(); - } - - protected List toStringList(String items, char separator) { - List result = Lists.newArrayList(); - boolean esc = false, quoted = false; - StringBuilder item = new StringBuilder(); - StringBuilder ws = new StringBuilder(); - for (int i = 0; i < items.length(); i++) { - char c = items.charAt(i); - if (!esc) { - if (!quoted) { - if (c == separator) { - result.add(item.toString()); - item = new StringBuilder(); - ws = new StringBuilder(); - continue; - } else if (Character.isWhitespace(c)) { - ws.append(c); - continue; - } - } - if (whitespaceSensitive && c == '"') { - if (quoted) - item.append(ws); - ws = new StringBuilder(); - quoted = !quoted; - continue; - } else if (c == '\\') { - 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); - else - ws = new StringBuilder(); - } - item.append(c); - } - if (item.length() > 0) - result.add(item.toString()); - return result; - } - -} \ No newline at end of file diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/AbstractParameterizedXtextTest.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/AbstractParameterizedXtextTest.java deleted file mode 100644 index d97359a1a..000000000 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/AbstractParameterizedXtextTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - *******************************************************************************/ -package org.eclipse.xtext.junit4.parameterized; - -import static org.junit.Assert.*; - -import java.util.List; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.EStructuralFeature; -import org.eclipse.xtext.AbstractElement; -import org.eclipse.xtext.Assignment; -import org.eclipse.xtext.GrammarUtil; -import org.eclipse.xtext.diagnostics.Severity; -import org.eclipse.xtext.junit4.serializer.SerializerTester; -import org.eclipse.xtext.junit4.validation.ValidationTestHelper; -import org.eclipse.xtext.nodemodel.ILeafNode; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; -import org.eclipse.xtext.resource.IEObjectDescription; -import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.scoping.IScope; -import org.eclipse.xtext.scoping.IScopeProvider; -import org.eclipse.xtext.util.CancelIndicator; -import org.eclipse.xtext.util.Pair; -import org.eclipse.xtext.util.Tuples; -import org.eclipse.xtext.validation.CheckMode; -import org.eclipse.xtext.validation.IResourceValidator; -import org.eclipse.xtext.validation.Issue; -import org.junit.Test; -import org.junit.runner.RunWith; - -import com.google.common.collect.Lists; -import com.google.inject.Inject; - -/** - * @author Moritz Eysholdt - Initial contribution and API - */ -@RunWith(ParameterizedXtextRunner.class) -public class AbstractParameterizedXtextTest { - - protected final static int LINE_WIDTH = 80; - - protected int offset; - - protected XtextResource resource; - - @Inject - protected IScopeProvider scopeProvider; - - @Inject - protected SerializerTester serializerTester; - - @Inject - protected ValidationTestHelper validationHelper; - - public AbstractParameterizedXtextTest(XtextResource resource, int offset, String[] params) { - super(); - this.resource = resource; - this.offset = offset; - } - - protected String formatIssue(Issue issue) { - StringBuilder result = new StringBuilder(); - result.append(issue.getSeverity().name().toLowerCase()); - if (issue.getOffset() != null && issue.getLength() != null) { - result.append(" at '"); - result.append(resource.getParseResult().getRootNode().getText() - .substring(issue.getOffset(), issue.getOffset() + issue.getLength())); - result.append("' "); - } else - result.append(" "); - result.append("message '"); - result.append(issue.getMessage()); - result.append("'"); - return result.toString(); - } - - protected EObject getEObjectAtOffset() { - EObject object = NodeModelUtils.findActualSemanticObjectFor(getLeafNodeAtOffset()); - assertNotNull("No EObject found at offset " + offset, object); - return object; - } - - protected Pair getEStructuralFeatureAtOffset() { - INode leaf = getLeafNodeAtOffset(); - assertTrue(leaf.getGrammarElement() instanceof AbstractElement); - EObject object = NodeModelUtils.findActualSemanticObjectFor(leaf); - 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()); - } - assertNotNull("No Assignment found at offset " + offset, ass); - @SuppressWarnings("null") - EStructuralFeature feature = object.eClass().getEStructuralFeature(ass.getFeature()); - return Tuples.create(object, feature); - } - - protected ILeafNode getLeafNodeAtOffset() { - ILeafNode node = NodeModelUtils.findLeafNodeAtOffset(resource.getParseResult().getRootNode(), offset); - assertNotNull("No Leaf Node found at offset " + offset, node); - return node; - } - - @Test - public void noValidationErrors() { - validationHelper.assertNoErrors(resource.getContents().get(0)); - } - - @Test - public void noValidationIssues() { - validationHelper.assertNoErrors(resource.getContents().get(0)); - } - - @TestAsCommaSeparatedValues() - public List scopeAllElements() { - Pair objAndFeature = getEStructuralFeatureAtOffset(); - assertTrue(objAndFeature.getSecond() instanceof EReference); - assertFalse(((EReference) objAndFeature.getSecond()).isContainment()); - IScope scope = scopeProvider.getScope(objAndFeature.getFirst(), (EReference) objAndFeature.getSecond()); - List result = Lists.newArrayList(); - for (IEObjectDescription desc : scope.getAllElements()) - result.add(desc.getName().toString()); - return result; - } - - @Test - public void serializeable() throws Exception { - EObject obj = getEObjectAtOffset(); - serializerTester.assertSerializeWithNodeModel(obj); - serializerTester.assertSerializeWithoutNodeModel(obj); - } - - @Test - public void serializeableWithNodeModel() throws Exception { - serializerTester.assertSerializeWithNodeModel(getEObjectAtOffset()); - } - - @Test - public void serializeableWithoutNodeModel() throws Exception { - serializerTester.assertSerializeWithoutNodeModel(getEObjectAtOffset()); - } - - protected List validateResource() { - IResourceValidator validator = resource.getResourceServiceProvider().get(IResourceValidator.class); - List issues = validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl); - return issues; - } - - @TestAsLines() - public List validationErrors() { - List result = Lists.newArrayList(); - for (Issue issue : validateResource()) - if (issue.getSeverity() == Severity.ERROR) - result.add(formatIssue(issue)); - return result; - } - - @TestAsLines() - public List validationIssues() { - List result = Lists.newArrayList(); - for (Issue issue : validateResource()) - result.add(formatIssue(issue)); - return result; - } - -} diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ActualCollection.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ActualCollection.java new file mode 100644 index 000000000..0ea177a7b --- /dev/null +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ActualCollection.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.junit4.parameterized; + +import org.eclipse.xtext.junit4.parameterized.ActualCollection.ActualItem; +import org.eclipse.xtext.util.Exceptions; + +import com.google.common.base.Function; + +/** + * @author Moritz Eysholdt - Initial contribution and API + */ +public class ActualCollection extends StringCollection { + + public static class ToString implements Function { + public String apply(Object from) { + return from == null ? "null" : from.toString(); + } + } + + public class ActualItem extends StringCollection.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 actual, Class> functionClass) { + items = createCollection(); + try { + Function 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/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ExpectationCollection.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ExpectationCollection.java new file mode 100644 index 000000000..240bc8d7d --- /dev/null +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ExpectationCollection.java @@ -0,0 +1,324 @@ +/******************************************************************************* + * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.junit4.parameterized; + +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.xtext.junit4.parameterized.ActualCollection.ActualItem; +import org.eclipse.xtext.junit4.parameterized.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; + +/** + * @author Moritz Eysholdt - Initial contribution and API + */ +public class ExpectationCollection extends StringCollection { + public class ExpectationItem extends StringCollection.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, ActualItem>> map(ActualCollection actual) { + if (!isOrdered() || !actual.isOrdered()) + return mapUnordered(actual); + else + return mapOrdered(actual); + } + + protected List, ActualItem>> mapUnordered(ActualCollection actual) { + Map> remainingExpectations = Maps.newHashMap(); + Set negatedExpectations = Sets.newLinkedHashSet(); + List wildcardExpectations = Lists.newArrayList(); + for (ExpectationItem exp : this) + if (exp.isWildcard()) + wildcardExpectations.add(exp); + else if (exp.isNegated()) + negatedExpectations.add(exp); + else { + List items = remainingExpectations.get(exp); + if (items == null) + remainingExpectations.put(exp, items = Lists.newArrayList()); + items.add(exp); + } + List sortedActual = Lists.newArrayList(actual); + Collections.sort(sortedActual); + List, ActualItem>> result = Lists.newArrayList(); + for (ActualItem act : sortedActual) { + List exp = remainingExpectations.get(act); + if (exp != null && !exp.isEmpty()) { + ExpectationItem ei = exp.get(exp.size() - 1); + exp.remove(exp.size() - 1); + result.add(Tuples., ActualItem> create(Collections.singleton(ei), act)); + } else if (!negatedExpectations.contains(act)) { + if (wildcardExpectations.isEmpty()) + result.add(Tuples., ActualItem> create(negatedExpectations, act)); + else + result.add(Tuples., ActualItem> create(wildcardExpectations, act)); + } else + result.add(Tuples., ActualItem> create(null, act)); + } + List remainingExpectationsSorted = Lists.newArrayList(); + for (List x : remainingExpectations.values()) + remainingExpectationsSorted.addAll(x); + Collections.sort(remainingExpectationsSorted); + for (ExpectationItem ei : remainingExpectationsSorted) + if (ei.isPure()) + result.add(Tuples., ActualItem> create(Collections.singleton(ei), null)); + return result; + } + + protected List, ActualItem>> mapOrdered(ActualCollection actual) { + List, ActualItem>> r = Lists.newArrayList(); + List exIt = Lists.newArrayList(this.iterator()); + List 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 expectedNegated = Sets.newLinkedHashSet(); + Set 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., 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., ActualItem> create(expectedNegated, + actIt.get(act))); + else + r.add(Tuples., ActualItem> create(expectedWildcard, + actIt.get(act))); + } else + r.add(Tuples., ActualItem> create(null, actIt.get(act))); + act++; + } + } else if (exIt.get(exp).equals(actIt.get(act))) { + r.add(Tuples., 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., ActualItem> create(null, actIt.get(act))); + act++; + } + r.add(Tuples., ActualItem> create(Collections.singleton(exIt.get(exp)), + actIt.get(lact))); + exp++; + act++; + } else { + r.add(Tuples., ActualItem> create(Collections.singleton(exIt.get(exp)), + null)); + exp++; + } + } + } + while (act < actIt.size()) { + r.add(Tuples., ActualItem> create(null, actIt.get(act))); + act++; + } + while (exp < exIt.size()) { + if (exIt.get(exp).isPure()) + r.add(Tuples., ActualItem> create(Collections.singleton(exIt.get(exp)), + null)); + exp++; + } + return r; + } + + protected boolean matchesOrdered(ActualCollection actual) { + if (isPure()) + return items.equals(actual.items); + Iterator exIt = this.iterator(); + Iterator actIt = actual.iterator(); + while (exIt.hasNext() && actIt.hasNext()) { + ExpectationItem exp = exIt.next(); + ActualItem act = actIt.next(); + if (exp.isNegated() || exp.isWildcard()) { + List 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 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 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/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/IParameterProvider.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/IParameterProvider.java index 6428165b8..91a0e64c6 100644 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/IParameterProvider.java +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/IParameterProvider.java @@ -9,6 +9,7 @@ package org.eclipse.xtext.junit4.parameterized; import org.eclipse.emf.common.util.URI; import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Strings; import com.google.inject.ImplementedBy; @@ -18,14 +19,38 @@ import com.google.inject.ImplementedBy; @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, Object[] params, String expectation, boolean ignore); + void acceptTest(String title, String method, Object[][] params, IExpectation expectation, boolean ignore); - void acceptTestClass(Class clazz); + // void acceptTestClass(Class clazz); } - void collectParameters(XtextResource resource, IParameterAcceptor acceptor); + void collectParameters(Class testClass, XtextResource resource, IParameterAcceptor acceptor); } diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/Parameter.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/Parameter.java new file mode 100644 index 000000000..b529b91ab --- /dev/null +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/Parameter.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.junit4.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Moritz Eysholdt - Initial contribution and API + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Parameter { + String syntax() default ""; + +} diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunner.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunner.java index 41ceceae9..bacfc8ab8 100644 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunner.java +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunner.java @@ -24,6 +24,7 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.xtext.junit4.IInjectorProvider; import org.eclipse.xtext.junit4.IRegistryConfigurator; import org.eclipse.xtext.junit4.InjectWith; +import org.eclipse.xtext.junit4.parameterized.IParameterProvider.IExpectation; import org.eclipse.xtext.junit4.parameterized.IParameterProvider.IParameterAcceptor; import org.eclipse.xtext.junit4.parameterized.ParameterizedXtextRunner.ResourceRunner; import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.ITestExpectationValidator; @@ -75,7 +76,9 @@ public class ParameterizedXtextRunner extends ParentRunner { Annotation annotation) throws Throwable { Class> validatorClass = trv.validator(); Class expectedResultType = getExpectedResultType(validatorClass); - if (!expectedResultType.isAssignableFrom(method.getReturnType())) + 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> c = validatorClass.getConstructor(annotation @@ -93,7 +96,12 @@ public class ParameterizedXtextRunner extends ParentRunner { if (trv != null) return (ITestExpectationValidator) createValidator(trv, an); } - throw new RuntimeException("Annotation missing: @Test or @TestString or @TestIterable, etc. in: " + method); + Class[] annotations = { Test.class, Xpect.class, XpectString.class, XpectLines.class, + XpectCommaSeparatedValues.class }; + List 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> clazz) { @@ -119,11 +127,11 @@ public class ParameterizedXtextRunner extends ParentRunner { protected static class ParameterSetRunner { protected Description description; - protected String expectation; + protected IExpectation expectation; protected boolean ignore; protected int index = -1; protected String methodName; - protected Object[] params; + protected Object[][] params; protected ResourceRunner runner; protected String title; @@ -133,7 +141,7 @@ public class ParameterizedXtextRunner extends ParentRunner { return description; } - public String getExpectation() { + public IExpectation getExpectation() { return expectation; } @@ -161,16 +169,20 @@ public class ParameterizedXtextRunner extends ParentRunner { return methodName; } - public Object[] getParams() { + public 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, Object[] params, String expectation, - boolean ignore) { + public void init(ResourceRunner runner, String title, String method, Object[][] params, + IExpectation expectation, boolean ignore) { this.runner = runner; this.title = title; this.methodName = method; @@ -197,7 +209,7 @@ public class ParameterizedXtextRunner extends ParentRunner { resourceSet.getResource(uri, true); } - public void acceptTest(String title, String method, Object[] params, String expectation, boolean ignore) { + public void acceptTest(String title, String method, Object[][] params, IExpectation expectation, boolean ignore) { ParameterSetRunner runner = injectorProvider.getInjector().getInstance(ParameterSetRunner.class); runner.init(this, title, method, params, expectation, ignore); parameterSets.add(runner); @@ -209,7 +221,7 @@ public class ParameterizedXtextRunner extends ParentRunner { protected void collectParameters() { IParameterProvider parameterProvider = injectorProvider.getInjector().getInstance(IParameterProvider.class); - parameterProvider.collectParameters(resource, this); + parameterProvider.collectParameters(clazz.getJavaClass(), resource, this); } public Description getDescription() { @@ -253,6 +265,7 @@ public class ParameterizedXtextRunner extends ParentRunner { r.index = count; } } + } private static Map, IInjectorProvider> injectorProviderClassCache = Maps.newHashMap(); @@ -291,22 +304,6 @@ public class ParameterizedXtextRunner extends ParentRunner { this.filter = filter; } - protected Constructor findConstructor(Object[] params) { - 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; - } - List types = Lists.newArrayList(); - for (Object p : params) - types.add(p == null ? "?" : p.getClass().getName()); - throw new RuntimeException("No valid constructor found in class " + getTestClass().getJavaClass().getName() - + " for types " + Joiner.on(", ").join(types)); - } - protected MethodWithExpectation findTestMethod(String name) throws Throwable { MethodWithExpectation result = methods.get(name); if (result == null) { @@ -370,24 +367,51 @@ public class ParameterizedXtextRunner extends ParentRunner { } } - protected List getURIs(ResourceURIs resourceURIs) { - Assert.assertFalse("@ResourceURIs needs a baseURI", Strings.isEmpty(resourceURIs.baseDir())); - Assert.assertTrue("@ResourceURIs needs at least one fileExtension", resourceURIs.fileExtensions().length > 0); + protected List getURIs(ResourceURIs uris) { + List result = Lists.newArrayList(); ResourceURICollector collector = new ResourceURICollector(); - return collector.collectFiles(resourceURIs.baseDir(), resourceURIs.fileExtensions()); + 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(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 alternatives = Lists.newArrayList(); + for (Object[] params : allParams) { + List 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()); - Constructor constructor = findConstructor(ps.getParams()); - Object test = constructor.newInstance(ps.getParams()); + Object test = newTestInstance(ps.getParams()); if (ps.getInjectorProvider() instanceof IRegistryConfigurator) ((IRegistryConfigurator) ps.getInjectorProvider()).setupRegistry(); try { ps.getInjectorProvider().getInjector().injectMembers(test); Object result = method.getMethod().invoke(test); - method.getValidator().validate(ps.getExpectation(), result); + method.getValidator().validate(ps.getResource(), ps.getExpectation(), result); } catch (InvocationTargetException e) { throw e.getCause(); } finally { diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ResourceURICollector.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ResourceURICollector.java index e640b1eee..e1c04089f 100644 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ResourceURICollector.java +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ResourceURICollector.java @@ -56,6 +56,17 @@ public class ResourceURICollector { return collectFiles(directory, new FilePredicate(fileExtensions)); } + public List collectFiles(String... files) { + List 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/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ResourceURIs.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ResourceURIs.java index abbabb82d..c434b5c94 100644 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ResourceURIs.java +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/ResourceURIs.java @@ -13,4 +13,6 @@ public @interface ResourceURIs { String baseDir() default ""; String[] fileExtensions() default {}; + + String[] files() default {}; } diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/StringCollection.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/StringCollection.java new file mode 100644 index 000000000..826e6cff1 --- /dev/null +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/StringCollection.java @@ -0,0 +1,159 @@ +package org.eclipse.xtext.junit4.parameterized; + +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; + +/** + * @author Moritz Eysholdt - Initial contribution and API + */ +public class StringCollection.Item> implements Iterable { + public class Item implements Comparable { + 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 items; + protected boolean ordered = true; + protected char quote = '\"'; + protected boolean quoted = true; + protected char separator = ','; + protected boolean whitespaceSensitive = true; + + protected Collection createCollection() { + return ordered ? Lists. newArrayList() : HashMultiset. 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 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/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestAsCommaSeparatedValues.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestAsCommaSeparatedValues.java deleted file mode 100644 index 2a346980b..000000000 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestAsCommaSeparatedValues.java +++ /dev/null @@ -1,187 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - *******************************************************************************/ -package org.eclipse.xtext.junit4.parameterized; - -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 java.util.regex.Pattern; - -import org.eclipse.xtext.junit4.parameterized.AbstractIterableExpectationValidator.ToString; -import org.eclipse.xtext.junit4.parameterized.TestAsCommaSeparatedValues.CSVResultValidator; -import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.TestResult; -import org.junit.ComparisonFailure; - -import com.google.common.base.Function; - -/** - * @author Moritz Eysholdt - Initial contribution and API - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@TestExpectationValidator(validator = CSVResultValidator.class) -public @interface TestAsCommaSeparatedValues { - - public class CSVResultValidator extends AbstractIterableExpectationValidator { - protected class ItemWithPred extends Item { - public ItemWithPred(String normalized, String escaped) { - super(normalized, escaped); - } - - protected ItemWithPred predecessor; - protected boolean suppressWrapBefore = false; - protected boolean wrapBefore = false; - - public int charsInLine() { - if (wrapBefore || predecessor == null) - return escaped.length(); - return predecessor.charsInLine() + 2 + escaped.length(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof Item) - return normalized.equals(((Item) obj).normalized); - return false; - } - - @Override - public int hashCode() { - return normalized.hashCode(); - } - - public int itemsInLine() { - if (wrapBefore || predecessor == null) - return 1; - return itemsInLine() + 1; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - if (suppressWrapBefore) - result.append("!"); - if (wrapBefore) - result.append("\\n"); - result.append(normalized); - return result.toString(); - } - } - - protected static final Pattern WS = Pattern.compile("\\s+", Pattern.MULTILINE); - - protected TestAsCommaSeparatedValues cfg; - - public CSVResultValidator(TestAsCommaSeparatedValues cfg) { - super(cfg.caseSensitive(), cfg.whitespaceSensitive()); - this.cfg = cfg; - } - - @Override - protected Item createItem(String normalized, String escaped) { - return new ItemWithPred(normalized, escaped); - } - - @Override - protected String escapeUnquoted(String str) { - return super.escapeUnquoted(str).replaceAll(",", "\\,"); - } - - protected String format(List items) { - StringBuilder result = new StringBuilder(); - for (Item i : items) { - if (result.length() > 0) { - if (((ItemWithPred) i).wrapBefore) - result.append(",\n"); - else - result.append(", "); - } - result.append(i.escaped); - } - return result.toString(); - } - - protected void suppressWrap(List wrapped, List items) { - int next = 0; - boolean wrap = false; - for (Item w : wrapped) { - int found = -1; - for (int i = next; i < items.size(); i++) - if (items.get(i).equals(w)) { - found = i; - break; - } - wrap |= ((ItemWithPred) w).wrapBefore; - if (found >= 0) { - if (!wrap) - for (int i = next; i <= found; i++) - ((ItemWithPred) items.get(i)).suppressWrapBefore = true; - else - ((ItemWithPred) items.get(found)).wrapBefore = true; - wrap = false; - next = found + 1; - } - } - } - - public void validate(String expectation, @TestResult Iterable actual) { - List expectedList = toStringList(expectation, ','); - List actualList = toStringList(actual, cfg.itemFormatter()); - Collection expectedNormalized = normalize(expectedList); - Collection actualNormalized = normalize(actualList); - if (!expectedNormalized.equals(actualNormalized)) { - List actualItems = escape(actualList); - List expectedItems = escape(expectedList); - if (!cfg.ordered()) - expectedItems = sort(actualItems, expectedItems); - wrap(actualItems); - suppressWrap(actualItems, expectedItems); - wrap(expectedItems); - String expectedFormatted = format(expectedItems); - String actualFormatted = format(actualItems); - throw new ComparisonFailure("", expectedFormatted, actualFormatted); - } - } - - protected void wrap(List items) { - boolean restrictWidth = cfg.maxLineWidth() >= 0; - boolean restrictItems = cfg.maxItemsPerLine() >= 0; - ItemWithPred last = null; - for (Item item : items) { - ItemWithPred i = (ItemWithPred) item; - i.predecessor = last; - last = i; - } - for (Item item : items) { - ItemWithPred i = (ItemWithPred) item; - if (!i.suppressWrapBefore && !i.wrapBefore) { - boolean itemsEx = restrictItems && i.itemsInLine() > cfg.maxItemsPerLine(); - boolean widthEx = restrictWidth && i.charsInLine() > cfg.maxLineWidth(); - i.wrapBefore = itemsEx || widthEx; - } - } - } - - } - - boolean caseSensitive() default true; - - Class> itemFormatter() default ToString.class; - - int maxItemsPerLine() default -1; - - int maxLineWidth() default 80; - - boolean ordered() default false; - - boolean whitespaceSensitive() default false; - -} diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestAsLines.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestAsLines.java deleted file mode 100644 index 5649d354e..000000000 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestAsLines.java +++ /dev/null @@ -1,77 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - *******************************************************************************/ -package org.eclipse.xtext.junit4.parameterized; - -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.xtext.junit4.parameterized.AbstractIterableExpectationValidator.ToString; -import org.eclipse.xtext.junit4.parameterized.TestAsLines.LinesResultValidator; -import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.TestResult; -import org.junit.ComparisonFailure; - -import com.google.common.base.Function; - -/** - * @author Moritz Eysholdt - Initial contribution and API - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@TestExpectationValidator(validator = LinesResultValidator.class) -public @interface TestAsLines { - - public class LinesResultValidator extends AbstractIterableExpectationValidator { - - protected TestAsLines cfg; - - public LinesResultValidator(TestAsLines cfg) { - super(cfg.caseSensitive(), cfg.whitespaceSensitive()); - this.cfg = cfg; - } - - protected String format(List items) { - StringBuilder result = new StringBuilder(); - for (Item item : items) { - if (result.length() > 0) - result.append("\n"); - result.append(item.escaped); - } - return result.toString(); - } - - public void validate(String expectation, @TestResult Iterable actual) { - List expectedList = toStringList(expectation, '\n'); - List actualList = toStringList(actual, cfg.itemFormatter()); - Collection expectedNormalized = normalize(expectedList); - Collection actualNormalized = normalize(actualList); - if (!expectedNormalized.equals(actualNormalized)) { - List actualItems = escape(actualList); - List expectedItems = escape(expectedList); - if (!cfg.ordered()) - expectedItems = sort(actualItems, expectedItems); - String expectedFormatted = format(expectedItems); - String actualFormatted = format(actualItems); - throw new ComparisonFailure("", expectedFormatted, actualFormatted); - } - } - - } - - boolean caseSensitive() default true; - - boolean whitespaceSensitive() default false; - - Class> itemFormatter() default ToString.class; - - boolean ordered() default false; - -} diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestExpectationValidator.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestExpectationValidator.java index 9b28073af..064c1d0b6 100644 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestExpectationValidator.java +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestExpectationValidator.java @@ -12,6 +12,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.eclipse.xtext.junit4.parameterized.IParameterProvider.IExpectation; +import org.eclipse.xtext.resource.XtextResource; import org.junit.Assert; import org.junit.Test; @@ -24,15 +26,19 @@ public @interface TestExpectationValidator { public interface ITestExpectationValidator { - void validate(String expectation, RESULT actual); + void validate(XtextResource resource, IExpectation expectation, RESULT actual); } public class NullTestResultValidator implements ITestExpectationValidator { public NullTestResultValidator(Test config) { } - public void validate(String expectation, @TestResult Void actual) { - if (expectation != null && expectation.length() > 0) + 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 + "'."); } } diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/Xpect.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/Xpect.java new file mode 100644 index 000000000..f022c606d --- /dev/null +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/Xpect.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.junit4.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Moritz Eysholdt - Initial contribution and API + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@TestExpectationValidator(validator = TestExpectationValidator.NullTestResultValidator.class) +public @interface Xpect { +} diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectCommaSeparatedValues.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectCommaSeparatedValues.java new file mode 100644 index 000000000..0a5838ef0 --- /dev/null +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectCommaSeparatedValues.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.junit4.parameterized; + +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.xtext.junit4.parameterized.ActualCollection.ActualItem; +import org.eclipse.xtext.junit4.parameterized.ActualCollection.ToString; +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection.ExpectationItem; +import org.eclipse.xtext.junit4.parameterized.IParameterProvider.IExpectation; +import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.ITestExpectationValidator; +import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.TestResult; +import org.eclipse.xtext.junit4.parameterized.XpectCommaSeparatedValues.CSVResultValidator; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Pair; +import org.junit.ComparisonFailure; + +import com.google.common.base.Function; + +/** + * @author Moritz Eysholdt - Initial contribution and API + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@TestExpectationValidator(validator = CSVResultValidator.class) +public @interface XpectCommaSeparatedValues { + + public class CSVResultValidator implements ITestExpectationValidator> { + 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 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, 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().isEmpty(); + } + 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> 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/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectLines.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectLines.java new file mode 100644 index 000000000..d083b0298 --- /dev/null +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectLines.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.junit4.parameterized; + +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.xtext.junit4.parameterized.ActualCollection.ActualItem; +import org.eclipse.xtext.junit4.parameterized.ActualCollection.ToString; +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection.ExpectationItem; +import org.eclipse.xtext.junit4.parameterized.IParameterProvider.IExpectation; +import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.ITestExpectationValidator; +import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.TestResult; +import org.eclipse.xtext.junit4.parameterized.XpectLines.LinesResultValidator; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Pair; +import org.junit.ComparisonFailure; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +/** + * @author Moritz Eysholdt - Initial contribution and API + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@TestExpectationValidator(validator = LinesResultValidator.class) +public @interface XpectLines { + + public class LinesResultValidator implements ITestExpectationValidator> { + + protected XpectLines cfg; + + public LinesResultValidator(XpectLines cfg) { + this.cfg = cfg; + } + + public void validate(XtextResource res, IExpectation expectation, @TestResult Iterable 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 expString = Lists.newArrayList(); + List actString = Lists.newArrayList(); + for (Pair, 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> itemFormatter() default ToString.class; + + boolean ordered() default false; + +} diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectParameterProvider.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectParameterProvider.java index 0d2771152..69f56e7d0 100644 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectParameterProvider.java +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectParameterProvider.java @@ -7,74 +7,289 @@ *******************************************************************************/ package org.eclipse.xtext.junit4.parameterized; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Map; +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.Lists; +import com.google.common.collect.Maps; /** * @author Moritz Eysholdt - Initial contribution and API */ +@SuppressWarnings("restriction") public class XpectParameterProvider implements IParameterProvider { - protected static Pattern XPECT_PATTERN = Pattern.compile("(\\S)?XPECT(_TEST_CLASS)?\\s*([a-zA-Z0-9]+)"); + protected static class AssignedProduction extends StringProduction { - // XPECT foo - // XPECT foo("param") - // XPECT foo("param", param2) - // XPECT foo at location - // XPECT foo at loca!tion - // XPECT foo("param") at location - // XPECT foo --> expectation - /* XPECT foo --- - expectation - --- */ - /* XPECT foo("param") at location --- - expectation - --- */ + public AssignedProduction(String production) { + super(production); + } - public void collectParameters(XtextResource resource, IParameterAcceptor acceptor) { - for (ILeafNode leaf : resource.getParseResult().getRootNode().getLeafNodes()) - if (leaf.isHidden() && leaf.getText().contains("XPECT")) - parseLeaf(resource, leaf, acceptor); + @Override + protected ProdElement parsePrim(Stack> tokens) { + Pair 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 eq = tokens.pop(); + if (eq.getFirst() == Token.EQ) { + Pair 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 void parseLeaf(XtextResource resource, ILeafNode leaf, IParameterAcceptor acceptor) { + 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 OFFSET = "offset"; + + 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) { + Object[][] params = createParams(res, Collections. emptyMap()); + acceptor.acceptTest(null, meth.getName(), params, null, false); + } + } + } + } + + protected String convertValue(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) + return String.valueOf(result + add); + else + throw new RuntimeException("OFFSET '" + value + "' not found"); + case ID: + case INT: + case STRING: + case TEXT: + return value; + } + return value; + } + + protected Object[][] createParams(XtextResource res, Map params) { + Object[] params1 = new Object[] { res, params }; + Object[] params2 = new Object[] { res }; + return new Object[][] { params1, params2 }; + } + + 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 it = new NodeIterator(node); + while (it.hasNext()) { + INode n = it.next(); + if (n instanceof ILeafNode && !((ILeafNode) n).isHidden()) + return n.getOffset(); + } + return node.getOffset() + node.getLength(); + } + + protected Nfa getParameterNfa(String syntax) { + AssignedProduction prod = new AssignedProduction(syntax); + FollowerFunctionImpl ff = new FollowerFunctionImpl(prod); + ProdElement start = prod.new ProdElement(ElementType.TOKEN); + ProdElement stop = prod.new ProdElement(ElementType.TOKEN); + Nfa result = new NfaUtil().create(prod, ff, start, stop); + return result; + } + + protected String getParameterSyntax(Class testClass, String methodName) { + try { + Method method = testClass.getMethod(methodName); + Parameter annotation = method.getAnnotation(Parameter.class); + if (annotation != null) + return annotation.syntax(); + } 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(resource, leaf, text, matcher.group(3), matcher.end(), acceptor, + 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 { - int newOffset; - if ((newOffset = parseXpectTest(text, matcher.group(3), matcher.end())) >= 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 parseStringOrText(String text, int offset, Wrapper value) { - int newOffset = parseString(text, offset, value); - if (newOffset < 0) - newOffset = parseText(text, offset, value); - return newOffset; - } - protected int parseString(String text, int offset, Wrapper value) { if (offset < text.length() && text.charAt(offset) == '"') { int i = offset + 1; @@ -88,6 +303,13 @@ public class XpectParameterProvider implements IParameterProvider { return -1; } + protected int parseStringOrText(String text, int offset, Wrapper 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 value) { int i = offset; while (i < text.length()) @@ -105,95 +327,148 @@ public class XpectParameterProvider implements IParameterProvider { return i; } - protected int parseXpect(XtextResource res, INode ctx, String text, String method, int offset, + protected int parseXpect(Class testClass, XtextResource res, INode ctx, String text, String method, int offset, IParameterAcceptor acceptor, boolean ignore) { int newOffset; - List params = Lists.newArrayList(); - Wrapper location = new Wrapper(null); - Wrapper expectation = new Wrapper(null); + Map params = Maps.newLinkedHashMap(); + Wrapper expectation = new Wrapper(null); offset = skipWhitespace(text, offset); - if ((newOffset = parseXpectParams(text, offset, params)) >= 0) + if ((newOffset = parseXpectParams(testClass, ctx, method, text, offset, params)) >= 0) offset = newOffset; offset = skipWhitespace(text, offset); - if ((newOffset = parseXpectLocation(text, offset, location)) >= 0) - offset = skipWhitespace(text, newOffset); - if ((newOffset = parseXpectSLExpectation(text, offset, expectation)) >= 0) + if ((newOffset = parseXpectSLExpectation(ctx, text, offset, expectation)) >= 0) offset = newOffset; - else if ((newOffset = parseXpectMLExpectation(text, offset, expectation)) >= 0) + else if ((newOffset = parseXpectMLExpectation(ctx, text, offset, expectation)) >= 0) offset = newOffset; - - int loc; - if (location.get() != null) - loc = res.getParseResult().getRootNode().getText() - .indexOf(location.get(), ctx.getOffset() + ctx.getLength()); - else - loc = ctx.getOffset(); - Object[] testParms = new Object[] { res, loc, params.toArray(new String[params.size()]) }; - acceptor.acceptTest("", method, testParms, expectation.get(), ignore); + acceptor.acceptTest(null, method, createParams(res, params), expectation.get(), ignore); return offset; } - protected int parseXpectLocation(String text, int offset, Wrapper location) { - if (offset + 1 < text.length() && text.substring(offset, offset + 2).equals("at")) { - offset = skipWhitespace(text, offset + 2); - return parseStringOrText(text, offset, location); - } - return -1; + 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(String text, int offset, Wrapper expectation) { - if (offset + 2 < text.length() && text.substring(offset, offset + 3).equals("---")) { + protected int parseXpectMLExpectation(INode node, String text, int offset, Wrapper 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) { - expectation.set(substring.substring(0, end)); - return end; + 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(String text, int offset, List params) { - if (offset < text.length() && text.charAt(offset) == '(') { - int index = text.indexOf(')', offset); - params.add(text.substring(offset, index)); - return index; + protected int parseXpectParams(Class testClass, INode node, String methodName, final String text, int offset, + Map params) { + String paramSyntax = getParameterSyntax(testClass, methodName); + if (org.eclipse.xtext.util.Strings.isEmpty(paramSyntax)) + return -1; + Nfa nfa = getParameterNfa(paramSyntax); + List trace = new NfaUtil().backtrack(nfa, new BacktrackItem(offset), + new BacktrackHandler() { + 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 sortFollowers(BacktrackItem result, Iterable followers) { + return followers; + } + }); + int semanticOffset = getOffsetOfNextSemanticNode(node); + params.put(OFFSET, String.valueOf(semanticOffset)); + if (trace != null && !trace.isEmpty()) { + for (BacktrackItem item : trace) + if (item.token != null && item.token.getName() != null) + params.put(item.token.getName(), + convertValue(node, semanticOffset, Token.valueOf(item.token.getValue()), item.value)); + return trace.get(trace.size() - 1).offset; } return -1; } - protected int parseXpectSLExpectation(String text, int offset, Wrapper expectation) { - if (offset + 2 < text.length() && text.substring(offset, offset + 3).equals("-->")) { - int index = text.indexOf('\n', offset + 3); - if (index < 0) - index = text.length(); - expectation.set(text.substring(offset + 3, index)); - return index; + protected int parseXpectSLExpectation(INode node, String text, int offset, Wrapper 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(String text, String name, int offset) { - return -1; - } + // protected int parseXpectTest(Wrapper> 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()) - switch (text.charAt(i)) { - case ' ': - case '\t': - case '\r': - case '\n': - i++; - break; - default: - return i; - } + if (Character.isWhitespace(text.charAt(i))) + i++; + else + return i; return i; } diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestAsString.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectString.java similarity index 63% rename from plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestAsString.java rename to plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectString.java index 2b7ff44c7..c934a0197 100644 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/TestAsString.java +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/parameterized/XpectString.java @@ -11,11 +11,12 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.util.regex.Pattern; +import org.eclipse.xtext.junit4.parameterized.IParameterProvider.IExpectation; import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.ITestExpectationValidator; import org.eclipse.xtext.junit4.parameterized.TestExpectationValidator.TestResult; -import org.eclipse.xtext.junit4.parameterized.TestAsString.StringResultValidator; +import org.eclipse.xtext.junit4.parameterized.XpectString.StringResultValidator; +import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.util.internal.FormattingMigrator; import org.junit.ComparisonFailure; @@ -25,30 +26,29 @@ import org.junit.ComparisonFailure; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @TestExpectationValidator(validator = StringResultValidator.class) -public @interface TestAsString { +public @interface XpectString { public class StringResultValidator implements ITestExpectationValidator { - protected static final Pattern WS = Pattern.compile("\\s+", Pattern.MULTILINE); - protected TestAsString config; + protected XpectString config; - public StringResultValidator(TestAsString config) { + public StringResultValidator(XpectString config) { this.config = config; } - public void validate(String expectation, @TestResult String actual) { + public void validate(XtextResource resource, IExpectation expectation, @TestResult String actual) { String exp; if (!config.whitespaceSensitive()) { FormattingMigrator migrator = new FormattingMigrator(); - exp = migrator.migrate(actual, expectation, WS); + exp = migrator.migrate(actual, expectation.getExpectation()); } else - exp = expectation; - if (config.caseSensitive()) { - if (!exp.equals(actual)) - throw new ComparisonFailure("", exp, actual); - } else { - if (!exp.equalsIgnoreCase(actual)) - throw new ComparisonFailure("", exp, actual); + 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); } } } diff --git a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/util/ParseHelper.java b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/util/ParseHelper.java index a1b3dc007..e7ad4b9a2 100644 --- a/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/util/ParseHelper.java +++ b/plugins/org.eclipse.xtext.junit4/src/org/eclipse/xtext/junit4/util/ParseHelper.java @@ -16,56 +16,62 @@ import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.xtext.Constants; import org.eclipse.xtext.resource.IResourceFactory; import org.eclipse.xtext.resource.XtextResourceSet; import org.eclipse.xtext.util.StringInputStream; import com.google.inject.Inject; import com.google.inject.Provider; +import com.google.inject.name.Named; /** - * Some convenience methods for parsing (i.e. testing, etc.) + * Some convenience methods for parsing (i.e. testing, etc.) * * @author Sven Efftinge - Initial contribution and API */ public class ParseHelper { - + @Inject private Provider resourceSetProvider; - + @Inject private IResourceFactory resourceFactory; + @Inject + @Named(Constants.FILE_EXTENSIONS) + public String fileExtension; + @SuppressWarnings("unchecked") - public T parse(InputStream in, URI uriToUse, Map options, ResourceSet resourceSet) { + public T parse(InputStream in, URI uriToUse, Map options, ResourceSet resourceSet) { Resource resource = resourceFactory.createResource(uriToUse); resourceSet.getResources().add(resource); try { resource.load(in, options); - final T root = (T) (resource.getContents().isEmpty()? null : resource.getContents().get(0)); + final T root = (T) (resource.getContents().isEmpty() ? null : resource.getContents().get(0)); return root; } catch (IOException e) { throw new WrappedException(e); } } - + public T parse(CharSequence text) throws Exception { return parse(text, resourceSetProvider.get()); } - + public T parse(CharSequence text, ResourceSet resourceSetToUse) throws Exception { return parse(getAsStream(text), computeUnusedUri(resourceSetToUse), null, resourceSetToUse); } - + public T parse(CharSequence text, URI uriToUse, ResourceSet resourceSetToUse) throws Exception { - return parse(getAsStream(text), uriToUse, null, resourceSetToUse); - } - + return parse(getAsStream(text), uriToUse, null, resourceSetToUse); + } + protected URI computeUnusedUri(ResourceSet resourceSet) { String name = "__synthetic"; for (int i = 0; i < Integer.MAX_VALUE; i++) { - URI syntheticUri = URI.createURI(name+i+".uri"); - if (resourceSet.getResource(syntheticUri, false)==null) + URI syntheticUri = URI.createURI(name + i + "." + fileExtension); + if (resourceSet.getResource(syntheticUri, false) == null) return syntheticUri; } throw new IllegalStateException(); diff --git a/tests/org.eclipse.xtext.junit4.tests/.classpath b/tests/org.eclipse.xtext.junit4.tests/.classpath index 1fa3e6803..64c5e31b7 100644 --- a/tests/org.eclipse.xtext.junit4.tests/.classpath +++ b/tests/org.eclipse.xtext.junit4.tests/.classpath @@ -1,6 +1,6 @@ - + diff --git a/tests/org.eclipse.xtext.junit4.tests/.project b/tests/org.eclipse.xtext.junit4.tests/.project index 37ee3c7da..880674485 100644 --- a/tests/org.eclipse.xtext.junit4.tests/.project +++ b/tests/org.eclipse.xtext.junit4.tests/.project @@ -20,9 +20,15 @@ + + org.eclipse.xtext.ui.shared.xtextBuilder + + + org.eclipse.jdt.core.javanature org.eclipse.pde.PluginNature + org.eclipse.xtext.ui.shared.xtextNature diff --git a/tests/org.eclipse.xtext.junit4.tests/.settings/org.eclipse.jdt.core.prefs b/tests/org.eclipse.xtext.junit4.tests/.settings/org.eclipse.jdt.core.prefs index 445503c33..1da885da2 100644 --- a/tests/org.eclipse.xtext.junit4.tests/.settings/org.eclipse.jdt.core.prefs +++ b/tests/org.eclipse.xtext.junit4.tests/.settings/org.eclipse.jdt.core.prefs @@ -1,7 +1,12 @@ -#Fri Jul 22 15:47:42 CEST 2011 +#Wed Oct 26 02:54:21 CEST 2011 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/tests/org.eclipse.xtext.junit4.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.xtext.junit4.tests/META-INF/MANIFEST.MF index 7da616dcd..86ea68283 100644 --- a/tests/org.eclipse.xtext.junit4.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.xtext.junit4.tests/META-INF/MANIFEST.MF @@ -6,5 +6,7 @@ Bundle-Vendor: Eclipse Modeling Project Bundle-Version: 2.1.0.qualifier Require-Bundle: org.junit4, org.eclipse.xtext.junit4, - com.google.inject;bundle-version="2.0.0" + com.google.inject;bundle-version="2.0.0", + com.google.collect;bundle-version="1.0.0", + org.eclipse.xtext;bundle-version="2.1.0" Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/ExpectationCollectionTest.java b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/ExpectationCollectionTest.java new file mode 100644 index 000000000..910e36363 --- /dev/null +++ b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/ExpectationCollectionTest.java @@ -0,0 +1,271 @@ +package org.eclipse.xtext.junit4.parameterized; + +import java.util.List; + +import junit.framework.Assert; + +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection; +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection.ExpectationItem; +import org.junit.Test; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +public class ExpectationCollectionTest +{ + + private String format(ExpectationCollection ec) + { + List result = Lists.newArrayList(); + for (ExpectationItem i : ec) + { + if (i.isWildcard()) + result.add("(wildcard)"); + else + result.add((i.isNegated() ? "(negated) " : "") + i.getNormalized().replace("\n", "\\n").replace("\r", "\\r") + + " -> " + i.getEscaped()); + } + return Joiner.on('\n').join(result); + } + + @Test(expected = NullPointerException.class) + public void robust1() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.init(null); + } + + @Test + public void robust2() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.init(""); + Assert.assertFalse(ec.iterator().hasNext()); + Assert.assertEquals("", format(ec)); + } + + @Test + public void robust3() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.init(" "); + Assert.assertFalse(ec.iterator().hasNext()); + Assert.assertEquals("", format(ec)); + } + + @Test + public void robust4() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.init("\t"); + Assert.assertFalse(ec.iterator().hasNext()); + Assert.assertEquals("", format(ec)); + } + + @Test + public void robust5() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.init("\t,\t"); + Assert.assertEquals("", format(ec)); + Assert.assertFalse(ec.iterator().hasNext()); + } + + @Test + public void simple() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.setQuote('\''); + ec.init("foo, bar"); + + StringBuilder exp = new StringBuilder(); + exp.append("foo -> 'foo'\n"); + exp.append("bar -> 'bar'"); + Assert.assertEquals(exp.toString(), format(ec)); + } + + @Test + public void simple_wcq() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.setQuote('\''); + ec.setWhitespaceSensitive(false); + ec.setCaseSensitive(false); + ec.setQuoted(false); + ec.init("foo, white space , CasE, !neg, \\!nneg, ..., \\..., es\\,\r\n\\\\caped, 'quoted', qu'ote"); + StringBuilder exp = new StringBuilder(); + exp.append("foo -> foo\n"); + exp.append("whitespace -> white space\n"); + exp.append("case -> CasE\n"); + exp.append("(negated) neg -> neg\n"); + exp.append("!nneg -> \\!nneg\n"); + exp.append("(wildcard)\n"); + exp.append("... -> \\...\n"); + exp.append("es,\\caped -> es\\,\\r\\n\\\\caped\n"); + exp.append("'quoted' -> 'quoted'\n"); + exp.append("qu'ote -> qu'ote"); + Assert.assertEquals(exp.toString(), format(ec)); + } + + @Test + public void simple_wcQ() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.setQuote('\''); + ec.setWhitespaceSensitive(false); + ec.setCaseSensitive(false); + ec.setQuoted(true); + ec.init("'foo', ' white space ', 'CasE', !'neg', '!nneg', ..., '...', 'es,\r\n\\\\caped', 'quoted', 'qu\\'ote'"); + StringBuilder exp = new StringBuilder(); + exp.append("foo -> 'foo'\n"); + exp.append("whitespace -> ' white space '\n"); + exp.append("case -> 'CasE'\n"); + exp.append("(negated) neg -> 'neg'\n"); + exp.append("!nneg -> '!nneg'\n"); + exp.append("(wildcard)\n"); + exp.append("... -> '...'\n"); + exp.append("es,\\caped -> 'es,\\r\\n\\\\caped'\n"); + exp.append("quoted -> 'quoted'\n"); + exp.append("qu'ote -> 'qu\\'ote'"); + Assert.assertEquals(exp.toString(), format(ec)); + } + + @Test + public void simple_wCq() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.setQuote('\''); + ec.setWhitespaceSensitive(false); + ec.setCaseSensitive(true); + ec.setQuoted(false); + ec.init("foo, white space , CasE, !neg, \\!nneg, ..., \\..., es\\,\r\n\\\\caped, 'quoted', qu'ote"); + StringBuilder exp = new StringBuilder(); + exp.append("foo -> foo\n"); + exp.append("whitespace -> white space\n"); + exp.append("CasE -> CasE\n"); + exp.append("(negated) neg -> neg\n"); + exp.append("!nneg -> \\!nneg\n"); + exp.append("(wildcard)\n"); + exp.append("... -> \\...\n"); + exp.append("es,\\caped -> es\\,\\r\\n\\\\caped\n"); + exp.append("'quoted' -> 'quoted'\n"); + exp.append("qu'ote -> qu'ote"); + Assert.assertEquals(exp.toString(), format(ec)); + } + + @Test + public void simple_wCQ() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.setQuote('\''); + ec.setWhitespaceSensitive(false); + ec.setCaseSensitive(true); + ec.setQuoted(true); + ec.init("'foo', ' white space ', 'CasE', !'neg', '!nneg', ..., '...', 'es,\r\n\\\\caped', 'quoted', 'qu\\'ote'"); + StringBuilder exp = new StringBuilder(); + exp.append("foo -> 'foo'\n"); + exp.append("whitespace -> ' white space '\n"); + exp.append("CasE -> 'CasE'\n"); + exp.append("(negated) neg -> 'neg'\n"); + exp.append("!nneg -> '!nneg'\n"); + exp.append("(wildcard)\n"); + exp.append("... -> '...'\n"); + exp.append("es,\\caped -> 'es,\\r\\n\\\\caped'\n"); + exp.append("quoted -> 'quoted'\n"); + exp.append("qu'ote -> 'qu\\'ote'"); + Assert.assertEquals(exp.toString(), format(ec)); + } + + @Test + public void simple_Wcq() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.setQuote('\''); + ec.setWhitespaceSensitive(true); + ec.setCaseSensitive(false); + ec.setQuoted(false); + ec.init("foo, white space , CasE, !neg, \\!nneg, ..., \\..., es\\,\r\n\\\\caped, 'quoted', qu'ote"); + StringBuilder exp = new StringBuilder(); + exp.append("foo -> foo\n"); + exp.append("white space -> white space\n"); + exp.append("case -> CasE\n"); + exp.append("(negated) neg -> neg\n"); + exp.append("!nneg -> \\!nneg\n"); + exp.append("(wildcard)\n"); + exp.append("... -> \\...\n"); + exp.append("es,\\r\\n\\caped -> es\\,\\r\\n\\\\caped\n"); + exp.append("'quoted' -> 'quoted'\n"); + exp.append("qu'ote -> qu'ote"); + Assert.assertEquals(exp.toString(), format(ec)); + } + + @Test + public void simple_WcQ() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.setQuote('\''); + ec.setWhitespaceSensitive(true); + ec.setCaseSensitive(false); + ec.setQuoted(true); + ec.init("'foo', ' white space ', 'CasE', !'neg', '!nneg', ..., '...', 'es,\r\n\\\\caped', 'quoted', 'qu\\'ote'"); + StringBuilder exp = new StringBuilder(); + exp.append("foo -> 'foo'\n"); + exp.append(" white space -> ' white space '\n"); + exp.append("case -> 'CasE'\n"); + exp.append("(negated) neg -> 'neg'\n"); + exp.append("!nneg -> '!nneg'\n"); + exp.append("(wildcard)\n"); + exp.append("... -> '...'\n"); + exp.append("es,\\r\\n\\caped -> 'es,\\r\\n\\\\caped'\n"); + exp.append("quoted -> 'quoted'\n"); + exp.append("qu'ote -> 'qu\\'ote'"); + Assert.assertEquals(exp.toString(), format(ec)); + } + + @Test + public void simple_WCq() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.setQuote('\''); + ec.setWhitespaceSensitive(true); + ec.setCaseSensitive(true); + ec.setQuoted(false); + ec.init("foo, white space , CasE, !neg, \\!nneg, ..., \\..., es\\,\r\n\\\\caped, 'quoted', qu'ote"); + StringBuilder exp = new StringBuilder(); + exp.append("foo -> foo\n"); + exp.append("white space -> white space\n"); + exp.append("CasE -> CasE\n"); + exp.append("(negated) neg -> neg\n"); + exp.append("!nneg -> \\!nneg\n"); + exp.append("(wildcard)\n"); + exp.append("... -> \\...\n"); + exp.append("es,\\r\\n\\caped -> es\\,\\r\\n\\\\caped\n"); + exp.append("'quoted' -> 'quoted'\n"); + exp.append("qu'ote -> qu'ote"); + Assert.assertEquals(exp.toString(), format(ec)); + } + + @Test + public void simple_WCQ() + { + ExpectationCollection ec = new ExpectationCollection(); + ec.setQuote('\''); + ec.setWhitespaceSensitive(true); + ec.setCaseSensitive(true); + ec.setQuoted(true); + ec.init("'foo', ' white space ', 'CasE', !'neg', '!nneg', ..., '...', 'es,\r\n\\\\caped', 'quoted', 'qu\\'ote'"); + StringBuilder exp = new StringBuilder(); + exp.append("foo -> 'foo'\n"); + exp.append(" white space -> ' white space '\n"); + exp.append("CasE -> 'CasE'\n"); + exp.append("(negated) neg -> 'neg'\n"); + exp.append("!nneg -> '!nneg'\n"); + exp.append("(wildcard)\n"); + exp.append("... -> '...'\n"); + exp.append("es,\\r\\n\\caped -> 'es,\\r\\n\\\\caped'\n"); + exp.append("quoted -> 'quoted'\n"); + exp.append("qu'ote -> 'qu\\'ote'"); + Assert.assertEquals(exp.toString(), format(ec)); + } + +} diff --git a/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/OrderedMapTest.java b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/OrderedMapTest.java new file mode 100644 index 000000000..a1f14058c --- /dev/null +++ b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/OrderedMapTest.java @@ -0,0 +1,185 @@ +package org.eclipse.xtext.junit4.parameterized; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.xtext.junit4.parameterized.ActualCollection; +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection; +import org.eclipse.xtext.junit4.parameterized.ActualCollection.ActualItem; +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection.ExpectationItem; +import org.eclipse.xtext.util.Pair; +import org.junit.Assert; +import org.junit.Test; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +public class OrderedMapTest +{ + + private String format(Collection items) + { + List result = Lists.newArrayList(); + for (ExpectationItem item : items) + { + if (item.isWildcard()) + result.add("..."); + else if (item.isNegated()) + result.add("!" + item.getNormalized()); + else + result.add(item.getNormalized()); + } + String r = Joiner.on(",").join(result); + return result.size() > 1 ? "(" + r + ")" : r; + } + + private String format(List, ActualItem>> map) + { + List result = Lists.newArrayList(); + for (Pair, ActualItem> item : map) + { + if ((item.getFirst() == null || item.getFirst().isEmpty()) && item.getSecond() == null) + result.add("null<>null"); + else if (item.getFirst() == null || item.getFirst().isEmpty()) + result.add("null<>" + item.getSecond().getNormalized()); + else if (item.getSecond() == null) + result.add(format(item.getFirst()) + "<>null"); + else + result.add(format(item.getFirst()) + "<>" + item.getSecond().getNormalized()); + } + return Joiner.on("; ").join(result); + } + + @Test + public void simple() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, bar"); + act.init("foo", "bar"); + Assert.assertEquals("foo<>foo; bar<>bar", format(exp.map(act))); + act.init("bar", "foo"); + Assert.assertEquals("null<>bar; foo<>foo; bar<>null", format(exp.map(act))); + act.init("bar"); + Assert.assertEquals("foo<>null; bar<>bar", format(exp.map(act))); + act.init("foo", "bar", "baz"); + Assert.assertEquals("foo<>foo; bar<>bar; null<>baz", format(exp.map(act))); + act.init("bar", "baz"); + Assert.assertEquals("foo<>null; bar<>bar; null<>baz", format(exp.map(act))); + } + + @Test + public void multi() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, foo"); + act.init("foo", "foo"); + Assert.assertEquals("foo<>foo; foo<>foo", format(exp.map(act))); + act.init("foo"); + Assert.assertEquals("foo<>foo; foo<>null", format(exp.map(act))); + act.init("foo", "foo", "foo"); + Assert.assertEquals("foo<>foo; foo<>foo; null<>foo", format(exp.map(act))); + } + + @Test + public void wildcard1() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, ..."); + act.init("foo", "foo"); + Assert.assertEquals("foo<>foo; ...<>foo", format(exp.map(act))); + act.init("foo", "baz", "zonk"); + Assert.assertEquals("foo<>foo; ...<>baz; ...<>zonk", format(exp.map(act))); + act.init("foo"); + Assert.assertEquals("foo<>foo", format(exp.map(act))); + act.init("foo", "bar"); + Assert.assertEquals("foo<>foo; ...<>bar", format(exp.map(act))); + act.init("bar", "foo"); + Assert.assertEquals("null<>bar; foo<>foo", format(exp.map(act))); + act.init("bar"); + Assert.assertEquals("foo<>null; ...<>bar", format(exp.map(act))); + } + + @Test + public void wildcard2() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, ..., bar, ..."); + act.init("foo", "bar"); + Assert.assertEquals("foo<>foo; bar<>bar", format(exp.map(act))); + act.init("foo", "xxx", "bar", "zzz"); + Assert.assertEquals("foo<>foo; ...<>xxx; bar<>bar; ...<>zzz", format(exp.map(act))); + act.init("foo", "aaa", "xxx", "bar", "bbb", "zzz"); + Assert.assertEquals("foo<>foo; ...<>aaa; ...<>xxx; bar<>bar; ...<>bbb; ...<>zzz", format(exp.map(act))); + act.init("foo"); + Assert.assertEquals("foo<>foo; bar<>null", format(exp.map(act))); + act.init("bar"); + Assert.assertEquals("foo<>null; bar<>bar", format(exp.map(act))); + act.init("bar", "foo"); + Assert.assertEquals("null<>bar; foo<>foo; bar<>null", format(exp.map(act))); + act.init("bar", "xxx", "foo", "zzz"); + Assert.assertEquals("null<>bar; null<>xxx; foo<>foo; ...<>zzz; bar<>null", format(exp.map(act))); + } + + @Test + public void negated1() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, !bar"); + act.init("foo", "foo"); + Assert.assertEquals("foo<>foo; !bar<>foo", format(exp.map(act))); + act.init("foo", "baz", "zonk"); + Assert.assertEquals("foo<>foo; !bar<>baz; !bar<>zonk", format(exp.map(act))); + act.init("foo"); + Assert.assertEquals("foo<>foo", format(exp.map(act))); + act.init("foo", "bar"); + Assert.assertEquals("foo<>foo; null<>bar", format(exp.map(act))); + act.init("bar", "foo"); + Assert.assertEquals("null<>bar; foo<>foo", format(exp.map(act))); + act.init("bar"); + Assert.assertEquals("foo<>null; null<>bar", format(exp.map(act))); + } + + @Test + public void negated2() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, !xxx, bar, !zzz"); + act.init("foo", "bar"); + Assert.assertEquals("foo<>foo; bar<>bar", format(exp.map(act))); + act.init("foo", "xxx", "bar", "zzz"); + Assert.assertEquals("foo<>foo; null<>xxx; bar<>bar; null<>zzz", format(exp.map(act))); + act.init("foo", "xxz", "bar", "zzx"); + Assert.assertEquals("foo<>foo; !xxx<>xxz; bar<>bar; !zzz<>zzx", format(exp.map(act))); + act.init("foo", "aaa", "xxx", "bar", "bbb", "zzz"); + Assert.assertEquals("foo<>foo; !xxx<>aaa; null<>xxx; bar<>bar; !zzz<>bbb; null<>zzz", format(exp.map(act))); + act.init("foo", "aaa", "xxz", "bar", "bbb", "zzx"); + Assert.assertEquals("foo<>foo; !xxx<>aaa; !xxx<>xxz; bar<>bar; !zzz<>bbb; !zzz<>zzx", format(exp.map(act))); + act.init("foo"); + Assert.assertEquals("foo<>foo; bar<>null", format(exp.map(act))); + act.init("bar"); + Assert.assertEquals("foo<>null; bar<>bar", format(exp.map(act))); + act.init("bar", "foo"); + Assert.assertEquals("null<>bar; foo<>foo; bar<>null", format(exp.map(act))); + act.init("bar", "xxz", "foo", "zzx"); + Assert.assertEquals("null<>bar; null<>xxz; foo<>foo; !xxx<>zzx; bar<>null", format(exp.map(act))); + } + +} diff --git a/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/OrderedMatchTest.java b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/OrderedMatchTest.java new file mode 100644 index 000000000..01d7ed257 --- /dev/null +++ b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/OrderedMatchTest.java @@ -0,0 +1,143 @@ +package org.eclipse.xtext.junit4.parameterized; + +import org.eclipse.xtext.junit4.parameterized.ActualCollection; +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection; +import org.junit.Assert; +import org.junit.Test; + +public class OrderedMatchTest +{ + + @Test + public void simple() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, bar"); + act.init("foo", "bar"); + Assert.assertTrue(exp.matches(act)); + act.init("bar", "foo"); + Assert.assertFalse(exp.matches(act)); + act.init("bar"); + Assert.assertFalse(exp.matches(act)); + act.init("foo", "bar", "baz"); + Assert.assertFalse(exp.matches(act)); + act.init("bar", "baz"); + Assert.assertFalse(exp.matches(act)); + } + + @Test + public void multi() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, foo"); + act.init("foo", "foo"); + Assert.assertTrue(exp.matches(act)); + act.init("foo"); + Assert.assertFalse(exp.matches(act)); + act.init("foo", "foo", "foo"); + Assert.assertFalse(exp.matches(act)); + } + + @Test + public void wildcard1() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, ..."); + act.init("foo", "foo"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "baz", "zonk"); + Assert.assertTrue(exp.matches(act)); + act.init("foo"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "bar"); + Assert.assertTrue(exp.matches(act)); + act.init("bar", "foo"); + Assert.assertFalse(exp.matches(act)); + act.init("bar"); + Assert.assertFalse(exp.matches(act)); + } + + @Test + public void wildcard2() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, ..., bar, ..."); + act.init("foo", "bar"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "xxx", "bar", "zzz"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "aaa", "xxx", "bar", "bbb", "zzz"); + Assert.assertTrue(exp.matches(act)); + act.init("foo"); + Assert.assertFalse(exp.matches(act)); + act.init("bar"); + Assert.assertFalse(exp.matches(act)); + act.init("bar", "foo"); + Assert.assertFalse(exp.matches(act)); + act.init("bar", "xxx", "foo", "zzz"); + Assert.assertFalse(exp.matches(act)); + } + + @Test + public void negated1() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, !bar"); + act.init("foo", "foo"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "baz", "zonk"); + Assert.assertTrue(exp.matches(act)); + act.init("foo"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "bar"); + Assert.assertFalse(exp.matches(act)); + act.init("bar", "foo"); + Assert.assertFalse(exp.matches(act)); + act.init("bar"); + Assert.assertFalse(exp.matches(act)); + } + + @Test + public void negated2() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(true); + act.setOrdered(true); + exp.init("foo, !xxx, bar, !zzz"); + act.init("foo", "bar"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "xxx", "bar", "zzz"); + Assert.assertFalse(exp.matches(act)); + act.init("foo", "xxz", "bar", "zzx"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "aaa", "xxx", "bar", "bbb", "zzz"); + Assert.assertFalse(exp.matches(act)); + act.init("foo", "aaa", "xxz", "bar", "bbb", "zzx"); + Assert.assertTrue(exp.matches(act)); + act.init("foo"); + Assert.assertFalse(exp.matches(act)); + act.init("bar"); + Assert.assertFalse(exp.matches(act)); + act.init("bar", "foo"); + Assert.assertFalse(exp.matches(act)); + act.init("bar", "xxz", "foo", "zzx"); + Assert.assertFalse(exp.matches(act)); + } + +} diff --git a/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunner.xtext b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunner.xtext new file mode 100644 index 000000000..c487759b5 --- /dev/null +++ b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunner.xtext @@ -0,0 +1,30 @@ +grammar org.eclipse.xtext.junit4.parameterized.ParameterizedXtextRunner + +generate pkg "http://org.eclipse.xtext.junit4.parameterized.ParameterizedXtextRunner" + +import "http://www.eclipse.org/emf/2002/Ecore" as ecore + + // XPECT plain + // XPECT plain + + // XPECT string --> My long String expectation + /* XPECT string --- + My long String expectation + --- */ + + /* XPECT csv --- + aaa, bbb, ccc, ddd, eee, fff, ggg, hhh, iii, jjj, kkk, lll, + mmm, nnn, ooo, ppp, qqq, rrr + --- */ + + /* XPECT lines --- + aaa bbb ccc + foo bar baz + xxx yyy zzz + --- */ + + // XPECT select from fo!o to b!az --> o='val' bar='val1' b + +Root: + foo='val' bar='val1' baz='xxx' +; \ No newline at end of file diff --git a/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunnerTest.java b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunnerTest.java new file mode 100644 index 000000000..dd4ac5100 --- /dev/null +++ b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunnerTest.java @@ -0,0 +1,107 @@ +package org.eclipse.xtext.junit4.parameterized; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import org.eclipse.xtext.IGrammarAccess; +import org.eclipse.xtext.junit4.InjectWith; +import org.eclipse.xtext.resource.XtextResource; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.google.common.collect.Lists; +import com.google.inject.Inject; + +@InjectWith(XtextInjectorProvider.class) +@RunWith(ParameterizedXtextRunner.class) +@ResourceURIs(files = +{ "src/org/eclipse/xtext/junit4/parameterized/ParameterizedXtextRunner.xtext" }) +public class ParameterizedXtextRunnerTest +{ + + private XtextResource resource; + private Map params; + + public ParameterizedXtextRunnerTest(XtextResource res, Map params) + { + this.resource = res; + this.params = params; + } + + @Inject + IGrammarAccess injected; + private static boolean goodOldTesthasRun = false; + private static int xpectPlain = 0; + private static int xpectString = 0; + private static int xpectCsv = 0; + private static int xpectLines = 0; + private static int xpectSelect = 0; + + @Test + public void goodOldTest() + { + goodOldTesthasRun = true; + } + + @Before + public void before() + { + assertNotNull(injected); + } + + @AfterClass + public static void after() + { + assertTrue(goodOldTesthasRun); + assertEquals(2, xpectPlain); + assertEquals(2, xpectString); + assertEquals(1, xpectCsv); + assertEquals(1, xpectLines); + assertEquals(1, xpectSelect); + } + + @Xpect + public void plain() + { + xpectPlain++; + } + + @XpectString + public String string() + { + xpectString++; + return "My long String expectation"; + } + + @XpectCommaSeparatedValues(maxLineWidth = 60) + public Iterable csv() + { + xpectCsv++; + return Lists.newArrayList("aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii", "jjj", "kkk", "lll", + "mmm", "nnn", "ooo", "ppp", "qqq", "rrr"); + } + + @XpectLines + public Iterable lines() + { + xpectLines++; + return Lists.newArrayList("foo bar baz", "xxx yyy zzz", "aaa bbb ccc"); + } + + @XpectString + @Parameter(syntax = "'from' from=OFFSET 'to' to=OFFSET") + public String select() + { + xpectSelect++; + String text = resource.getParseResult().getRootNode().getText(); + int from = Integer.parseInt(params.get("from")); + int to = Integer.parseInt(params.get("to")); + return text.substring(from, to); + } + +} diff --git a/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/UnorderedMapTest.java b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/UnorderedMapTest.java new file mode 100644 index 000000000..40ccafaa1 --- /dev/null +++ b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/UnorderedMapTest.java @@ -0,0 +1,126 @@ +package org.eclipse.xtext.junit4.parameterized; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.xtext.junit4.parameterized.ActualCollection; +import org.eclipse.xtext.junit4.parameterized.ActualCollection.ActualItem; +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection; +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection.ExpectationItem; +import org.eclipse.xtext.util.Pair; +import org.junit.Assert; +import org.junit.Test; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +public class UnorderedMapTest +{ + + private String format(Collection items) + { + List result = Lists.newArrayList(); + for (ExpectationItem item : items) + { + if (item.isWildcard()) + result.add("..."); + else if (item.isNegated()) + result.add("!" + item.getNormalized()); + else + result.add(item.getNormalized()); + } + String r = Joiner.on(",").join(result); + return result.size() > 1 ? "(" + r + ")" : r; + } + + private String format(List, ActualItem>> map) + { + List result = Lists.newArrayList(); + for (Pair, ActualItem> item : map) + { + if ((item.getFirst() == null || item.getFirst().isEmpty()) && item.getSecond() == null) + result.add("null<>null"); + else if (item.getFirst() == null || item.getFirst().isEmpty()) + result.add("null<>" + item.getSecond().getNormalized()); + else if (item.getSecond() == null) + result.add(format(item.getFirst()) + "<>null"); + else + result.add(format(item.getFirst()) + "<>" + item.getSecond().getNormalized()); + } + return Joiner.on("; ").join(result); + } + + @Test + public void simple() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(false); + act.setOrdered(false); + exp.init("foo, bar"); + act.init("foo", "bar"); + Assert.assertEquals("bar<>bar; foo<>foo", format(exp.map(act))); + act.init("bar", "foo"); + Assert.assertEquals("bar<>bar; foo<>foo", format(exp.map(act))); + } + + @Test + public void multi() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(false); + act.setOrdered(false); + exp.init("foo, foo"); + act.init("foo", "foo"); + Assert.assertEquals("foo<>foo; foo<>foo", format(exp.map(act))); + act.init("foo"); + Assert.assertEquals("foo<>foo; foo<>null", format(exp.map(act))); + act.init("foo", "foo", "foo"); + Assert.assertEquals("foo<>foo; foo<>foo; null<>foo", format(exp.map(act))); + } + + @Test + public void wildcard() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(false); + act.setOrdered(false); + exp.init("foo, ..."); + act.init("foo", "foo"); + Assert.assertEquals("foo<>foo; ...<>foo", format(exp.map(act))); + act.init("foo", "baz", "zonk"); + Assert.assertEquals("...<>baz; foo<>foo; ...<>zonk", format(exp.map(act))); + act.init("foo"); + Assert.assertEquals("foo<>foo", format(exp.map(act))); + act.init("foo", "bar"); + Assert.assertEquals("...<>bar; foo<>foo", format(exp.map(act))); + act.init("bar", "foo"); + Assert.assertEquals("...<>bar; foo<>foo", format(exp.map(act))); + act.init("bar"); + Assert.assertEquals("...<>bar; foo<>null", format(exp.map(act))); + } + + @Test + public void negated() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(false); + act.setOrdered(false); + exp.init("foo, !bar"); + act.init("foo", "baz"); + Assert.assertEquals("!bar<>baz; foo<>foo", format(exp.map(act))); + act.init("foo", "baz", "zonk"); + Assert.assertEquals("!bar<>baz; foo<>foo; !bar<>zonk", format(exp.map(act))); + act.init("foo"); + Assert.assertEquals("foo<>foo", format(exp.map(act))); + act.init("foo", "bar"); + Assert.assertEquals("null<>bar; foo<>foo", format(exp.map(act))); + act.init("bar", "foo"); + Assert.assertEquals("null<>bar; foo<>foo", format(exp.map(act))); + act.init("bar"); + Assert.assertEquals("null<>bar; foo<>null", format(exp.map(act))); + } +} diff --git a/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/UnorderedMatchTest.java b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/UnorderedMatchTest.java new file mode 100644 index 000000000..1608fe537 --- /dev/null +++ b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/UnorderedMatchTest.java @@ -0,0 +1,84 @@ +package org.eclipse.xtext.junit4.parameterized; + +import org.eclipse.xtext.junit4.parameterized.ActualCollection; +import org.eclipse.xtext.junit4.parameterized.ExpectationCollection; +import org.junit.Assert; +import org.junit.Test; + +public class UnorderedMatchTest +{ + + @Test + public void simple() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(false); + act.setOrdered(false); + exp.init("foo, bar"); + act.init("foo", "bar"); + Assert.assertTrue(exp.matches(act)); + act.init("bar", "foo"); + Assert.assertTrue(exp.matches(act)); + } + + @Test + public void multi() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(false); + act.setOrdered(false); + exp.init("foo, foo"); + act.init("foo", "foo"); + Assert.assertTrue(exp.matches(act)); + act.init("foo"); + Assert.assertFalse(exp.matches(act)); + act.init("foo", "foo", "foo"); + Assert.assertFalse(exp.matches(act)); + } + + @Test + public void wildcard() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(false); + act.setOrdered(false); + exp.init("foo, ..."); + act.init("foo", "foo"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "baz", "zonk"); + Assert.assertTrue(exp.matches(act)); + act.init("foo"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "bar"); + Assert.assertTrue(exp.matches(act)); + act.init("bar", "foo"); + Assert.assertTrue(exp.matches(act)); + act.init("bar"); + Assert.assertFalse(exp.matches(act)); + } + + @Test + public void negated() + { + ExpectationCollection exp = new ExpectationCollection(); + ActualCollection act = new ActualCollection(); + exp.setOrdered(false); + act.setOrdered(false); + exp.init("foo, !bar"); + act.init("foo", "baz"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "baz", "zonk"); + Assert.assertTrue(exp.matches(act)); + act.init("foo"); + Assert.assertTrue(exp.matches(act)); + act.init("foo", "bar"); + Assert.assertFalse(exp.matches(act)); + act.init("bar", "foo"); + Assert.assertFalse(exp.matches(act)); + act.init("bar"); + Assert.assertFalse(exp.matches(act)); + } +} diff --git a/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/XpectParameterProviderTest.java b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/XpectParameterProviderTest.java new file mode 100644 index 000000000..5b7537e65 --- /dev/null +++ b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/XpectParameterProviderTest.java @@ -0,0 +1,493 @@ +package org.eclipse.xtext.junit4.parameterized; + +import java.util.List; +import java.util.Map; + +import junit.framework.Assert; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.junit4.InjectWith; +import org.eclipse.xtext.junit4.XtextRunner; +import org.eclipse.xtext.junit4.parameterized.IParameterProvider.IExpectation; +import org.eclipse.xtext.junit4.parameterized.IParameterProvider.IParameterAcceptor; +import org.eclipse.xtext.junit4.util.ParseHelper; +import org.eclipse.xtext.junit4.validation.ValidationTestHelper; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.util.Exceptions; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.inject.Inject; + +@RunWith(XtextRunner.class) +@InjectWith(value = XtextInjectorProvider.class) +public class XpectParameterProviderTest +{ + public static class TestTestCase + { + public void meth() + { + } + + @Parameter(syntax = "'at' myOffset=OFFSET") + public void offsetMeth() + { + } + + @Parameter(syntax = "('text' myText=TEXT)? ('int' myInt=INT)? ('string' myString=STRING)? ('id' myId=ID)?") + public void optionalMeth() + { + } + } + + @Inject + private ParseHelper parseHelper; + @Inject + private ValidationTestHelper validationHelper; + + @Inject + private XpectParameterProvider parameterProvider; + private String model = "grammar foo.Bar import 'http://www.eclipse.org/emf/2002/Ecore' as ecore\n generate bar 'http://bar.com/'\n{0}\n foo:val='bar';"; + + private String parse(String text) + { + String model = this.model.replace("{0}", text); + Grammar grammar; + try + { + grammar = parseHelper.parse(model); + validationHelper.assertNoIssues(grammar); + final List result = Lists.newArrayList(); + parameterProvider.collectParameters(TestTestCase.class, (XtextResource) grammar.eResource(), + new IParameterAcceptor() + { + + public void acceptImportURI(URI uri) + { + result.add("import " + uri); + } + + public void acceptTest(String title, String method, Object[][] params, IExpectation expectation, + boolean ignore) + { + StringBuilder str = new StringBuilder(); + if (ignore) + str.append("(ignored) "); + if (title != null) + str.append("'" + title + "' "); + List p1 = Lists.newArrayList(); + for (Object[] o : params) + { + List p2 = Lists.newArrayList(); + for (Object x : o) + { + if (x == null) + p2.add("null"); + else if (x instanceof Resource) + p2.add("Res"); + else if (x instanceof Map) + p2.add("Map"); + else if (x instanceof Integer) + p2.add("Int"); + else + p2.add(x.getClass().getSimpleName()); + } + p1.add(Joiner.on(", ").join(p2)); + } + str.append(method + "(" + Joiner.on(" | ").join(p1) + ")"); + if (expectation != null) + str.append(" --> " + expectation.getExpectation().replace("\n", "\\n")); + result.add(str.toString()); + } + + }); + return Joiner.on("\n").join(result); + } catch (Exception e) + { + Exceptions.throwUncheckedException(e); + return ""; + } + } + + private String params(String text) + { + String model = this.model.replace("{0}", text); + Grammar grammar; + try + { + grammar = parseHelper.parse(model); + validationHelper.assertNoIssues(grammar); + final List result = Lists.newArrayList(); + parameterProvider.collectParameters(TestTestCase.class, (XtextResource) grammar.eResource(), + new IParameterAcceptor() + { + + public void acceptImportURI(URI uri) + { + } + + public void acceptTest(String title, String method, Object[][] params, IExpectation expectation, + boolean ignore) + { + XtextResource res = (XtextResource) params[0][0]; + @SuppressWarnings("unchecked") + Map map = (Map) params[0][1]; + List p2 = Lists.newArrayList(); + String text = res.getParseResult().getRootNode().getText(); + for (Map.Entry e : map.entrySet()) + { + if (e.getKey().toLowerCase().contains("offset")) + { + int offs = Integer.parseInt(e.getValue()); + String val = "'" + text.substring(offs - 3, offs) + "!" + text.substring(offs, offs + 3) + "'"; + p2.add(e.getKey() + " at " + val.replace("\n", "\\n")); + } else + p2.add(e.getKey() + " = " + e.getValue()); + } + result.add(method + "(" + Joiner.on(", ").join(p2) + ")"); + } + }); + return Joiner.on("\n").join(result); + } catch (Exception e) + { + Exceptions.throwUncheckedException(e); + return ""; + } + } + + private String expectation(String text) + { + String model = this.model.replace("{0}", text); + int before = this.model.indexOf("{0}"); + int after = this.model.length() - before - 3; + Grammar grammar; + try + { + grammar = parseHelper.parse(model); + validationHelper.assertNoIssues(grammar); + XtextResource resource = (XtextResource) grammar.eResource(); + final StringBuffer result = new StringBuffer(resource.getParseResult().getRootNode().getText()); + parameterProvider.collectParameters(TestTestCase.class, resource, new IParameterAcceptor() + { + + public void acceptImportURI(URI uri) + { + } + + public void acceptTest(String title, String method, Object[][] params, IExpectation expectation, boolean ignore) + { + String replace; + if (expectation.getIndentation() != null) + replace = expectation.getIndentation() + "xxx"; + else + replace = "xxx"; + result.replace(expectation.getOffset(), expectation.getOffset() + expectation.getLength(), replace); + } + }); + return result.substring(before, result.length() - after); + } catch (Exception e) + { + Exceptions.throwUncheckedException(e); + return ""; + } + } + + @Test + public void xpectSL() + { + String model = "// XPECT meth"; + Assert.assertEquals("meth(Res, Map | Res)", parse(model)); + } + + @Test + public void xpectML() + { + String model = "/* XPECT meth */"; + Assert.assertEquals("meth(Res, Map | Res)", parse(model)); + } + + @Test + public void xpectSLExpectation0() + { + String model = "// XPECT meth -->\n"; + String actual = parse(model); + Assert.assertEquals("meth(Res, Map | Res) --> ", actual); + Assert.assertEquals("// XPECT meth -->xxx\n", expectation(model)); + } + + @Test + public void xpectSLExpectation01() + { + String model = "// XPECT meth --> \n"; + String actual = parse(model); + Assert.assertEquals("meth(Res, Map | Res) --> ", actual); + Assert.assertEquals("// XPECT meth --> xxx\n", expectation(model)); + } + + @Test + public void xpectSLExpectation02() + { + String model = "// XPECT meth --> \n"; + String actual = parse(model); + Assert.assertEquals("meth(Res, Map | Res) --> ", actual); + Assert.assertEquals("// XPECT meth --> xxx\n", expectation(model)); + } + + @Test + public void xpectSLExpectation1() + { + String model = "// XPECT meth -->exp\n"; + Assert.assertEquals("meth(Res, Map | Res) --> exp", parse(model)); + Assert.assertEquals("// XPECT meth -->xxx\n", expectation(model)); + } + + @Test + public void xpectSLExpectation2() + { + String model = "// XPECT meth --> exp\n"; + Assert.assertEquals("meth(Res, Map | Res) --> exp", parse(model)); + Assert.assertEquals("// XPECT meth --> xxx\n", expectation(model)); + } + + @Test + public void xpectSLExpectation3() + { + String model = "// XPECT meth --> exp \n"; + Assert.assertEquals("meth(Res, Map | Res) --> exp ", parse(model)); + Assert.assertEquals("// XPECT meth --> xxx\n", expectation(model)); + } + + @Test + public void xpectMLExpectation0() + { + String model = "/* XPECT meth -->\n*/"; + Assert.assertEquals("meth(Res, Map | Res) --> ", parse(model)); + Assert.assertEquals("/* XPECT meth -->xxx\n*/", expectation(model)); + } + + @Test + public void xpectMLExpectation01() + { + String model = "/* XPECT meth --> \n*/"; + Assert.assertEquals("meth(Res, Map | Res) --> ", parse(model)); + Assert.assertEquals("/* XPECT meth --> xxx\n*/", expectation(model)); + } + + @Test + public void xpectMLExpectation02() + { + String model = "/* XPECT meth --> \n*/"; + Assert.assertEquals("meth(Res, Map | Res) --> ", parse(model)); + Assert.assertEquals("/* XPECT meth --> xxx\n*/", expectation(model)); + } + + @Test + public void xpectMLExpectation1() + { + String model = "/* XPECT meth -->exp\n*/"; + Assert.assertEquals("meth(Res, Map | Res) --> exp", parse(model)); + Assert.assertEquals("/* XPECT meth -->xxx\n*/", expectation(model)); + } + + @Test + public void xpectMLExpectation2() + { + String model = "/* XPECT meth --> exp\n*/"; + Assert.assertEquals("meth(Res, Map | Res) --> exp", parse(model)); + Assert.assertEquals("/* XPECT meth --> xxx\n*/", expectation(model)); + } + + @Test + public void xpectMLExpectation3() + { + String model = "/* XPECT meth --> exp \n*/"; + Assert.assertEquals("meth(Res, Map | Res) --> exp ", parse(model)); + Assert.assertEquals("/* XPECT meth --> xxx\n*/", expectation(model)); + } + + @Test + public void xpectMLExpectation4() + { + String model = "\n/* XPECT meth ---\nexp\n---*/\n"; + Assert.assertEquals("meth(Res, Map | Res) --> exp", parse(model)); + Assert.assertEquals("\n/* XPECT meth ---\nxxx\n---*/\n", expectation(model)); + } + + @Test + public void xpectMLIndentation1() + { + StringBuilder model = new StringBuilder(); + model.append("/* XPECT meth ---\n"); + model.append(" exp\n"); + model.append("--- */\n"); + + StringBuilder parsed = new StringBuilder(); + parsed.append("meth(Res, Map | Res) --> exp"); + + StringBuilder replaced = new StringBuilder(); + replaced.append("/* XPECT meth ---\n"); + replaced.append("xxx\n"); + replaced.append("--- */\n"); + + Assert.assertEquals(parsed.toString(), parse(model.toString())); + Assert.assertEquals(replaced.toString(), expectation(model.toString())); + } + + @Test + public void xpectMLIndentation2() + { + StringBuilder model = new StringBuilder(); + model.append(" /* XPECT meth ---\n"); + model.append(" exp\n"); + model.append(" --- */\n"); + + StringBuilder parsed = new StringBuilder(); + parsed.append("meth(Res, Map | Res) --> exp"); + + StringBuilder replaced = new StringBuilder(); + replaced.append(" /* XPECT meth ---\n"); + replaced.append(" xxx\n"); + replaced.append(" --- */\n"); + + Assert.assertEquals(parsed.toString(), parse(model.toString())); + Assert.assertEquals(replaced.toString(), expectation(model.toString())); + } + + @Test + public void xpectMLIndentation3() + { + StringBuilder model = new StringBuilder(); + model.append("\t/* XPECT meth ---\n"); + model.append("\t exp\n"); + model.append("\t--- */\n"); + + StringBuilder parsed = new StringBuilder(); + parsed.append("meth(Res, Map | Res) --> exp"); + + StringBuilder replaced = new StringBuilder(); + replaced.append("\t/* XPECT meth ---\n"); + replaced.append("\txxx\n"); + replaced.append("\t--- */\n"); + + Assert.assertEquals(parsed.toString(), parse(model.toString())); + Assert.assertEquals(replaced.toString(), expectation(model.toString())); + } + + @Test + public void xpectMLIndentation4() + { + StringBuilder model = new StringBuilder(); + model.append("\t /* XPECT meth ---\n"); + model.append("\t tree {\n"); + model.append("\t node\n"); + model.append("\t }\n"); + model.append("\t --- */\n"); + + StringBuilder parsed = new StringBuilder(); + parsed.append("meth(Res, Map | Res) --> tree {\\n node\\n }"); + + StringBuilder replaced = new StringBuilder(); + replaced.append("\t /* XPECT meth ---\n"); + replaced.append("\t xxx\n"); + replaced.append("\t --- */\n"); + + Assert.assertEquals(parsed.toString(), parse(model.toString())); + Assert.assertEquals(replaced.toString(), expectation(model.toString())); + } + + @Test + public void xpectMLOffsetParameter1() + { + String model = "/* XPECT offsetMeth at foo --> exp \n*/ Bar: val='foo';"; + Assert.assertEquals("offsetMeth(offset at '*/ !Bar', myOffset at 'l='!foo')", params(model)); + } + + @Test + public void xpectMLOffsetParameter2() + { + String model = "/* XPECT offsetMeth at fo!o --> exp \n*/ Bar: val='foo';"; + Assert.assertEquals("offsetMeth(offset at '*/ !Bar', myOffset at ''fo!o';')", params(model)); + } + + @Test + public void xpectSLParameterText() + { + String model = "// XPECT optionalMeth text foo"; + Assert.assertEquals("optionalMeth(offset at 'o\\n !foo', myText = foo)", params(model)); + } + + @Test + public void xpectSLParameterString() + { + String model = "// XPECT optionalMeth string 'foo'"; + Assert.assertEquals("optionalMeth(offset at ''\\n !foo', myString = foo)", params(model)); + } + + @Test + public void xpectSLParameterInt() + { + String model = "// XPECT optionalMeth int 123"; + Assert.assertEquals("optionalMeth(offset at '3\\n !foo', myInt = 123)", params(model)); + } + + @Test + public void xpectSLParameterId() + { + String model = "// XPECT optionalMeth id foo"; + Assert.assertEquals("optionalMeth(offset at 'o\\n !foo', myId = foo)", params(model)); + } + + @Test + public void xpectSLParameterAll() + { + String model = "// XPECT optionalMeth text abc int 123 string 'str' id foo"; + Assert.assertEquals("optionalMeth(offset at 'o\\n !foo', myText = abc, myInt = 123, myString = str, myId = foo)", + params(model)); + } + + @Test + public void xpectSLParameterWithExp() + { + String model = "// XPECT optionalMeth text abc --> exp\n"; + Assert.assertEquals("optionalMeth(Res, Map | Res) --> exp", parse(model)); + Assert.assertEquals("optionalMeth(offset at '\\n\\n !foo', myText = abc)", params(model)); + } + + @Test + public void xpectMLParameterWithExp() + { + String model = "/* XPECT optionalMeth text abc ---\n exp\n--- */"; + Assert.assertEquals("optionalMeth(Res, Map | Res) --> exp", parse(model)); + Assert.assertEquals("optionalMeth(offset at '/\\n !foo', myText = abc)", params(model)); + } + + @Test + public void xpectImport() + { + String model = "// XPECT_IMPORT foo/bar_baz-file.ext\n"; + Assert.assertEquals("import foo/bar_baz-file.ext", parse(model)); + } + + @Test + public void xpectMulti() + { + StringBuilder model = new StringBuilder(); + model.append("// XPECT_IMPORT foo.ext\n"); + model.append("// XPECT meth ---\n"); + model.append("// XPECT meth ---\n"); + model.append("/* XPECT optionalMeth text abc ---\n exp\n--- */\n"); + + StringBuilder parsed = new StringBuilder(); + parsed.append("import foo.ext\n"); + parsed.append("meth(Res, Map | Res)\n"); + parsed.append("meth(Res, Map | Res)\n"); + parsed.append("optionalMeth(Res, Map | Res) --> exp"); + + Assert.assertEquals(parsed.toString(), parse(model.toString())); + } + +} diff --git a/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/XtextInjectorProvider.java b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/XtextInjectorProvider.java new file mode 100644 index 000000000..4c925ceb1 --- /dev/null +++ b/tests/org.eclipse.xtext.junit4.tests/src/org/eclipse/xtext/junit4/parameterized/XtextInjectorProvider.java @@ -0,0 +1,42 @@ +package org.eclipse.xtext.junit4.parameterized; + +import org.eclipse.xtext.XtextStandaloneSetup; +import org.eclipse.xtext.junit4.GlobalRegistries; +import org.eclipse.xtext.junit4.IInjectorProvider; +import org.eclipse.xtext.junit4.IRegistryConfigurator; +import org.eclipse.xtext.junit4.GlobalRegistries.GlobalStateMemento; + +import com.google.inject.Injector; + +public class XtextInjectorProvider implements IInjectorProvider, IRegistryConfigurator +{ + + protected GlobalStateMemento globalStateMemento; + protected Injector injector; + + static + { + GlobalRegistries.initializeDefaults(); + } + + public Injector getInjector() + { + if (injector == null) + { + this.injector = new XtextStandaloneSetup().createInjectorAndDoEMFRegistration(); + } + return injector; + } + + public void restoreRegistry() + { + globalStateMemento.restoreGlobalState(); + } + + public void setupRegistry() + { + globalStateMemento = GlobalRegistries.makeCopyOfGlobalState(); + if (injector != null) + new XtextStandaloneSetup().register(injector); + } +} \ No newline at end of file