/*******************************************************************************
* Copyright (c) 2011 Sebastian Benz 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
*
* Contributors:
* Sebastian Benz - initial API and implementation
*******************************************************************************/
package org.xrepl.tests;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Iterators.filter;
import static com.google.common.collect.Iterators.transform;
import static com.google.common.collect.Lists.newArrayList;
import static org.eclipse.xtext.util.SimpleAttributeResolver.NAME_RESOLVER;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.Constants;
import org.eclipse.xtext.xbase.interpreter.IExpressionInterpreter;
import org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter;
import org.junit.Test;
import org.xrepl.DefaultEvaluator;
import org.xrepl.XreplResourceSetProvider;
import org.xrepl.xscript.XscriptInterpreter;
import com.google.common.base.Function;
import com.google.inject.Inject;
import com.google.inject.name.Named;
public class EvaluatorTest extends AbstractXScriptTest {
private DefaultEvaluator evaluator;
private ResourceSet resourceSet;
@Inject
private XscriptInterpreter interpreter;
@Inject
@Named(Constants.FILE_EXTENSIONS)
private String fileExtension;
@Inject
private XreplResourceSetProvider resourceSetProvider;
@Override
public void setUp() throws Exception {
super.setUp();
interpreter.setClassLoader(getClass().getClassLoader());
evaluator = new DefaultEvaluator(interpreter, resourceSetProvider, fileExtension);
resourceSet = resourceSetProvider.get();
}
@Test public void shouldReportSyntaxErrors() {
assertThat(evaluator.canEvaluate("1 + 1"), is(true));
assertThat(evaluator.canEvaluate("1 &? 2"), is(false));
}
@Test public void shouldEvaluteAssignments() throws Throwable {
assertThat(evalToString("var x = 3"), is("3"));
}
@Test public void shouldAllowFeatureCalls() throws Throwable {
assertThat(evalToString("'literal'.toUpperCase()"), is("LITERAL"));
}
@Test public void shouldEvaluateUseStatement() throws Throwable {
assertThat(evalToString("import 'http://www.eclipse.org/emf/2002/Ecore'"),
is(EcorePackage.eINSTANCE.toString()));
}
@Test public void shouldReturnErrorIfPackageNotFoundUseStatement()
throws Throwable {
assertThat(
evalutionException("import 'NotExistingPackage'"),
is(instanceOf(Exception.class)));
}
@Test public void shouldKeepVariableDeclarations() throws Throwable {
eval("var x = 'result'");
eval("var y = x");
assertThat(evalToString("y"), is("result"));
}
private Throwable evalutionException(String string) {
try {
eval(string);
return null;
} catch (Throwable e) {
return e;
}
}
@Test public void shouldAllowCreationOfObjects() throws Throwable {
assertThat(evalToString("new String('aString')"),
is("aString"));
}
@Test public void shouldAllowCreationOfEObjects() throws Throwable {
evalToString("import 'http://www.eclipse.org/emf/2002/Ecore'");
assertThat(eval("var newEPackage = create EPackage"),
is(instanceOf(EPackage.class)));
}
@Test public void shouldAddTwoIntegers() throws Throwable {
assertThat((Integer) eval("1 + 1"), is(2));
}
@Test public void shouldCompareTwoValues() throws Throwable {
assertThat((Boolean) eval("1 == 1"), is(true));
}
@Test public void shouldAllowSettingAttributes() throws Throwable {
eval("import 'http://www.eclipse.org/emf/2002/Ecore'");
eval("var aPackage = create EPackage");
eval("aPackage.name = 'test'");
assertThat(evalToString("aPackage.name"), is("test"));
}
@Test
public void shouldSupportClojures() throws Throwable {
eval("var list = new java.util.ArrayList()");
eval("list.add('a')");
eval("list = list.map(e | e.toString.toUpperCase)");
assertThat(evalToString("list"), is("[A]"));
}
@Test public void shouldAllowSettingAttributesViaSetMethod() throws Throwable {
evalToString("import 'http://www.eclipse.org/emf/2002/Ecore'");
eval("var aPackage = create EPackage");
eval("aPackage.setName('test')");
assertThat(evalToString("aPackage.name"), is("test"));
}
@SuppressWarnings("restriction")
@Test public void shouldRemoveElementsFromResourceOnException()
throws Throwable {
try {
eval("{" + " var x = 'shouldBeRemovedAfterException' "
+ " null.toString" + "}");
fail("no exception");
} catch (Exception e) {
// expected
}
eval("'This triggers a new evaluation'");
assertThat(resourceSet(), not(hasItem("x")));
}
@Test public void shouldFullyResetTheInterpreter() throws Throwable {
eval("var x = 'aVariable'");
eval("'another input'");
evaluator.reset();
assertFalse("Variable is still in scope", evaluator.canEvaluate("x"));
}
@SuppressWarnings("restriction")
@Test public void shouldKeepNonEvaluatorResources() throws Throwable {
Resource resource = resourceSet.createResource(URI
.createFileURI("shouldBeKept.ecore"));
eval("var x = 'aVariable'");
eval("'another input'");
evaluator.reset();
assertThat(resourceSet.getResources(), hasItem(resource));
}
@Test public void shouldResetEvaluationContext() throws Throwable {
eval("var x = 'aVariable'");
evaluator.reset();
assertThat(evalutionException("var x = 'aVariable'"), is(nullValue()));
}
@Test public void shouldSupportJavaImports() throws Throwable {
eval("import java.util.ArrayList");
assertThat(eval("new ArrayList()"), is(ArrayList.class));
}
@Test public void shouldSupportJavaImportsWithWildCard() throws Throwable {
eval("import java.util.*");
assertThat(eval("new ArrayList()"), is(ArrayList.class));
}
@Test public void shouldSupportJavaQualifiedNames() throws Throwable {
assertThat(eval("new java.util.ArrayList()"), is(ArrayList.class));
}
/*
@Test public void shouldSupportStaticJavaImportsWithWildCard() throws Throwable {
eval("import static java.lang.Math.*");
assertThat(eval("random()"), is(double.class));
}
@Test public void shouldSupportStaticJavaImports() throws Throwable {
assertThat(eval("Math.random()"), is(double.class));
}
@Test public void shouldSupportStaticJavaImportsByQualifiedName() throws Throwable {
eval("import static java.lang.Math.random");
assertThat(eval("random()"), is(double.class));
}
@Test public void shouldSupportStaticJavaImportsByQualifiedName2() throws Throwable {
assertThat(eval("java.lang.Math.random()"), is(double.class));
}
@Test public void shouldSupportStaticJavaImportsByQualifiedNameWithParams() throws Throwable {
assertThat(eval("java.util.Arrays.asList('x', 'y')"), is(List.class));
}
*/
private Iterable<String> resourceSet() {
TreeIterator<Notifier> allContents = resourceSet.getAllContents();
Iterator<String> allNames = transform(allContents,
new Function<Notifier, String>() {
public String apply(Notifier from) {
if (from instanceof EObject) {
String name = NAME_RESOLVER.apply((EObject) from);
return name;
}
return null;
}
});
return newArrayList(filter(allNames, notNull()));
}
private String evalToString(String toEvaluate) throws Throwable {
Object result = eval(toEvaluate);
return result != null ? result.toString() : "null";
}
private Object eval(String toEvaluate) throws Throwable {
return evaluator.evaluate(toEvaluate);
}
}