aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java
diff options
context:
space:
mode:
authorGravatar Florian Weikert <fwe@google.com>2015-07-01 14:52:30 +0000
committerGravatar Florian Weikert <fwe@google.com>2015-07-01 15:23:25 +0000
commit28da3658dd620879589badac61153fde39dcca8a (patch)
treed58120c4d3f272e8ee6956f9873677de3ec0f468 /src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java
parentf07e5446f93e89acd84bef9e5dad68286d0fb7f4 (diff)
MethodLibraryTest: Implemented an alternative approach that reduces duplicated code and may lead to cleaner tests.
-- MOS_MIGRATED_REVID=97326780
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java')
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java412
1 files changed, 405 insertions, 7 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java
index 5749c40e5f..692a338e52 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java
@@ -16,43 +16,89 @@ package com.google.devtools.build.lib.syntax;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import com.google.common.truth.Ordered;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventCollector;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.build.lib.events.util.EventCollectionApparatus;
-import com.google.devtools.build.lib.rules.SkylarkModules;
+import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.testutil.TestMode;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import org.junit.Before;
+import java.util.LinkedList;
import java.util.List;
/**
* Base class for test cases that use parsing and evaluation services.
*/
public class EvaluationTestCase {
-
+
private EventCollectionApparatus eventCollectionApparatus;
+ private PackageFactory factory;
+ private TestMode testMode = TestMode.SKYLARK;
+
protected EvaluationContext evaluationContext;
+ public EvaluationTestCase() {
+ createNewInfrastructure();
+ }
+
@Before
public void setUp() throws Exception {
+ createNewInfrastructure();
+ evaluationContext = newEvaluationContext();
+ }
+
+ public EvaluationContext newEvaluationContext() throws Exception {
+ if (testMode == null) {
+ throw new IllegalArgumentException(
+ "TestMode is null. Please set a Testmode via setMode() or set the "
+ + "evaluatenContext manually by overriding newEvaluationContext()");
+ }
+
+ return testMode.createContext(getEventHandler(), factory.getEnvironment());
+ }
+
+ protected void createNewInfrastructure() {
eventCollectionApparatus = new EventCollectionApparatus(EventKind.ALL_EVENTS);
+ factory = new PackageFactory(TestRuleClassProvider.getRuleClassProvider());
+ }
+
+ /**
+ * Sets the specified {@code TestMode} and tries to create the appropriate {@code
+ * EvaluationContext}
+ *
+ * @param testMode
+ * @throws Exception
+ */
+ protected void setMode(TestMode testMode) throws Exception {
+ this.testMode = testMode;
evaluationContext = newEvaluationContext();
}
+ protected void enableSkylarkMode() throws Exception {
+ setMode(TestMode.SKYLARK);
+ }
+
+ protected void enableBuildMode() throws Exception {
+ setMode(TestMode.BUILD);
+ }
+
protected EventHandler getEventHandler() {
return eventCollectionApparatus.reporter();
}
- public Environment getEnvironment() {
- return evaluationContext.getEnvironment();
+ protected PackageFactory getFactory() {
+ return factory;
}
- public EvaluationContext newEvaluationContext() throws Exception {
- return SkylarkModules.newEvaluationContext(getEventHandler());
+ public Environment getEnvironment() {
+ return evaluationContext.getEnvironment();
}
-
+
public boolean isSkylark() {
return evaluationContext.isSkylark();
}
@@ -133,4 +179,356 @@ public class EvaluationTestCase {
eventCollectionApparatus.collector().clear();
return this;
}
+
+ /**
+ * Encapsulates a separate test which can be executed by a {@code TestMode}
+ */
+ protected interface Testable {
+ public void run() throws Exception;
+ }
+
+ /**
+ * Base class for test cases that run in specific modes (e.g. Build and/or Skylark)
+ *
+ */
+ protected abstract class ModalTestCase {
+ private final SetupActions setup;
+
+ protected ModalTestCase() {
+ setup = new SetupActions();
+ }
+
+ /**
+ * Allows the execution of several statements before each following test
+ * @param statements The statement(s) to be executed
+ * @return This {@code ModalTestCase}
+ */
+ public ModalTestCase setUp(String... statements) {
+ setup.registerEval(statements);
+ return this;
+ }
+
+ /**
+ * Allows the update of the specified variable before each following test
+ * @param name The name of the variable that should be updated
+ * @param value The new value of the variable
+ * @return This {@code ModalTestCase}
+ */
+ public ModalTestCase update(String name, Object value) {
+ setup.registerUpdate(name, value);
+ return this;
+ }
+
+ /**
+ * Evaluates two parameters and compares their results.
+ * @param statement The statement to be evaluated
+ * @param expectedEvalString The expression of the expected result
+ * @return This {@code ModalTestCase}
+ * @throws Exception
+ */
+ public ModalTestCase testEval(String statement, String expectedEvalString) throws Exception {
+ runTest(createComparisonTestable(statement, expectedEvalString, true));
+ return this;
+ }
+
+ /**
+ * Evaluates the given statement and compares its result to the expected object
+ * @param statement
+ * @param expected
+ * @return This {@code ModalTestCase}
+ * @throws Exception
+ */
+ public ModalTestCase testStatement(String statement, Object expected) throws Exception {
+ runTest(createComparisonTestable(statement, expected, false));
+ return this;
+ }
+
+ /**
+ * Evaluates the given statement and compares its result to the collection of expected objects
+ * without considering their order
+ * @param statement The statement to be evaluated
+ * @param items The expected items
+ * @return This {@code ModalTestCase}
+ * @throws Exception
+ */
+ public ModalTestCase testCollection(String statement, Object... items) throws Exception {
+ runTest(collectionTestable(statement, false, items));
+ return this;
+ }
+
+ /**
+ * Evaluates the given statement and compares its result to the collection of expected objects
+ * while considering their order
+ * @param statement The statement to be evaluated
+ * @param items The expected items, in order
+ * @return This {@code ModalTestCase}
+ * @throws Exception
+ */
+ public ModalTestCase testExactOrder(String statement, Object... items) throws Exception {
+ runTest(collectionTestable(statement, true, items));
+ return this;
+ }
+
+ /**
+ * Evaluates the given statement and checks whether the given error message appears
+ * @param expectedError The expected error message
+ * @param statements The statement(s) to be evaluated
+ * @return This ModalTestCase
+ * @throws Exception
+ */
+ public ModalTestCase testIfExactError(String expectedError, String... statements)
+ throws Exception {
+ runTest(errorTestable(true, expectedError, statements));
+ return this;
+ }
+
+ /**
+ * Evaluates the given statement and checks whether an error that contains the expected message
+ * occurs
+ * @param expectedError
+ * @param statements
+ * @return This ModalTestCase
+ * @throws Exception
+ */
+ public ModalTestCase testIfErrorContains(String expectedError, String... statements)
+ throws Exception {
+ runTest(errorTestable(false, expectedError, statements));
+ return this;
+ }
+
+ /**
+ * Looks up the value of the specified variable and compares it to the expected value
+ * @param name
+ * @param expected
+ * @return This ModalTestCase
+ * @throws Exception
+ */
+ public ModalTestCase testLookup(String name, Object expected) throws Exception {
+ runTest(createLookUpTestable(name, expected));
+ return this;
+ }
+
+ /**
+ * Creates a Testable that checks whether the evaluation of the given statement leads to the
+ * expected error
+ * @param statements
+ * @param error
+ * @param exactMatch If true, the error message has to be identical to the expected error
+ * @return An instance of Testable that runs the error check
+ */
+ protected Testable errorTestable(final boolean exactMatch, final String error,
+ final String... statements) {
+ return new Testable() {
+ @Override
+ public void run() throws Exception {
+ if (exactMatch) {
+ checkEvalError(error, statements);
+ } else {
+ checkEvalErrorContains(error, statements);
+ }
+ }
+ };
+ }
+
+ /**
+ * Creates a testable that checks whether the evaluation of the given statement leads to a list
+ * that contains exactly the expected objects
+ * @param statement The statement to be evaluated
+ * @param ordered Determines whether the order of the elements is checked as well
+ * @param expected Expected objects
+ * @return An instance of Testable that runs the check
+ */
+ protected Testable collectionTestable(
+ final String statement, final boolean ordered, final Object... expected) {
+ return new Testable() {
+ @Override
+ public void run() throws Exception {
+ Ordered tmp = assertThat((Iterable<?>) eval(statement)).containsExactly(expected);
+
+ if (ordered) {
+ tmp.inOrder();
+ }
+ }
+ };
+ }
+
+ /**
+ * Creates a testable that compares the evaluation of the given statement to a specified result
+ *
+ * @param statement The statement to be evaluated
+ * @param expected Either the expected object or an expression whose evaluation leads to the
+ * expected object
+ * @param expectedIsExpression Signals whether {@code expected} is an object or an expression
+ * @return An instance of Testable that runs the comparison
+ */
+ protected Testable createComparisonTestable(
+ final String statement, final Object expected, final boolean expectedIsExpression) {
+ return new Testable() {
+ @Override
+ public void run() throws Exception {
+ Object actual = eval(statement);
+
+ // Prints the actual object instead of evaluating the expected expression
+ if (expectedIsExpression) {
+ actual = Printer.repr(actual, '\'');
+ }
+
+ assertThat(actual).isEqualTo(expected);
+ }
+ };
+ }
+
+ /**
+ * Creates a Testable that looks up the given variable and compares its value to the expected
+ * value
+ * @param name
+ * @param expected
+ * @return An instance of Testable that does both lookup and comparison
+ */
+ protected Testable createLookUpTestable(final String name, final Object expected) {
+ return new Testable() {
+ @Override
+ public void run() throws Exception {
+ assertThat(lookup(name)).isEqualTo(expected);
+ }
+ };
+ }
+
+ /**
+ * Executes the given Testable
+ * @param testable
+ * @throws Exception
+ */
+ protected void runTest(Testable testable) throws Exception {
+ run(new TestableDecorator(setup, testable));
+ }
+
+ protected abstract void run(Testable testable) throws Exception;
+ }
+
+ /**
+ * A simple decorator that allows the execution of setup actions before running
+ * a {@code Testable}
+ */
+ class TestableDecorator implements Testable {
+ private final SetupActions setup;
+ private final Testable decorated;
+
+ public TestableDecorator(SetupActions setup, Testable decorated) {
+ this.setup = setup;
+ this.decorated = decorated;
+ }
+
+ /**
+ * Executes all stored actions and updates plus the actual {@code Testable}
+ */
+ @Override
+ public void run() throws Exception {
+ setup.executeAll();
+ decorated.run();
+ }
+ }
+
+ /**
+ * A container for collection actions that should be executed before a test
+ */
+ class SetupActions {
+ private List<Testable> setup;
+
+ public SetupActions() {
+ setup = new LinkedList<>();
+ }
+
+ /**
+ * Registers a variable that has to be updated before a test
+ *
+ * @param name
+ * @param value
+ */
+ public void registerUpdate(final String name, final Object value) {
+ setup.add(new Testable() {
+ @Override
+ public void run() throws Exception {
+ EvaluationTestCase.this.update(name, value);
+ }
+ });
+ }
+
+ /**
+ * Registers a statement for evaluation prior to a test
+ *
+ * @param statements
+ */
+ public void registerEval(final String... statements) {
+ setup.add(new Testable() {
+ @Override
+ public void run() throws Exception {
+ EvaluationTestCase.this.eval(statements);
+ }
+ });
+ }
+
+ /**
+ * Executes all stored actions and updates
+ * @throws Exception
+ */
+ public void executeAll() throws Exception {
+ for (Testable testable : setup) {
+ testable.run();
+ }
+ }
+ }
+
+ /**
+ * A class that executes each separate test in both modes (Build and Skylark)
+ */
+ protected class BothModesTest extends ModalTestCase {
+ public BothModesTest() {}
+
+ /**
+ * Executes the given Testable in both Build and Skylark mode
+ */
+ @Override
+ protected void run(Testable testable) throws Exception {
+ enableSkylarkMode();
+ try {
+ testable.run();
+ } catch (Exception e) {
+ throw new Exception("While in Skylark mode", e);
+ }
+
+ enableBuildMode();
+ try {
+ testable.run();
+ } catch (Exception e) {
+ throw new Exception("While in Build mode", e);
+ }
+ }
+ }
+
+ /**
+ * A class that runs all tests in Build mode
+ */
+ protected class BuildTest extends ModalTestCase {
+ public BuildTest() {}
+
+ @Override
+ protected void run(Testable testable) throws Exception {
+ enableBuildMode();
+ testable.run();
+ }
+ }
+
+ /**
+ * A class that runs all tests in Skylark mode
+ */
+ protected class SkylarkTest extends ModalTestCase {
+ public SkylarkTest() {}
+
+ @Override
+ protected void run(Testable testable) throws Exception {
+ enableSkylarkMode();
+ testable.run();
+ }
+ }
}