diff --git a/com.avaloq.tools.ddk.check.core.test/src/com/avaloq/tools/ddk/check/test/core/CheckCoreTestSuite.java b/com.avaloq.tools.ddk.check.core.test/src/com/avaloq/tools/ddk/check/test/core/CheckCoreTestSuite.java index d2950d0e2..42aa8b601 100644 --- a/com.avaloq.tools.ddk.check.core.test/src/com/avaloq/tools/ddk/check/test/core/CheckCoreTestSuite.java +++ b/com.avaloq.tools.ddk.check.core.test/src/com/avaloq/tools/ddk/check/test/core/CheckCoreTestSuite.java @@ -22,6 +22,9 @@ import com.avaloq.tools.ddk.check.core.test.IssueCodeToLabelMapGenerationTest; import com.avaloq.tools.ddk.check.core.test.ProjectBasedTests; import com.avaloq.tools.ddk.check.formatting.CheckFormattingTest; +import com.avaloq.tools.ddk.check.validation.CheckApiAccessValidationsTest; +import com.avaloq.tools.ddk.check.validation.CheckJavaValidatorUtilTest; +import com.avaloq.tools.ddk.check.validation.CheckValidationTest; /** @@ -34,10 +37,13 @@ BasicModelTest.class, BugAig830.class, CheckScopingTest.class, + CheckValidationTest.class, + CheckJavaValidatorUtilTest.class, IssueCodeToLabelMapGenerationTest.class, ProjectBasedTests.class, BugAig1314.class, BugDsl27.class, + CheckApiAccessValidationsTest.class, CheckFormattingTest.class // @Format-On }) diff --git a/com.avaloq.tools.ddk.check.core.test/src/com/avaloq/tools/ddk/check/validation/CheckJavaValidatorUtilTest.java b/com.avaloq.tools.ddk.check.core.test/src/com/avaloq/tools/ddk/check/validation/CheckJavaValidatorUtilTest.java index f02660584..e7cc376e3 100644 --- a/com.avaloq.tools.ddk.check.core.test/src/com/avaloq/tools/ddk/check/validation/CheckJavaValidatorUtilTest.java +++ b/com.avaloq.tools.ddk.check.core.test/src/com/avaloq/tools/ddk/check/validation/CheckJavaValidatorUtilTest.java @@ -29,7 +29,7 @@ @InjectWith(CheckUiInjectorProvider.class) @ExtendWith(InjectionExtension.class) @SuppressWarnings("nls") -class CheckJavaValidatorUtilTest { +public class CheckJavaValidatorUtilTest { // assertion messages. private static final String STARTS_WITH_LOWER_CASE = "starts with lower case"; diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/AbstractSystemTest.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/AbstractSystemTest.java deleted file mode 100644 index eb050d62b..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/AbstractSystemTest.java +++ /dev/null @@ -1,342 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core; - -import java.util.List; -import java.util.Set; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.internal.AssumptionViolatedException; -import org.junit.rules.TestWatcher; -import org.junit.rules.Verifier; -import org.junit.runner.Description; - - -/** - * Abstract base class for system tests. - */ -@SuppressWarnings({"restriction", "nls"}) -public abstract class AbstractSystemTest implements TestStepListener { - // The particular suppress warnings annotation is needed because we WANT to have a unique logger for each of the subclasses - @SuppressWarnings("PMD.LoggerIsNotStaticFinal") - private final Logger logger = LogManager.getLogger(getClass()); - private int stepCounter = 1; - private final MultipleTestProblems multipleTestProblems = new MultipleTestProblems(); - - /** The {@link TestPlan}, that holds all setup- and test-steps of the current test. */ - private final TestPlan testPlan = TestPlan.create(); - - /** The {@link TestPlan}, that holds all setup- and test-steps, which are actually executed. */ - private TestPlan executedTestPlan = TestPlan.create(); - - private static TestPlan previousTestPlan; - private static boolean lastExecutedTestFailed; - private static boolean lastExecutedTestWasSystemTest; - private boolean executingSystemTest; - - /** - * Indicates the current state of this {@link AbstractSystemTest}. - */ - private enum TestRunState { - SETUP, - TEST, - TEARDOWN - } - - private TestRunState testRunState = TestRunState.SETUP; - - /** - * Returns the logger for this test class. - * - * @return the logger for this test class. - */ - protected Logger log() { - return logger; - } - - /** - * Registers a junit {@link Verifier} rule, which is called after a test passed successfully, and checks additional possible error sources. - */ - @Rule - // CHECKSTYLE:OFF - public Verifier abstractSystemTestVerifier = new Verifier() { - // CHECKSTYLE:ON - @Override - protected void verify() { - // check if any test problem occurred - multipleTestProblems.assertEmpty(); - } - }; - - /** - * Enables support for unresolved bug tests. - * This declaration must textually be located after the {@code abstractSystemTestVerifier} above. - * This influences the nesting of rules in the chain and bug test aware rule depends on exceptions thrown by the Verifier to succeed. - */ - @Rule - // CHECKSTYLE:OFF - public BugTestAwareRule bugTestRule = BugTestAwareRule.getInstance(); - // CHECKSTYLE:ON - - @Rule - // CHECKSTYLE:OFF - public TestWatcher testWatchman = new TestWatcher() { - // CHECKSTYLE:ON - - @Override - public void starting(final Description description) { - logger.info(description.getMethodName() + " started."); - } - - @Override - public void succeeded(final Description description) { - if (multipleTestProblems.hasProblems()) { - logger.info(description.getMethodName() + " failed."); - } else { - logger.info(description.getMethodName() + " succeeded."); - } - } - - @Override - public void failed(final Throwable e, final Description description) { - if (e instanceof AssumptionViolatedException) { - logger.warn(description.getMethodName() + " skipped because of failing assumption: " + e.toString()); - } else { - logger.warn(description.getMethodName() + " failed."); - } - } - - @Override - public void finished(final Description description) { - } - }; - - /** - * Setup of the system test. - * Implementations need to specify the setup steps in this method and also provide the @Before annotation. - */ - @Before - public void setUp() { - AbstractTestStep.registerTestStepListener(this); - } - - /** - * Executes the test plan starting with the setup steps and subsequently executing the test steps. - */ - protected final void executeTestPlan() { - cleanUpPreviousTestPlan(); - // If current test is no system Test or first executed test, current test plan does not have to be changed - if (!executingSystemTest || previousTestPlan == null || !lastExecutedTestWasSystemTest || lastExecutedTestFailed) { - executedTestPlan = testPlan; - } else { - executedTestPlan = TestPlan.createExecutableTestPlan(testPlan, previousTestPlan); - } - previousTestPlan = testPlan; - try { - AbstractTestStep.setCheckPreconditions(true); - AbstractTestStep.setCheckPostconditions(true); - executedTestPlan.getCompoundSetupStep().run(); - AbstractTestStep.setCheckPreconditions(true); - AbstractTestStep.setCheckPostconditions(true); - testRunState = TestRunState.TEST; - executedTestPlan.getCompoundTestStep().run(); - lastExecutedTestFailed = false; - // CHECKSTYLE:OFF - } catch (Throwable t) { - // CHECKSTYLE:ON - lastExecutedTestFailed = true; - addTestProblem(t); - } finally { - lastExecutedTestWasSystemTest = executingSystemTest; - testRunState = TestRunState.TEARDOWN; // cannot be put at start of tearDown(), because subclasses may override that method. - } - } - - /** - * Undo the steps of the previous test, that are not needed by the current test. - */ - private void cleanUpPreviousTestPlan() { - // No clean up is necessary if one of the following is true: - // - there was no previous test - // - the last test failed (then everything is cleaned up in the previous test) - // - the last test was not a "system test" (then the test cleans up by itself) - if (previousTestPlan == null || lastExecutedTestFailed || !lastExecutedTestWasSystemTest) { - return; - } - TestPlan undoTestPlan = TestPlan.createUndoTestPlan(testPlan, previousTestPlan, executingSystemTest); - AbstractTestStep.setCheckPreconditions(true); - AbstractTestStep.setCheckPostconditions(true); - try { - undoTestPlan.getCompoundTestStep().runIgnoreAndContinue(); - // CHECKSTYLE:CHECK-OFF IllegalCatch - } catch (Throwable t) { - // CHECKSTYLE:CHECK-ON IllegalCatch - // ignore problems during tear down - } - try { - undoTestPlan.getCompoundSetupStep().runIgnoreAndContinue(); - // CHECKSTYLE:CHECK-OFF IllegalCatch - } catch (Throwable t) { - // CHECKSTYLE:CHECK-ON IllegalCatch - // ignore problems during tear down - } - - } - - /** - * Executes a system test plan. - */ - protected final void executeSystemTestPlan() { - executingSystemTest = true; - executeTestPlan(); - } - - /** - * Cleans up the workbench state after a test. - *

- * Note: Undoes all test and setup steps in the correct order, if the test is not marked as a System Test. - *

- */ - @After - public void tearDown() { - try { - AbstractTestStep.setCheckPreconditions(true); - AbstractTestStep.setCheckPostconditions(true); - if (!executingSystemTest) { - try { - executedTestPlan.getCompoundTestStep().undo(); - // CHECKSTYLE:CHECK-OFF IllegalCatch - } catch (Throwable t) { - // CHECKSTYLE:CHECK-ON IllegalCatch - // ignore problems during tear down - } - try { - executedTestPlan.getCompoundSetupStep().undo(); - // CHECKSTYLE:CHECK-OFF IllegalCatch - } catch (Throwable t) { - // CHECKSTYLE:CHECK-ON IllegalCatch - // ignore problems during tear down - } - } else if (lastExecutedTestFailed) { - Set filter = executedTestPlan.getAllExecutedSteps(); - filter.addAll(TestPlan.getAllStepsWithPreExistingTestEntities(previousTestPlan, testPlan)); - TestPlan undoTestPlan = TestPlan.createUndoStepsTestPlan(TestPlan.createReverseTestPlan(TestPlan.createFilteredTestPlan(testPlan, filter))); - try { - undoTestPlan.getCompoundTestStep().runIgnoreAndContinue(); - // CHECKSTYLE:CHECK-OFF IllegalCatch - } catch (Throwable t) { - // CHECKSTYLE:CHECK-ON IllegalCatch - // ignore problems during tear down - } - try { - undoTestPlan.getCompoundSetupStep().runIgnoreAndContinue(); - // CHECKSTYLE:CHECK-OFF IllegalCatch - } catch (Throwable t) { - // CHECKSTYLE:CHECK-ON IllegalCatch - // ignore problems during tear down - } - } - } finally { - AbstractTestStep.removeTestStepListener(this); - } - } - - /** - * Adds a step as setup step. Setup steps are run before the test starts. Therefore, it is not possible to add a setup step after adding a test step. - * - * @param - * the type of the {@link AbstractStep} - * @param setupStep - * the step to append to the list of setup steps - * @return the added {@link AbstractStep} - */ - protected T addSetupStep(final T setupStep) { - return testPlan.addSetupStep(setupStep); - } - - /** - * Adds a step as test step. - * - * @param - * the type of the {@link AbstractStep} - * @param testStep - * the step to append to the list of test steps - * @return the added {@link AbstractStep} - */ - protected T addTestStep(final T testStep) { - return testPlan.addTestStep(testStep); - } - - /** - * Adds a new test problem. - * - * @param problem - * the new {@link Throwable} to add - */ - protected void addTestProblem(final Throwable problem) { - multipleTestProblems.addProblem(problem); - logger.error("Error: " + problem.getLocalizedMessage()); - } - - /** - * Adds a new test problem with a message. - * - * @param message - * the message of the problem - */ - protected void addTestProblem(final String message) { - addTestProblem(new AssertionError(message)); - } - - /** - * Adds new test problems. - * - * @param problems - * the new {@link Throwable}s to add - */ - protected void addTestProblems(final List problems) { - for (Throwable problem : problems) { - addTestProblem(problem); - } - } - - @Override - public void stepStateChanged(final AbstractTestStep testStep, final TestStepState testStepState, final Throwable throwable) { - switch (testStepState) { - case START: - switch (testRunState) { - case SETUP: - logger.info("Setup " + stepCounter++ + ": " + testStep.getName()); - break; - case TEST: - logger.info("Test " + stepCounter++ + ": " + testStep.getName()); - break; - case TEARDOWN: - logger.info("Teardown: " + testStep.getName()); - break; - } - break; - case ERRORED: - logger.error("ERRORED", throwable); - break; - case FAILED: - logger.error("FAILED", throwable); - break; - default: - break; - } - } - -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/BugTestAwareRule.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/BugTestAwareRule.java deleted file mode 100644 index 75b0cece7..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/BugTestAwareRule.java +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - - -/** - * This {@link TestRule} implementation changes the behavior for unresolved bug tests. - *

- * The behavior for at test that is annotated with {@link BugTest(unresolved=true)} is the following: - *

    - *
  • Test evaluation OK results in FAIL ({@link AssertionError})
  • - *
  • Test evaluation FAIL results in OK
  • - *
  • Test evaluation ERROR results in ERROR
  • - *
- *

- *

- * Example for a bug test: - * - *

- * public class TestClass {
- *
- *   @Rule
- *   public BugTestAwareRule rule = BugTestAwareRule.getInstance();
- *
- *   @org.junit.Test
- *   @com.avaloq.tools.ddk.test.core.BugTest(value = "BUG-42", unresolved = true)
- *   public void testMethod() {
- *     org.junit.Assert.fail();
- *   }
- * }
- * 
- *

- * - * @see BugTest - */ -public final class BugTestAwareRule implements TestRule { - - private static final String ERROR_TEST_MUST_FAIL = "The unresolved bug test must fail:"; //$NON-NLS-1$ - /** The singleton instance, or {@code null} if not cached. */ - private static BugTestAwareRule instance; - private static final Object LOCK = new Object(); - - /** - * Creates a new instance of {@link BugTestAwareRule}. - */ - private BugTestAwareRule() { - // prevent instantiation - } - - /** - * Returns a shared singleton instance. - * - * @return a shared instance, never {@code null} - */ - public static BugTestAwareRule getInstance() { - synchronized (LOCK) { - if (instance == null) { - instance = new BugTestAwareRule(); - } - return instance; - } - } - - @Override - public Statement apply(final Statement base, final Description description) { - BugTest bugTestAnnotation = description.getAnnotation(BugTest.class); - if (bugTestAnnotation == null && description.getTestClass() != null) { - bugTestAnnotation = description.getTestClass().getAnnotation(BugTest.class); - } - if (bugTestAnnotation != null && bugTestAnnotation.unresolved()) { - return StatementFactory.createResultInvertingStatement(ERROR_TEST_MUST_FAIL, base, description); - - } else { - return base; - } - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/IssueAwareRule.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/IssueAwareRule.java deleted file mode 100644 index 466c59751..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/IssueAwareRule.java +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - - -/** - * This {@link TestRule} implementation changes the behavior for not fixed issues. - *

- * The behavior for at test that is annotated with {@link Issue(fixed = false)} is the following: - *

    - *
  • Test evaluation OK results in FAIL ({@link AssertionError})
  • - *
  • Test evaluation FAIL results in OK
  • - *
  • Test evaluation ERROR results in ERROR
  • - *
- *

- *

- * Example for a issue test: - * - *

- * public class TestClass {
- *
- *   @Rule
- *   public IssueAwareRule rule = IssueAwareRule.getInstance();
- *
- *   @org.junit.Test
- *   @com.avaloq.tools.ddk.test.core.Issue(value = "ISSUE-42", fixed = false)
- *   public void testMethod() {
- *     org.junit.Assert.fail();
- *   }
- * }
- * 
- *

- * - * @see Issue - */ -public final class IssueAwareRule implements TestRule { - - private static final String ERROR_TEST_MUST_FAIL = "The issue test for a not fixed issue must fail:"; //$NON-NLS-1$ - /** The singleton instance, or {@code null} if not cached. */ - private static IssueAwareRule instance; - private static Object lock = new Object(); - - /** - * Creates a new instance of {@link IssueAwareRule}. - */ - private IssueAwareRule() { - // prevent instantiation - } - - /** - * Returns a shared singleton instance. - * - * @return a shared instance, never {@code null} - */ - public static IssueAwareRule getInstance() { - synchronized (lock) { - if (instance == null) { - instance = new IssueAwareRule(); - } - return instance; - } - } - - @Override - public Statement apply(final Statement base, final Description description) { - Issue issueAnnotation = description.getAnnotation(Issue.class); - if (issueAnnotation == null && description.getTestClass() != null) { - issueAnnotation = description.getTestClass().getAnnotation(Issue.class); - } - if (issueAnnotation != null && !issueAnnotation.fixed()) { - return StatementFactory.createResultInvertingStatement(ERROR_TEST_MUST_FAIL, base, description); - } else { - return base; - } - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/LoggingRule.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/LoggingRule.java deleted file mode 100644 index 69558281a..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/LoggingRule.java +++ /dev/null @@ -1,91 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; - - -/** - * A test watcher that logs the start and end of each test, as well as its success or failure. - */ -@SuppressWarnings("nls") -public final class LoggingRule extends TestWatcher { - - private static final Logger LOGGER = LogManager.getLogger(LoggingRule.class); - - /** The singleton instance, or {@code null} if not cached. */ - private static LoggingRule instance; - - private static final Object LOCK = new Object(); - - /** - * Creates a new instance of {@link LoggingRule}. - */ - private LoggingRule() { - // prevent instantiation - } - - /** - * Returns a shared singleton instance. - * - * @return a shared instance, never {@code null} - */ - public static LoggingRule getInstance() { - synchronized (LOCK) { - if (instance == null) { - instance = new LoggingRule(); - } - return instance; - } - } - - /** - * Returns the name of a test to be logged. - * - * @param description - * the description, must not be {@code null} - * @return the description name, never {@code null} - */ - private String getDescriptionName(final Description description) { - return description.getClassName() + '.' + description.getMethodName(); - } - - @Override - public void starting(final Description description) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("STARTING: " + getDescriptionName(description)); - } - } - - @Override - protected void finished(final Description description) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("FINISHED: " + getDescriptionName(description)); - } - } - - @Override - protected void succeeded(final Description description) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("SUCCEEDED: " + getDescriptionName(description)); - } - } - - @Override - protected void failed(final Throwable e, final Description description) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("FAILED: " + getDescriptionName(description)); - } - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/StatementFactory.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/StatementFactory.java deleted file mode 100644 index 4e1291494..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/StatementFactory.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core; - -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - - -/** - * A factory for creating {@link Statement}s. - */ -public final class StatementFactory { - - /** - * Instantiates a new statement factory. - */ - private StatementFactory() { - // Empty constructor to avoid instantiation. - } - - /** - * Creates a {@link Statement} object which inverts the behaviour of {@code base}. - *

- * If the {@code base} statement throws an {@link AssertionError}, the returned statement will catch it and succeed nevertheless. If the {@code base} - * statement succeeds, the returned statement will throw an {@link AssertionError} with the custom error message {@code errorMessage}. - *

- * - * @param errorMessage - * the error message for the new {@link AssertionError}, must not be {@code null} - * @param base - * the base statement which shall be inverted, must not be {@code null} - * @param description - * the description of the test that will be run, must not be {@code null} - * @return the inverted statement, never {@code null} - */ - @SuppressWarnings("nls") - public static Statement createResultInvertingStatement(final String errorMessage, final Statement base, final Description description) { - return new Statement() { - @Override - // CHECKSTYLE:CHECK-OFF IllegalThrowsCheck - public void evaluate() throws Throwable { - // CHECKSTYLE:CHECK-ON IllegalThrowsCheck - try { - base.evaluate(); - } catch (AssertionError exception) { - return; - } - String testCase = description.getClassName() + "." + description.getMethodName(); - throw new AssertionError(errorMessage + " " + testCase); - } - }; - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/TestLabelFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/TestLabelFilter.java deleted file mode 100644 index 2d5c32a45..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/TestLabelFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core; - -import org.apache.commons.lang3.ArrayUtils; -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - - -/** - * A test filter that makes sure to run only those {@link Description}s that are annotated with a specific {@link TestLabel}. - */ -public class TestLabelFilter extends Filter { - private final String label; - - public TestLabelFilter(final String label) { - this.label = label; - } - - @Override - public boolean shouldRun(final Description description) { - TestLabels labels = description.getAnnotation(TestLabels.class); - return labels != null && ArrayUtils.contains(labels.value(), label); - } - - @Override - public String describe() { - return TestLabelFilter.class.getSimpleName(); - } - -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/TestPlan.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/TestPlan.java deleted file mode 100644 index f712ea3a6..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/TestPlan.java +++ /dev/null @@ -1,408 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Set; - -import org.junit.Assert; - -import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; - - -/** - * The test plan containing the setup and test steps and the {@link TestEntityAction}s of a test. - */ -final class TestPlan { - /** A {@link CompoundStep}, which is used to hold the setup steps for this {@link TestPlan}. */ - private final CompoundStep compoundSetupStep = new CompoundStep(); - /** A {@link CompoundStep}, which is used to hold the test steps for this {@link TestPlan}. */ - private final CompoundStep compoundTestStep = new CompoundStep(); - /** The {@link TestEntityAction}s of the test steps of this {@link TestPlan}. */ - private List testEntityActions; - - /** - * Instantiates a new {@link TestPlan} with empty {@link CompoundStep}s. - */ - private TestPlan() { - } - - /** - * Instantiates a new {@link TestPlan} with the given test-steps and setup-steps. - * - * @param setupSteps - * the setup steps, must not be {@code null} - * @param testSteps - * the test steps, must not be {@code null} - */ - private TestPlan(final List setupSteps, final List testSteps) { - compoundSetupStep.addSteps(setupSteps); - compoundTestStep.addSteps(testSteps); - } - - /** - * Returns a new instance of {@link TestPlan} with empty {@link CompoundStep}s. - * - * @return a new test plan instance, never {@code null} - */ - static TestPlan create() { - return new TestPlan(); - } - - /** - * Adds a setup step to this {@link TestPlan} and asserts that it is not added after a test step. - * - * @param - * the generic type - * @param setupStep - * the setup step, must not be {@code null} - * @return the newly added {@link AbstractStep}, never {@code null} - */ - T addSetupStep(final T setupStep) { - Assert.assertTrue("Must not add a setup step after adding a test step.", getCompoundTestStep().getSteps().isEmpty()); //$NON-NLS-1$ - getCompoundSetupStep().addStep(setupStep); - return setupStep; - } - - /** - * Adds a test step to this {@link TestPlan}. - * - * @param - * the generic type - * @param testStep - * the test step, must not be {@code null} - * @return the newly added {@link AbstractStep}, never {@code null} - */ - T addTestStep(final T testStep) { - getCompoundTestStep().addStep(testStep); - return testStep; - } - - /** - * Returns the {@link TestEntityAction}s contained in the given {@link CompoundStep}. - * - * @param compoundStep - * the compound step, must not be {@code null} - * @return the {@link TestEntityAction}s, never {@code null} - */ - private List getTestEntityActions(final CompoundStep compoundStep) { - List allTestEntityActions = new ArrayList(); - for (final AbstractStep step : compoundStep.getSteps()) { - if (step instanceof ITestEntityActionProvider) { - allTestEntityActions.addAll(((ITestEntityActionProvider) step).getTestEntityActions()); - } - } - return allTestEntityActions; - } - - /** - * Returns all {@link TestEntityAction}s contained in the setup and test compound steps. - * - * @return all {@link TestEntityAction}s, never {@code null} - */ - private List getAllTestEntityActions() { - if (testEntityActions == null) { - testEntityActions = getTestEntityActions(compoundSetupStep); - testEntityActions.addAll(getTestEntityActions(compoundTestStep)); - } - return testEntityActions; - } - - /** - * Creates a {@link TestPlan} which contains the previous test steps that need to be undone, before the current test can be executed. - * - * @param testPlan - * current {@link TestPlan}, must not be {@code null} - * @param previousTestPlan - * previous {@link TestPlan}, may be {@code null} - * @param systemTest - * whether a system test plan shall be created - * @return test plan containing previous test steps that need to be undone, never {@code null} - */ - static TestPlan createUndoTestPlan(final TestPlan testPlan, final TestPlan previousTestPlan, final boolean systemTest) { - if (systemTest) { - // If the current and the previous test are system tests, compute the steps to undo. - List setupStepsToUndo = computeStepsToUndo(previousTestPlan.getCompoundSetupStep().getSteps(), testPlan); - List testStepsToUndo = computeStepsToUndo(previousTestPlan.getCompoundTestStep().getSteps(), testPlan); - return new TestPlan(setupStepsToUndo, testStepsToUndo); - } - // else: If current test is not a System Test and the step before was, all steps have to be undone. - List setupStepsToUndo = getAllUndoSteps(previousTestPlan.getCompoundSetupStep().getExecutedSteps()); - Collections.reverse(setupStepsToUndo); - List testStepsToUndo = getAllUndoSteps(previousTestPlan.getCompoundTestStep().getExecutedSteps()); - Collections.reverse(setupStepsToUndo); - return new TestPlan(setupStepsToUndo, testStepsToUndo); - } - - /** - * Returns a set containing all steps of a {@link TestPlan} that need a {@link ITestEntity} that is still available from a previous test. - * - * @param previousTestPlan - * the previous test plan, must not be {@code null} - * @param testPlan - * the test plan, must not be {@code null} - * @return a set of all steps with previously existing test entities, never {@code null} - */ - static Set getAllStepsWithPreExistingTestEntities(final TestPlan previousTestPlan, final TestPlan testPlan) { - Set stepsWithPreExistingTestEntities = new HashSet(); - stepsWithPreExistingTestEntities.addAll(getStepsWithPreExistingEntities(testPlan, previousTestPlan.compoundSetupStep)); - stepsWithPreExistingTestEntities.addAll(getStepsWithPreExistingEntities(testPlan, previousTestPlan.compoundTestStep)); - return stepsWithPreExistingTestEntities; - } - - /** - * Returns a set containing all steps of a {@link CompoundStep} that need a {@link ITestEntity} that is still available from a previous test. - * - * @param testPlan - * the test plan, must not be {@code null} - * @param compoundStep - * the compound step, must not be {@code null} - * @return the steps with pre existing entities, never {@code null} - */ - private static Set getStepsWithPreExistingEntities(final TestPlan testPlan, final CompoundStep compoundStep) { - Set preExistingTestEntities = new HashSet(); - for (final AbstractStep step : compoundStep.getSteps()) { - if (step instanceof ITestEntityActionProvider && testPlan.hasAllTestEntities((ITestEntityActionProvider) step)) { - preExistingTestEntities.add(step); - } - } - return preExistingTestEntities; - } - - /** - * Utility method that checks if the {@link TestPlan} contains all {@link ITestEntity}s of the given {@link ITestEntityActionProvider}. - * - * @param step - * the step, must not be {@code null} - * @return {@code true}, if this {@link TestPlan} contains all {@link ITestEntity}s - */ - private boolean hasAllTestEntities(final ITestEntityActionProvider step) { - boolean hasAllEntites = false; - for (TestEntityAction action : step.getTestEntityActions()) { - if (hasTestEntity(action)) { - hasAllEntites = true; - } else { - return false; - } - } - return hasAllEntites; - } - - /** - * Returns all executed steps of the given {@link TestPlan}. - * - * @return all executed steps, never {@code null} - */ - Set getAllExecutedSteps() { - Set executedSteps = new HashSet(); - executedSteps.addAll(getCompoundSetupStep().getExecutedSteps()); - executedSteps.addAll(getCompoundTestStep().getExecutedSteps()); - return executedSteps; - } - - /** - * Creates a {@link TestPlan} that contains only the {@link AbstractStep}s contained in the filter. - * - * @param testPlan - * the test plan, must not be {@code null} - * @param filter - * the filter, must not be {@code null} - * @return the new filtered test plan, never {@code null} - */ - static TestPlan createFilteredTestPlan(final TestPlan testPlan, final Collection filter) { - List filteredSetupSteps = filterCompoundStep(filter, testPlan.getCompoundSetupStep()); - List filteredTestSteps = filterCompoundStep(filter, testPlan.getCompoundTestStep()); - return new TestPlan(filteredSetupSteps, filteredTestSteps); - } - - /** - * Creates a {@link TestPlan} where all the {@link AbstractStep}s of the {@link CompoundStep}s of the given {@link TestPlan} are put in reverse order. - * - * @param testPlan - * the test plan, must not be {@code null} - * @return the test reverse test plan, never {@code null} - */ - static TestPlan createReverseTestPlan(final TestPlan testPlan) { - List setupSteps = testPlan.getCompoundSetupStep().getSteps(); - Collections.reverse(setupSteps); - List testSteps = testPlan.getCompoundTestStep().getSteps(); - Collections.reverse(testSteps); - return new TestPlan(setupSteps, testSteps); - } - - /** - * Filters a {@link CompoundStep} and returns a list of {@link AbstractStep}s that are contained in the filter {@link Set}. - * - * @param filter - * the filter, must not be {@code null} - * @param compoundStep - * the compound step, must not be {@code null} - * @return the list of filtered {@link AbstractStep}s, never {@code null} - */ - private static List filterCompoundStep(final Collection filter, final CompoundStep compoundStep) { - List steps = new ArrayList(); - for (AbstractStep step : compoundStep.getSteps()) { - if (filter.contains(step)) { - steps.add(step); - } - } - return steps; - } - - /** - * Creates a {@link TestPlan} that contains the undo-steps of all {@link AbstractStep}s of the given {@link TestPlan}. - * - * @param testPlan - * the test plan, must not be {@code null} - * @return the test plan containing all undo-steps, never {@code null} - */ - static TestPlan createUndoStepsTestPlan(final TestPlan testPlan) { - List setupUndoSteps = getAllUndoSteps(testPlan.getCompoundSetupStep().getSteps()); - List testUndoSteps = getAllUndoSteps(testPlan.getCompoundTestStep().getSteps()); - return new TestPlan(setupUndoSteps, testUndoSteps); - } - - /** - * Utility method that returns a list of all undo steps from a list of {@link AbstractStep}. - * - * @param stepsToUndo - * list of steps to undo, must not be {@code null} - * @return list of undo steps in reverse order, never {@code null} - */ - private static List getAllUndoSteps(final List stepsToUndo) { - List undoSteps = new ArrayList(); - for (AbstractStep step : stepsToUndo) { - if (!NullStep.INSTANCE.equals(step.getUndoStep())) { - undoSteps.add(step.getUndoStep()); - } - } - return undoSteps; - } - - /** - * Utility method that returns a list of steps steps of the previous test that need to be undone before executing the current test. - * - * @param previousSteps - * previous steps, must not be {@code null} - * @param testPlan - * current test plan, must not be {@code null} - * @return list of steps to be undone, never {@code null} - */ - private static List computeStepsToUndo(final List previousSteps, final TestPlan testPlan) { - List stepsToUndo = new ArrayList(); - ListIterator reverseIterator = previousSteps.listIterator(previousSteps.size()); - while (reverseIterator.hasPrevious()) { - AbstractStep step = reverseIterator.previous(); - if (testPlan.isStepToUndo(step)) { - stepsToUndo.add(step.getUndoStep()); - } - } - return stepsToUndo; - } - - /** - * Utility method that checks if is step to undo. - * - * @param step - * step, must not be {@code null} - * @return true, if is step to undo - */ - private boolean isStepToUndo(final AbstractStep step) { - // Undo all steps that don't influence ITestEntities and have been executed - if (!(step instanceof ITestEntityActionProvider)) { - return true; - } - - for (final TestEntityAction testEntityAction : ((ITestEntityActionProvider) step).getTestEntityActions()) { - if (!hasTestEntity(testEntityAction)) { // Undo the step, if one TestEntity does not match. - return true; - } - } - return false; - } - - /** - * Checks if this {@link TestPlan} contains the {@link ITestEntity} of the given {@link TestEntityAction}. - * - * @param testEntityAction - * the test entity action, must not be {@code null} - * @return whether this {@link TestPlan} has {@link ITestEntity} of the given {@link TestEntityAction} - */ - private boolean hasTestEntity(final TestEntityAction testEntityAction) { - for (TestEntityAction action : getAllTestEntityActions()) { - if (action.hasSameTestEntity(testEntityAction)) { - return true; - } - } - return false; - } - - /** - * Creates a {@link TestPlan} with the steps that actually need to be executed for this test. - * - * @param testPlan - * current test plan, must not be {@code null} - * @param previousTestPlan - * previous test plan, can be {@code null} - * @return executable test plan, never {@code null} - */ - static TestPlan createExecutableTestPlan(final TestPlan testPlan, final TestPlan previousTestPlan) { - final List setupStepsToExecute = Lists.newArrayList(Collections2.filter(testPlan.getCompoundSetupStep().getSteps(), input -> input != null - && isStepToExecute(input, previousTestPlan))); - return new TestPlan(setupStepsToExecute, testPlan.getCompoundTestStep().getSteps()); - } - - /** - * Utility method that checks whether the given {@link AbstractStep} should be executed. - * - * @param setupStep - * the setup step, must not be {@code null} - * @param previousTestPlan - * the previous test plan, must not be {@code null} - * @return whether the given {@link AbstractStep} should be executed - */ - private static boolean isStepToExecute(final AbstractStep setupStep, final TestPlan previousTestPlan) { - if (setupStep instanceof ITestEntityActionProvider) { - // all ITestEntites must match in order not to execute the test step - for (final TestEntityAction action : ((ITestEntityActionProvider) setupStep).getTestEntityActions()) { - if (!previousTestPlan.hasTestEntity(action)) { - return true; - } - } - return false; - } // else: steps that don't involve ITestEntities must always be executed - return true; - } - - /** - * Returns the compound setup step. - * - * @return the compound setup step, never {@code null} - */ - CompoundStep getCompoundSetupStep() { - return compoundSetupStep; - } - - /** - * Returns the compound test step. - * - * @return the compound test step, never {@code null} - */ - CompoundStep getCompoundTestStep() { - return compoundTestStep; - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/ClassRunner.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/ClassRunner.java deleted file mode 100644 index 3c2b5c94c..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/ClassRunner.java +++ /dev/null @@ -1,351 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.internal.AssumptionViolatedException; -import org.junit.internal.runners.model.EachTestNotifier; -import org.junit.internal.runners.statements.RunAfters; -import org.junit.internal.runners.statements.RunBefores; -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; -import org.junit.runner.manipulation.NoTestsRemainException; -import org.junit.runner.manipulation.Sorter; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.ParentRunner; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; - -import com.avaloq.tools.ddk.test.core.AfterAll; -import com.avaloq.tools.ddk.test.core.BeforeAll; -import com.avaloq.tools.ddk.test.core.BugTest; -import com.avaloq.tools.ddk.test.core.IntegrationTest; -import com.avaloq.tools.ddk.test.core.ModuleTest; -import com.avaloq.tools.ddk.test.core.MultipleTestProblems; -import com.avaloq.tools.ddk.test.core.PerformanceTest; -import com.avaloq.tools.ddk.test.core.Retry; -import com.avaloq.tools.ddk.test.core.SystemTest; -import com.avaloq.tools.ddk.test.core.UnitTest; -import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - - -/** - * A JUnit runner extending the {@link BlockJUnit4ClassRunner} with support for @BeforeAll and @AfterAll annotated methods. These methods will be run once for a - * given test class before the first test method and after the last test method respectively. - *

- *

Test Methods

Considered are all those methods of the test class that are annotated with one (or more) of the following test annotations: - *
    - *
  • {@link Test} - *
  • {@link UnitTest} - *
  • {@link ModuleTest} - *
  • {@link IntegrationTest} - *
  • {@link SystemTest} - *
  • {@link PerformanceTest} - *
  • {@link BugTest} - *
- *

- *

- *

Execution Order

The order of execution of the test methods is random by default. This can be changed by setting a specific sorter using the property - * 'com.avaloq.test.sorter'. Available sorters: - *
    - *
  • alphanumeric - *
- *

- *

- *

Multiple Test Runs

The number of runs for each test can be set using the system property {@value #PROPERTY_TEST_RUNS}. By default a test is run - * exactly once, i.e. the number is set to 1. - *

- *

- * The runner can be configured to retry a failing test. The system property {@value #PROPERTY_TEST_RETRIES} allows to specify how many times at most a failing - * test shall be retried. By default a failing test is not retried, i.e. the number is set to 0. Note: Test retries are ignored if a value greater than - * 1 has been set for {@value #PROPERTY_TEST_RUNS}, in which case the test will be run exactly the specified number of times, regardless of failures. - *

- *

- * A test which succeeds at least once is regarded as successful. To make a test fail in the case where it failed at least once, set the system property - * {@value #PROPERTY_UNSTABLE_FAIL} to {@code true} (default: {@code false}). - *

- */ -@SuppressWarnings({"restriction", "deprecation"}) -public class ClassRunner extends BlockJUnit4ClassRunner { - /** The system property for the number of test runs. */ - public static final String PROPERTY_TEST_RUNS = "com.avaloq.test.runs"; //$NON-NLS-1$ - /** The system property for the number of times a failing test shall be retried. */ - public static final String PROPERTY_TEST_RETRIES = "com.avaloq.test.retries"; //$NON-NLS-1$ - /** The system property to specify whether unstable tests shall fail. */ - public static final String PROPERTY_UNSTABLE_FAIL = "com.avaloq.test.unstablefail"; //$NON-NLS-1$ - /** Class-wide logger. */ - private static final Logger LOGGER = LogManager.getLogger(ClassRunner.class); - private static final List> TEST_ANNOTATIONS = Lists.newArrayList(Test.class, UnitTest.class, ModuleTest.class, IntegrationTest.class, SystemTest.class, PerformanceTest.class, BugTest.class); - private List expectedMethods; - private int currentMethodIndex; - private final int testRuns; - private final int testRetries; - private final boolean unstableFail; - private Description description; - private boolean descriptionOutdated = true; - - /** - * Creates a new test class runner. - * - * @param klass - * target test class, must not be {@code null} - * @throws InitializationError - * if the runner could not be initialized - */ - public ClassRunner(final Class klass) throws InitializationError { - super(klass); - SorterUtil.getInstance().initializeSorter(this); - testRuns = Integer.parseInt(System.getProperty(PROPERTY_TEST_RUNS, "1")); //$NON-NLS-1$ - testRetries = Integer.parseInt(System.getProperty(PROPERTY_TEST_RETRIES, "0")); //$NON-NLS-1$ - unstableFail = Boolean.parseBoolean(System.getProperty(PROPERTY_UNSTABLE_FAIL, "false")); //$NON-NLS-1$ - } - - /** - * Initializes this runner by initializing {@link #expectedMethods} with the list of methods which are expected to be called. This is then also checked by - * {@link #methodBlock(FrameworkMethod)} and allows identifying the first and last methods correctly. - */ - private void ensureInitialized() { - if (expectedMethods == null) { - try { - final Method getChildrenMethod = ParentRunner.class.getDeclaredMethod("getFilteredChildren"); //$NON-NLS-1$ - getChildrenMethod.setAccessible(true); - @SuppressWarnings("unchecked") - final Collection testMethods = (Collection) getChildrenMethod.invoke(this); - expectedMethods = ImmutableList.copyOf(Iterables.filter(testMethods, new Predicate() { - @Override - public boolean apply(final FrameworkMethod input) { - return input.getAnnotation(Ignore.class) == null; - } - })); - currentMethodIndex = 0; - // CHECKSTYLE:OFF - } catch (Exception e) { - // CHECKSTYLE:ON - throw new IllegalStateException(e); - } - } - } - - @Override - public Description getDescription() { - if (descriptionOutdated) { - description = super.getDescription(); - descriptionOutdated = false; - } - return description; - } - - @Override - protected void validateInstanceMethods(final List errors) { - validatePublicVoidNoArgMethods(AfterAll.class, false, errors); - validatePublicVoidNoArgMethods(BeforeAll.class, false, errors); - - super.validateInstanceMethods(errors); - } - - @Override - public void sort(final Sorter sorter) { - super.sort(sorter); - descriptionOutdated = true; - } - - @Override - public void filter(final Filter filter) throws NoTestsRemainException { - super.filter(filter); - descriptionOutdated = true; - } - - @Override - protected void runChild(final FrameworkMethod method, final RunNotifier notifier) { - ensureInitialized(); - final boolean ignored = method.getAnnotation(Ignore.class) != null; - if (!ignored) { - Assert.assertEquals("Method " + method.getName() + " not equal", expectedMethods.get(currentMethodIndex++), method); //$NON-NLS-1$//$NON-NLS-2$ - } - if (ignored || testRuns == 1 && testRetries == 0 && method.getAnnotation(Retry.class) == null) { - super.runChild(method, notifier); - } else { - runRepeatedly(method, createNotifier(method, notifier)); - } - } - - /** - * Runs the test method repeatedly according to the number of runs or retries. - * - * @param method - * the {@link FrameworkMethod}, must not be {@code null} - * @param eachNotifier - * the {@link EachTestNotifier}, must not be {@code null} - */ - @SuppressWarnings("PMD.NPathComplexity") - private void runRepeatedly(final FrameworkMethod method, final EachTestNotifier eachNotifier) { - - final Retry retryAnnotation = method.getAnnotation(Retry.class); - final int retryAnnotationValue = retryAnnotation != null ? retryAnnotation.value() : 0; - final int thisTestRetries = Math.max(retryAnnotationValue, testRetries); - - eachNotifier.fireTestStarted(); - try { - final MultipleTestProblems problems = new MultipleTestProblems(); - final Collection failures = Lists.newArrayList(); - final Collection errors = Lists.newArrayList(); - int run = 0; - int succeeded = 0; - while (run < testRuns || (testRuns == 1 && succeeded == 0 && run < thisTestRetries + 1)) { - try { - run++; - methodBlock(method).evaluate(); - succeeded++; - } catch (AssumptionViolatedException e) { - throw e; - } catch (AssertionError exception) { - failures.add(exception); - problems.addProblem(exception); - // CHECKSTYLE:CHECK-OFF IllegalCatch // we want to catch all possible errors - } catch (Throwable throwable) { - // CHECKSTYLE:CHECK-ON IllegalCatch - errors.add(throwable); - problems.addProblem(throwable); - } - } - final String testCase = getTestClass().getJavaClass().getSimpleName() + '.' + method.getName(); - if (run > 1) { - logRepeatedTestResult(testCase, run, succeeded, failures.size(), errors.size()); - } - if (succeeded == 0) { - problems.assertEmpty(); - } - if (problems.hasProblems()) { - if (unstableFail) { - problems.assertEmpty(); - } else { - final StringWriter stringWriter = new StringWriter(); - problems.printStackTrace(new PrintWriter(stringWriter)); - LOGGER.info(stringWriter.toString()); - } - } - } catch (AssumptionViolatedException e) { // NOPMD ExceptionAsFlowControl - eachNotifier.addFailedAssumption(e); - // CHECKSTYLE:CHECK-OFF IllegalCatch // we want to catch all possible errors - } catch (Throwable e) { - // CHECKSTYLE:CHECK-ON IllegalCatch - eachNotifier.addFailure(e); - } finally { - eachNotifier.fireTestFinished(); - } - } - - /** - * Logs the repeated test result. - * - * @param testCase - * the test case, must not be {@code null} - * @param runs - * the number of runs - * @param succeeded - * the number of succeeded runs - * @param failures - * the number of failed runs - * @param errors - * the number of errored runs - */ - public void logRepeatedTestResult(final String testCase, final int runs, final int succeeded, final int failures, final int errors) { - final StringBuilder testResult = new StringBuilder(testCase).append(" Repeated Test Result: "); //$NON-NLS-1$ - if (succeeded == runs) { - testResult.append("SUCCESS"); //$NON-NLS-1$ - } else if (succeeded == 0) { - testResult.append("FAILURE"); //$NON-NLS-1$ - } else { - testResult.append("UNSTABLE"); //$NON-NLS-1$ - } - testResult.append(" (").append(succeeded).append(" of ").append(runs).append(" succeeded"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - if (failures > 0) { - testResult.append(", ").append(failures).append(" failed"); //$NON-NLS-1$ //$NON-NLS-2$ - } - if (errors > 0) { - testResult.append(", ").append(errors).append(" errored"); //$NON-NLS-1$ //$NON-NLS-2$ - } - testResult.append(')'); - LOGGER.info(testResult.toString()); - } - - /** - * Creates a notifier for each test. - * - * @param method - * the {@link FrameworkMethod}, must not be {@code null} - * @param notifier - * the {@link RunNotifier}, must not be {@code null} - * @return an instance of {@link EachTestNotifier}, never {@code null} - */ - private EachTestNotifier createNotifier(final FrameworkMethod method, final RunNotifier notifier) { - return new EachTestNotifier(notifier, describeChild(method)); - } - - /** - * Adds any @BeforeAll methods to be run before the normal @Before annotated methods for the first test method only. - *

- * {@inheritDoc} - */ - @Override - protected Statement withBefores(final FrameworkMethod method, final Object target, final Statement stmt) { - ensureInitialized(); - Statement statement = super.withBefores(method, target, stmt); // NOPMD.CloseResource - if (method.equals(expectedMethods.get(0))) { - // reverse BeforeAll method order to get a 'runs top to bottom' order - final List befores = Lists.reverse(getTestClass().getAnnotatedMethods(BeforeAll.class)); - statement = befores.isEmpty() ? statement : new RunBefores(statement, befores, target); - } - return statement; - } - - /** - * Adds any @AfterAll methods to be run after the normal @After annotated methods for the last test method only. - *

- * {@inheritDoc} - */ - @Override - protected Statement withAfters(final FrameworkMethod method, final Object target, final Statement stmt) { - ensureInitialized(); - Statement statement = super.withAfters(method, target, stmt); // NOPMD.CloseResource - if (method.equals(Iterables.getLast(expectedMethods))) { - final List afters = getTestClass().getAnnotatedMethods(AfterAll.class); - statement = afters.isEmpty() ? statement : new RunAfters(statement, afters, target); - } - return statement; - } - - @Override - protected List computeTestMethods() { - final Collection result = Sets.newHashSet(); - for (final Class annotationClass : TEST_ANNOTATIONS) { - result.addAll(getTestClass().getAnnotatedMethods(annotationClass)); - } - return Lists.newArrayList(result); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DebugClassFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DebugClassFilter.java deleted file mode 100644 index 86bcb8505..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DebugClassFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.Debug; - - -/** - * A test class filter that makes sure only test classes annotated with @{@link Debug} are run. - */ -public class DebugClassFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestClass(description); - return description.getAnnotation(Debug.class) != null; - } - - @Override - public String describe() { - return DebugClassFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DebugMethodFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DebugMethodFilter.java deleted file mode 100644 index 5214a176e..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DebugMethodFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.Debug; - - -/** - * A test method filter that makes sure only tests annotated with @{@link Debug} are run. - */ -public class DebugMethodFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestMethod(description); - return description.getAnnotation(Debug.class) != null; - } - - @Override - public String describe() { - return DebugMethodFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DiscerningSuite.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DiscerningSuite.java deleted file mode 100644 index ec91e7b7d..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DiscerningSuite.java +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import java.util.List; - -import org.junit.runner.Description; -import org.junit.runner.Runner; -import org.junit.runner.manipulation.Filter; -import org.junit.runner.manipulation.NoTestsRemainException; -import org.junit.runner.manipulation.Sorter; -import org.junit.runners.Suite; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.RunnerBuilder; - - -/** - * Discerns tests using test filters. - */ -public class DiscerningSuite extends Suite { - - private boolean descriptionOutdated = true; - private Description description; - private Filter testFilter; - - /** - * Creates a new instance of {@link DiscerningSuite}. - * - * @param klass - * root of the suite - * @param runners - * for each class in the suite, a {@link Runner} - * @throws InitializationError - * if an error occurred during the initialization - */ - public DiscerningSuite(final Class klass, final List runners) throws InitializationError { - super(klass, runners); - initialize(); - } - - /** - * Creates a new instance of {@link DiscerningSwtBotNonSwtBotSuite}. - * - * @param klass - * the root of the suite - * @param suiteClasses - * the classes in the suite - * @throws InitializationError - * if an error occurred during the initialization - */ - public DiscerningSuite(final Class klass, final Class... suiteClasses) throws InitializationError { - super(klass, suiteClasses); - initialize(); - } - - /** - * Creates a new instance of {@link DiscerningSwtBotNonSwtBotSuite}. - * - * @param klass - * the root class - * @param builder - * builds runners for classes in the suite - * @throws InitializationError - * if an error occurred during the initialization - */ - public DiscerningSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { - super(klass, builder); - initialize(); - } - - /** - * Creates a new instance of {@link DiscerningSwtBotNonSwtBotSuite}. - * - * @param builder - * builds runners for classes in the suite - * @param klass - * the root of the suite - * @param suiteClasses - * the classes in the suite - * @throws InitializationError - * if an error occurred during the initialization - */ - public DiscerningSuite(final RunnerBuilder builder, final Class klass, final Class... suiteClasses) throws InitializationError { - super(builder, klass, suiteClasses); - initialize(); - } - - /** - * Creates a new instance of {@link DiscerningSwtBotNonSwtBotSuite}. - * - * @param builder - * builds runners for classes in the suite - * @param classes - * the classes in the suite - * @throws InitializationError - * if an error occurred during the initialization - */ - public DiscerningSuite(final RunnerBuilder builder, final Class... classes) throws InitializationError { - super(builder, classes); - initialize(); - } - - /** - * Initializes the {@link DiscerningSuite}. - */ - private void initialize() { - SorterUtil.getInstance().initializeSorter(this); - FilterRegistry.initializeFilter(this); - } - - @Override - public Description getDescription() { - if (descriptionOutdated) { - description = super.getDescription(); - descriptionOutdated = false; - } - return description; - } - - @Override - public void sort(final Sorter sorter) { - super.sort(sorter); - descriptionOutdated = true; - } - - @Override - public void filter(final Filter filter) throws NoTestsRemainException { - if (testFilter == null || !testFilter.equals(filter)) { - testFilter = filter; - super.filter(filter); - descriptionOutdated = true; - } - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DynamicSuite.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DynamicSuite.java deleted file mode 100644 index 540f7fcd8..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DynamicSuite.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.ArrayList; -import java.util.List; - -import org.junit.runner.Runner; -import org.junit.runners.Suite; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.RunnerBuilder; - - -/** - * Using {@link DynamicSuite} as a runner allows you to manually build a suite containing - * tests from many classes, as with {@link Suite}. While {@link Suite} has static references - * to the test classes, {@link DynamicSuite} resolves the class names at runtime. - */ -public class DynamicSuite extends Suite { - - /** - * Annotation allows to specify names of suite classes as separate strings. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({java.lang.annotation.ElementType.TYPE}) - @Inherited - public static @interface SuiteClasses { - String[] value(); - } - - public DynamicSuite(final Class clazz, final RunnerBuilder builder) throws InitializationError { - this(clazz, builder.runners(clazz, getSuiteClasses(clazz))); - } - - public DynamicSuite(final RunnerBuilder builder, final Class... classes) throws InitializationError { - super(builder, classes); - } - - public DynamicSuite(final Class clazz, final Class... suiteClasses) throws InitializationError { - super(clazz, suiteClasses); - } - - public DynamicSuite(final Class clazz, final List runners) throws InitializationError { - super(clazz, runners); - } - - public DynamicSuite(final RunnerBuilder builder, final Class clazz, final Class... suiteClasses) throws InitializationError { - super(builder, clazz, suiteClasses); - } - - /** - * Collect suite classes from annotation. - * - * @param clazz - * main suite to get suite classes annotation from - * @return list of suite classes - * @throws InitializationError - * thrown when annotation is missing or class can not be found - */ - @SuppressWarnings("nls") - private static Class[] getSuiteClasses(final Class clazz) throws InitializationError { - // get annotation - SuiteClasses annotation = clazz.getAnnotation(SuiteClasses.class); - if (annotation == null) { - throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", clazz.getName())); - } - - // get classes from class names - List> suiteClasses = new ArrayList>(); - for (String className : annotation.value()) { - try { - suiteClasses.add(Class.forName(className, true, clazz.getClassLoader())); // use class loader of suite class in order to resolve specified classes - } catch (ClassNotFoundException e) { - throw new InitializationError(String.format("class '%s' not found", className)); // NOPMD: InitializationError has no parameter for Exception - } - } - - // return suite classes - return suiteClasses.toArray(new Class[suiteClasses.size()]); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FilterRegistry.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FilterRegistry.java deleted file mode 100644 index d0b4ae70a..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FilterRegistry.java +++ /dev/null @@ -1,265 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import java.lang.reflect.InvocationTargetException; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.eclipse.osgi.util.NLS; -import org.junit.runner.Description; -import org.junit.runner.RunWith; -import org.junit.runner.manipulation.Filter; -import org.junit.runner.manipulation.NoTestsRemainException; -import org.junit.runners.ParentRunner; -import org.junit.runners.Suite; - -import com.google.common.collect.Lists; - - -/** - * A delegating filter that allows registration of suite, test class and test method filters. - *

- * Note: Filters can be registered using the system properties {@value #PROPERTY_SUITE_FILTERS}, {@value #PROPERTY_CLASS_FILTERS} and - * {@value #PROPERTY_METHOD_FILTERS}. - *

- */ -@SuppressWarnings("nls") -public final class FilterRegistry extends Filter { - private static final String NEGATOR = "!"; - private static final String FILTER_SEPARATOR = ","; - private static final String EMPTY_STRING = ""; - /** The system property to register test suite filters. */ - private static final String PROPERTY_SUITE_FILTERS = "com.avaloq.test.suitefilters"; - /** The system property to register test class filters. */ - private static final String PROPERTY_CLASS_FILTERS = "com.avaloq.test.classfilters"; - /** The system property to register test method filters. */ - private static final String PROPERTY_METHOD_FILTERS = "com.avaloq.test.methodfilters"; - private static final String MESSAGE_INVALID_FILTER = "Invalid filter ''{0}'' registered in property ''{1}''."; - /** The package prefix for default filters, including the last segment separator. */ - private static final String PACKAGE_PREFIX = FilterRegistry.class.getCanonicalName().substring(0, FilterRegistry.class.getCanonicalName().length() - - FilterRegistry.class.getSimpleName().length()); - private static final FilterRegistry INSTANCE = new FilterRegistry(); - private static final Logger LOGGER = LogManager.getLogger(FilterRegistry.class); - /** The list of test suite filters. */ - private final List suiteFilters; - /** The list of test class filters. */ - private final List classFilters; - /** The list of test method filters. */ - private final List methodFilters; - - /** - * Initializes the test filter. - * - * @param parentRunner - * the {@link ParentRunner} to initialize, must not be {@code null} - */ - public static void initializeFilter(final ParentRunner parentRunner) { - try { - parentRunner.filter(INSTANCE); - } catch (NoTestsRemainException e) { - // we ignore the case where no children are left - } - } - - /** - * Creates a new instance of {@link FilterRegistry}. - *

- * Note: This filter is initialized using the values of the system properties {@value #PROPERTY_SUITE_FILTERS}, {@value #PROPERTY_CLASS_FILTERS} and - * {@value #PROPERTY_METHOD_FILTERS}. - *

- *

- * The values are separated by {@value #FILTER_SEPARATOR}. A filter can be negated by prepending a {@value #NEGATOR}. - *

- */ - private FilterRegistry() { - suiteFilters = parseFilters(PROPERTY_SUITE_FILTERS); - classFilters = parseFilters(PROPERTY_CLASS_FILTERS); - methodFilters = parseFilters(PROPERTY_METHOD_FILTERS); - } - - /** - * Parses and instantiates filters registered with the given system property. - * - * @param property - * the name of the system property to parse, must not be {@code null} or empty - * @return the list of filters, never {@code null} - */ - private List parseFilters(final String property) { - final List result = Lists.newArrayList(); - final String filtersProperty = System.getProperty(property, EMPTY_STRING); - if (filtersProperty.length() == 0) { - return result; - } - final String[] filterNames = filtersProperty.split(FILTER_SEPARATOR); - for (final String splitSegment : filterNames) { - String splitSegmentTrimmed = splitSegment.trim(); - if (splitSegmentTrimmed.isEmpty()) { - continue; - } - - // Handle negation - boolean negated = splitSegmentTrimmed.startsWith(NEGATOR); - String filterClassOrLabel = splitSegmentTrimmed; - if (negated) { - filterClassOrLabel = splitSegmentTrimmed.substring(1); - } - - // Try to find a Filter subclass with this name - String qualifiedClassName = filterClassOrLabel; - if (!filterClassOrLabel.contains(".")) { - qualifiedClassName = PACKAGE_PREFIX + filterClassOrLabel; - } - try { - Filter filter = null; - try { - Class filterClass = Class.forName(qualifiedClassName); - if (Filter.class.isAssignableFrom(filterClass)) { - final Object filterInstance = filterClass.getConstructor().newInstance(); - filter = (Filter) filterInstance; - } - } catch (ClassNotFoundException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { - // If no class exists with this name, interpret the String as a label and create a label filter for it - filter = new TestLabelFilter(filterClassOrLabel); - } - - if (filter != null) { - if (negated) { - result.add(new NegatedFilter(filter)); - } else { - result.add(filter); - } - } - } catch (InstantiationException e) { - LOGGER.error(NLS.bind(MESSAGE_INVALID_FILTER, qualifiedClassName, property), e); - } catch (IllegalAccessException e) { - LOGGER.error(NLS.bind(MESSAGE_INVALID_FILTER, qualifiedClassName, property), e); - } - } - return result; - } - - /** - * Determines if the given {@link Description} is describing a {@link Suite}. - * - * @param description - * the {@link Description} to check, must not be {@code null} - * @return whether the description is describing a Suite - */ - public static boolean isSuite(final Description description) { - final RunWith runWithAnnotation = description.getAnnotation(RunWith.class); - return runWithAnnotation != null && Suite.class.isAssignableFrom(runWithAnnotation.value()); - } - - /** - * Determines if the given {@link Description} is describing a test class. - * - * @param description - * the {@link Description} to check, must not be {@code null} - * @return whether the description is describing a test class - */ - public static boolean isTestClass(final Description description) { - return !isSuite(description) && !isTestMethod(description); - } - - /** - * Determines if the given {@link Description} is describing a test method. - * - * @param description - * the {@link Description} to check, must not be {@code null} - * @return whether the description is describing a test method - */ - public static boolean isTestMethod(final Description description) { - return description.getMethodName() != null; - } - - @Override - public boolean shouldRun(final Description description) { - if (isSuite(description)) { - // check if the description passes all suite filters - for (final Filter filter : suiteFilters) { - if (!filter.shouldRun(description)) { - return false; - } - } - if (!hasRunnableChildren(description)) { - return false; - } - } else if (isTestClass(description)) { - if (!hasRunnableChildren(description)) { - return false; - } - // check if the description passes all test class filters - for (final Filter filter : classFilters) { - if (!filter.shouldRun(description)) { - return false; - } - } - } else if (isTestMethod(description)) { - // check if the description passes all test method filters - for (final Filter filter : methodFilters) { - if (!filter.shouldRun(description)) { - return false; - } - } - } - return true; - } - - /** - * Check recursively if at least one child description should be run. - * - * @param description - * the parent {@link Description}, must not be {@code null} - * @return whether there are runnable children - */ - private boolean hasRunnableChildren(final Description description) { - for (final Description child : description.getChildren()) { - if (shouldRun(child)) { - return true; - } - } - return false; - } - - @Override - public String describe() { - final StringBuilder description = new StringBuilder("FilterRegistry"); - description.append(describeFilters(suiteFilters, "suite")); - description.append(describeFilters(classFilters, "class")); - description.append(describeFilters(methodFilters, "method")); - return description.toString(); - } - - /** - * Describes the list of filters. - * - * @param filters - * the filters to describe, must not be {@code null} - * @param title - * the title for the filters, may be {@code null} - * @return - * the description for the given filters, never {@code null} - */ - private String describeFilters(final List filters, final String title) { - assert filters != null; - final StringBuilder description = new StringBuilder(); - if (!filters.isEmpty()) { - description.append(" (").append(title).append(" filters:"); - for (final Filter filter : filters) { - description.append(' ').append(filter.describe()); - } - description.append(')'); - } - return description.toString(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FixedIssueFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FixedIssueFilter.java deleted file mode 100644 index 240004171..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FixedIssueFilter.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.BugTest; -import com.avaloq.tools.ddk.test.core.Issue; -import com.avaloq.tools.ddk.test.core.Issues; - - -/** - * A test filter that makes sure only tests for fixed issues are run. - *

- * The filter checks if all of the related issues are fixed . If there are no related issues specified, the test is not run. - *

- */ -public class FixedIssueFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - if (FilterRegistry.isSuite(description)) { - return true; - } - // else: test class or test method - final Issues issuesAnnotation = description.getAnnotation(Issues.class); - if (issuesAnnotation != null) { - for (final Issue issueAnnotation : issuesAnnotation.value()) { - if (issueAnnotation != null && !issueAnnotation.fixed()) { - return false; - } - } - } - final Issue issueAnnotation = description.getAnnotation(Issue.class); - if (issueAnnotation != null && !issueAnnotation.fixed()) { - return false; - } - final BugTest bugTestAnnotation = description.getAnnotation(BugTest.class); - if (bugTestAnnotation != null && bugTestAnnotation.unresolved()) { - return false; - } - return FilterRegistry.isTestMethod(description); // if it is a test class we still want to check (and maybe run) its children - } - - @Override - public String describe() { - return FixedIssueFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FlakyMethodFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FlakyMethodFilter.java deleted file mode 100644 index f45b2ec50..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FlakyMethodFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.Flaky; - - -/** - * A test method filter that makes sure only tests annotated with @{@link Flaky} are run. - */ -public class FlakyMethodFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestMethod(description); - return description.getAnnotation(Flaky.class) != null; - } - - @Override - public String describe() { - return FlakyMethodFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/IntegrationTestMethodFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/IntegrationTestMethodFilter.java deleted file mode 100644 index 6ebd42a76..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/IntegrationTestMethodFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.IntegrationTest; - - -/** - * A test method filter that makes sure only tests annotated with @{@link IntegrationTest} are run. - */ -public class IntegrationTestMethodFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestMethod(description); - return description.getAnnotation(IntegrationTest.class) != null; - } - - @Override - public String describe() { - return IntegrationTestMethodFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/ModuleTestMethodFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/ModuleTestMethodFilter.java deleted file mode 100644 index acbdba084..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/ModuleTestMethodFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.ModuleTest; - - -/** - * A test method filter that makes sure only tests annotated with @{@link ModuleTest} are run. - */ -public class ModuleTestMethodFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestMethod(description); - return description.getAnnotation(ModuleTest.class) != null; - } - - @Override - public String describe() { - return ModuleTestMethodFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/NameMethodFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/NameMethodFilter.java deleted file mode 100644 index 56d2af9b6..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/NameMethodFilter.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import java.util.regex.Pattern; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - - -/** - * A test method filter that makes sure to run only those tests, which have a fully qualified name that matches a specified regular expression. - *

- * Note: The regular expression needs to be defined using the property {@value #PROPERTY_NAME_PREFIX}. - *

- */ -@SuppressWarnings("nls") -public class NameMethodFilter extends Filter { - - private static final String PROPERTY_NAME_PREFIX = "com.avaloq.test.namemethodfilter"; - private static final String REGULAR_EXPRESSION = System.getProperty(PROPERTY_NAME_PREFIX, ""); - private static final Pattern PATTERN = Pattern.compile(REGULAR_EXPRESSION); - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestMethod(description); - return PATTERN.matcher(getQualifiedMethodName(description)).matches(); - } - - @Override - public String describe() { - return NameMethodFilter.class.getSimpleName(); - } - - /** - * Returns the qualified method name of the given {@link Description}. - * - * @param description - * the {@link Description} which must describe a test method, must not be {@code null} - * @return the qualified method name of the given {@link Description} - */ - private String getQualifiedMethodName(final Description description) { - assert FilterRegistry.isTestMethod(description); - return description.getClassName() + "." + description.getMethodName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/NegatedFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/NegatedFilter.java deleted file mode 100644 index 37e9ec33a..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/NegatedFilter.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - - -/** - * A filter that inverts the filter criteria of the underlying filter. - */ -public class NegatedFilter extends Filter { - - private final Filter underlyingFilter; - - /** - * Creates a new instance of {@link NegatedFilter}. - * - * @param filter - * the filter to negate, must not be {@code null} - */ - public NegatedFilter(final Filter filter) { - assert filter != null; - this.underlyingFilter = filter; - } - - @Override - public boolean shouldRun(final Description description) { - return !underlyingFilter.shouldRun(description); - } - - @Override - public String describe() { - return "!" + underlyingFilter.describe(); //$NON-NLS-1$ - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/PerformanceTestMethodFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/PerformanceTestMethodFilter.java deleted file mode 100644 index 628f6e1bb..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/PerformanceTestMethodFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.PerformanceTest; - - -/** - * A test method filter that makes sure only tests annotated with @{@link PerformanceTest} are run. - */ -public class PerformanceTestMethodFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestMethod(description); - return description.getAnnotation(PerformanceTest.class) != null; - } - - @Override - public String describe() { - return PerformanceTestMethodFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SorterUtil.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SorterUtil.java deleted file mode 100644 index 6f92d9246..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SorterUtil.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import java.util.Comparator; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Sorter; -import org.junit.runners.ParentRunner; - - -/** - * Utility to specify a {@link Sorter}. - */ -@SuppressWarnings("nls") -public final class SorterUtil { - - public static final String SORTER_ALPHANUMERIC = "alphanumeric"; - public static final String PROPERTY_SORTER = "com.avaloq.test.sorter"; - private static final String EMPTY = ""; - private final Sorter sorter; - - private static final class SingletonHolder { - private static SorterUtil instance = new SorterUtil(); - - static SorterUtil get() { - return instance; - } - } - - /** - * Returns the singleton instance of {@link SorterUtil}. - * - * @return the singleton instance of {@link SorterUtil}, never {@code null} - */ - public static SorterUtil getInstance() { - return SingletonHolder.get(); - } - - /** - * Creates a new instance of {@link SorterUtil}. - */ - private SorterUtil() { - final String propertySorter = System.getProperty(PROPERTY_SORTER, EMPTY); - if (SORTER_ALPHANUMERIC.equalsIgnoreCase(propertySorter)) { - sorter = new Sorter(new Comparator() { - @Override - public int compare(final Description o1, final Description o2) { - return o1.getDisplayName().compareTo(o2.getDisplayName()); - } - }); - } else { - sorter = Sorter.NULL; - } - } - - /** - * Initializes the test sorter. - * - * @param parentRunner - * the {@link ParentRunner} to initialize, must not be {@code null} - */ - public void initializeSorter(final ParentRunner parentRunner) { - parentRunner.sort(sorter); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SwtBotClassFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SwtBotClassFilter.java deleted file mode 100644 index 920ae40a4..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SwtBotClassFilter.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.RunWith; -import org.junit.runner.manipulation.Filter; - - -/** - * A test filter that makes sure only SWT bot tests are run. - *

- * Note: The SWT bot test classes must use the test runner class specified in the VM argument - * `com.avaloq.tools.ddk.test.core.junit.runners.SwtBotClassFilter.runwithannotation` for them to be recognized. - *

- */ -public class SwtBotClassFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestClass(description); - final RunWith runWithAnnotation = description.getAnnotation(RunWith.class); - - return runWithAnnotation != null - && System.getProperty(SwtBotClassFilter.class.getCanonicalName() + ".runwithannotation").equals(runWithAnnotation.value().getName()); //$NON-NLS-1$ - } - - @Override - public String describe() { - return SwtBotClassFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SystemTestMethodFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SystemTestMethodFilter.java deleted file mode 100644 index 256a11e2e..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SystemTestMethodFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.SystemTest; - - -/** - * A test method filter that makes sure only tests annotated with @{@link SystemTest} are run. - */ -public class SystemTestMethodFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestMethod(description); - return description.getAnnotation(SystemTest.class) != null; - } - - @Override - public String describe() { - return SystemTestMethodFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/TestLabelFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/TestLabelFilter.java deleted file mode 100644 index efe543ede..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/TestLabelFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.apache.commons.lang3.ArrayUtils; -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.TestLabels; - - -/** - * A test filter that makes sure to run only those {@link Description}s that are annotated with a specific {@link TestLabel}. - */ -public class TestLabelFilter extends Filter { - private final String label; - - public TestLabelFilter(final String label) { - this.label = label; - } - - @Override - public boolean shouldRun(final Description description) { - TestLabels labels = description.getAnnotation(TestLabels.class); - return labels != null && ArrayUtils.contains(labels.value(), label); - } - - @Override - public String describe() { - return TestLabelFilter.class.getSimpleName(); - } - -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnitTestMethodFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnitTestMethodFilter.java deleted file mode 100644 index f443053a4..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnitTestMethodFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.UnitTest; - - -/** - * A test method filter that makes sure only tests annotated with @{@link UnitTest} are run. - */ -public class UnitTestMethodFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestMethod(description); - return description.getAnnotation(UnitTest.class) != null; - } - - @Override - public String describe() { - return UnitTestMethodFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnresolvedBugMethodFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnresolvedBugMethodFilter.java deleted file mode 100644 index 6b7629657..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnresolvedBugMethodFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.BugTest; - - -/** - * A test method filter that makes sure only unresolved bug tests are run. - */ -public class UnresolvedBugMethodFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - assert FilterRegistry.isTestMethod(description); - final BugTest bugTestAnnotation = description.getAnnotation(BugTest.class); - return bugTestAnnotation != null && bugTestAnnotation.unresolved(); - } - - @Override - public String describe() { - return UnresolvedBugMethodFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnresolvedIssueFilter.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnresolvedIssueFilter.java deleted file mode 100644 index 926e4127d..000000000 --- a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnresolvedIssueFilter.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.core.junit.runners; - -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; - -import com.avaloq.tools.ddk.test.core.BugTest; -import com.avaloq.tools.ddk.test.core.Issue; -import com.avaloq.tools.ddk.test.core.Issues; - - -/** - * A test filter that makes sure only tests for unresolved issues are run. - *

- * The filter checks if any of the related issues is unresolved. If there are no related issues specified, the test is not run. - *

- */ -public class UnresolvedIssueFilter extends Filter { - - @Override - public boolean shouldRun(final Description description) { - if (FilterRegistry.isSuite(description)) { - return true; - } - // else: test class or test method - final Issues issuesAnnotation = description.getAnnotation(Issues.class); - if (issuesAnnotation != null) { - for (final Issue issueAnnotation : issuesAnnotation.value()) { - if (issueAnnotation != null && !issueAnnotation.fixed()) { - return true; - } - } - } - final Issue issueAnnotation = description.getAnnotation(Issue.class); - if (issueAnnotation != null && !issueAnnotation.fixed()) { - return true; - } - final BugTest bugTestAnnotation = description.getAnnotation(BugTest.class); - if (bugTestAnnotation != null && bugTestAnnotation.unresolved()) { // NOPMD SimplifyBooleanReturns - return true; - } - return FilterRegistry.isTestClass(description); // if it is a test class we still want to check (and maybe run) its children - } - - @Override - public String describe() { - return UnresolvedIssueFilter.class.getSimpleName(); - } -} diff --git a/com.avaloq.tools.ddk.test.ui/src/com/avaloq/tools/ddk/test/ui/junit/runners/SwtBotRecordingTestRunner.java b/com.avaloq.tools.ddk.test.ui/src/com/avaloq/tools/ddk/test/ui/junit/runners/SwtBotRecordingTestRunner.java deleted file mode 100644 index 1e97cfdc4..000000000 --- a/com.avaloq.tools.ddk.test.ui/src/com/avaloq/tools/ddk/test/ui/junit/runners/SwtBotRecordingTestRunner.java +++ /dev/null @@ -1,128 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.ui.junit.runners; - -import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences; -import org.junit.internal.runners.statements.InvokeMethod; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; - -import com.avaloq.tools.ddk.test.core.AbstractSystemTest; -import com.avaloq.tools.ddk.test.core.junit.runners.ClassRunner; -import com.avaloq.tools.ddk.test.ui.swtbot.CoreSwtbotTools; - - -/** - * A {@link org.junit.runner.Runner} to use with swt bot tests featuring screenshot recording. - */ -// suppress warning restriction of org.junit.internal.runners.statements.InvokeMethod -@SuppressWarnings("restriction") -public class SwtBotRecordingTestRunner extends ClassRunner { - /** - * Used to mark a test class or method to define the recording interval in milliseconds to use for the {@link SwtBotRecordingTestRunner}. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.METHOD, ElementType.TYPE}) - public static @interface RecordingInterval { - /** - * Returns the recording interval value in milliseconds. - * - * @return the recording interval value in milliseconds - */ - long value(); - } - - private final TestRunRecording testRunRecording; - - /** - * Creates a new instance of {@link SwtBotRecordingTestRunner}. - * Only called reflectively. Do not use programmatically. - * - * @param testClass - * the test class to run - * @throws InitializationError - * if any initialization failed - */ - public SwtBotRecordingTestRunner(final Class testClass) throws InitializationError { - super(testClass); - CoreSwtbotTools.initializePreferences(); - testRunRecording = new TestRunRecording(getTestClass().getJavaClass(), SWTBotPreferences.SCREENSHOTS_DIR); - // TODO: add custom made {@link org.junit.runners.model.Statement} to check for exceptions during the test - // (by default test failures are reported only after the teardown has taken place) - } - - @Override - public void run(final RunNotifier notifier) { - try { - notifier.removeListener(testRunRecording); // remove existing listeners that could be added by suite or class runners - notifier.addListener(testRunRecording); - super.run(notifier); - } finally { - notifier.removeListener(testRunRecording); - } - } - - @Override - protected void runChild(final FrameworkMethod method, final RunNotifier notifier) { - testRunRecording.setRecordingInterval(getRecordingInterval(method)); - super.runChild(method, notifier); - } - - @Override - protected Statement methodInvoker(final FrameworkMethod method, final Object test) { - return new InvokeMethod(method, test) { - @Override - // CHECKSTYLE:CHECK-OFF IllegalThrow // inherited JUnit throw style - public void evaluate() throws Throwable { - // CHECKSTYLE:CHECK-ON IllegalThrow - try { - super.evaluate(); - // CHECKSTYLE:CHECK-OFF IllegalCatch // catching in order to act upon but then throwing the exception again - } catch (final Throwable throwable) { - // CHECKSTYLE:CHECK-ON IllegalCatch - testRunRecording.methodInvokeFailure(throwable); - throw throwable; - } - } - }; - } - - /** - * Computes the capture interval to use for recording the given test method. - * Checks first the annotation {@link RecordingInterval} of the method, - * then the annotations of the test class. - * Falls back to the default setting if no annotations were found. - * - * @param method - * the method under test - * @return the framerate to use for recording the given test method - */ - private long getRecordingInterval(final FrameworkMethod method) { - RecordingInterval recordingInterval = method.getAnnotation(RecordingInterval.class); - if (recordingInterval != null) { - return recordingInterval.value(); - } - for (Annotation annotation : getTestClass().getAnnotations()) { - if (annotation.annotationType().equals(RecordingInterval.class)) { - return ((RecordingInterval) annotation).value(); - } - } - return TestRunRecording.DEFAULT_RECORDING_INTERVAL; - } -} diff --git a/com.avaloq.tools.ddk.test.ui/src/com/avaloq/tools/ddk/test/ui/junit/runners/TestRunRecording.java b/com.avaloq.tools.ddk.test.ui/src/com/avaloq/tools/ddk/test/ui/junit/runners/TestRunRecording.java deleted file mode 100644 index 69c0da2c9..000000000 --- a/com.avaloq.tools.ddk.test.ui/src/com/avaloq/tools/ddk/test/ui/junit/runners/TestRunRecording.java +++ /dev/null @@ -1,726 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.test.ui.junit.runners; - -import java.awt.AWTException; -import java.awt.Color; -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.Robot; -import java.awt.Toolkit; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.lang.management.LockInfo; -import java.lang.management.ManagementFactory; -import java.lang.management.MonitorInfo; -import java.lang.management.ThreadInfo; -import java.lang.management.ThreadMXBean; -import java.nio.charset.StandardCharsets; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.Set; -import java.util.logging.Logger; - -import javax.imageio.ImageIO; - -import org.eclipse.emf.common.util.WrappedException; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable; -import org.eclipse.swtbot.swt.finder.results.VoidResult; -import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences; -import org.eclipse.ui.PlatformUI; -import org.junit.runner.Description; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunListener; - -import com.avaloq.tools.ddk.annotations.SuppressFBWarnings; -import com.avaloq.tools.ddk.test.core.AbstractTestStep; -import com.avaloq.tools.ddk.test.core.TestStepListener; -import com.google.common.collect.Sets; -import com.google.common.primitives.Longs; - - -/** - * Recording of a test run. - */ -// CHECKSTYLE:OFF -@SuppressWarnings("nls") -public class TestRunRecording extends RunListener implements TestStepListener, MouseListener { - private static final String NEW_LINE = "\r\n"; - private static final String FILE_INFORMATION_SEPARATOR = "__"; - private static final int MOUSE_POINTER_SIZE = 24; - private static final int INFO_BORDER_SIZE = 3; - // CHECKSTYLE:ON - // private static final int FILE_READ_BUFFER = 2048; - private static final int INFO_FONT_SIZE = 18; - private static final Logger LOGGER = Logger.getLogger(TestRunRecording.class.getSimpleName()); - private static final String IMAGE_TYPE = SWTBotPreferences.SCREENSHOT_FORMAT; - private static final String IMAGE_FILE_EXTENSION = '.' + IMAGE_TYPE; - private static final String CALL_STACK_FILE_EXTENSION = ".txt"; - private static boolean screenshotsCleared; - private final Robot robot; - private final Toolkit toolkit = Toolkit.getDefaultToolkit(); - private static final int STACK_DEPTH = 80; - private static final ThreadMXBean THREAD_BEAN = ManagementFactory.getThreadMXBean(); - - /** - * The recording interval defines the (approximate) maximum interval in milliseconds between two consecutive screenshots. - */ - public static final long DEFAULT_RECORDING_INTERVAL = 500; - private static final int MINIMAL_RECORDING_INTERVAL = 100; - private long recordingInterval; - - private final String screenshotFolder; - private final Class testClass; - private String testClassName; - private String testMethodName; - private AbstractTestStep currentTestStep; - private TestStepState currentTestStepState; - - private Thread captureThread; - private final Object lock = new Object(); // used for synchronizing with anonymous classes - private boolean captureThreadRunning; - private long lastCaptureTime; - - private boolean testFailed; - /** The associated exception in case of a failure. */ - private Throwable exception; - private boolean testStepStarted; - private boolean mouseClicked; - private boolean mousePressedDown; - private MouseEvent mouseEvent; - - /** - * Creates a new instance of {@link TestRunRecording}. - * - * @param testClass - * the class under test - * @param screenshotFolder - * the folder dedicated for screenshots - */ - public TestRunRecording(final Class testClass, final String screenshotFolder) { - this(testClass, screenshotFolder, DEFAULT_RECORDING_INTERVAL); - } - - /** - * Creates a new instance of {@link TestRunRecording}. - * - * @param testClass - * the class under test - * @param screenshotFolder - * the folder dedicated for screenshots - * @param recordingInterval - * the interval in milliseconds between two consecutive screenshots - */ - public TestRunRecording(final Class testClass, final String screenshotFolder, final long recordingInterval) { - this.testClass = testClass; - this.screenshotFolder = screenshotFolder; - setRecordingInterval(recordingInterval); - try { - robot = new Robot(); - } catch (AWTException e) { - throw new WrappedException("TestRunRecording cannot be used in a headless environment.", e); - } - } - - /** - * Sets the interval in milliseconds between two consecutive screenshots. - * - * @param recordingInterval - * the new recording interval in milliseconds - */ - public final void setRecordingInterval(final long recordingInterval) { - this.recordingInterval = Math.max(recordingInterval, MINIMAL_RECORDING_INTERVAL); - } - - @Override - @SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation") - @SuppressFBWarnings("AT_STALE_THREAD_WRITE_OF_PRIMITIVE") - public void testStarted(final Description description) { - if (!screenshotsCleared) { - deleteDirectoryContents(new File(screenshotFolder)); - screenshotsCleared = true; - } - testFailed = false; - testStepStarted = false; - if (description.getTestClass() != null) { - testClassName = description.getTestClass().getName(); - } else { - testClassName = testClass.getSimpleName(); - } - testMethodName = description.getMethodName(); - AbstractTestStep.registerTestStepListener(this); - UIThreadRunnable.syncExec(new VoidResult() { - @Override - public void run() { - PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell().addMouseListener(TestRunRecording.this); - } - }); - start(); - } - - @Override - @SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation") - public void testFinished(final Description description) { - stop(); - AbstractTestStep.removeTestStepListener(this); - UIThreadRunnable.syncExec(new VoidResult() { - @Override - public void run() { - PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell().removeMouseListener(TestRunRecording.this); - } - }); - if (!testFailed) { - deleteScreenshots(); - } - } - - @Override - @SuppressWarnings("PMD.JUnit4TestShouldUseTestAnnotation") - public void testFailure(final Failure failure) { - methodInvokeFailure(failure.getException()); - } - - @Override - public void stepStateChanged(final AbstractTestStep testStep, final TestStepState testStepState, final Throwable throwable) { - synchronized (lock) { - this.currentTestStep = testStep; - this.currentTestStepState = testStepState; - testStepStarted = true; - if (TestStepState.FAILED == testStepState || TestStepState.ERRORED == testStepState) { - exception = throwable; - testFailed = true; - } - createScreenshot(0); // always create a screenshot when the step state changes - createCallStack(0); - } - } - - /** - * Handles an exception caused by invoking the test method. - * - * @param throwable - * the thrown {@link Throwable}, must not be {@code null} - */ - public void methodInvokeFailure(final Throwable throwable) { - synchronized (lock) { - if (!testFailed) { - exception = throwable; - testFailed = true; - createScreenshot(0); // immediately create a screenshot when a test failure is reported - createCallStack(0); - } - } - } - - /** - * Creates a new asynchronous thread that regularly creates screenshots of the running test environment. - * Stops a previously started and still running thread. - */ - private void start() { - if (captureThreadRunning) { - stop(); - } - deleteScreenshots(); - captureThreadRunning = true; - captureThread = new Thread(new Runnable() { - @Override - public void run() { - while (captureThreadRunning) { - synchronized (lock) { - // if test steps are used, only create continuous screenshots in the run step state; for all other states 1 screenshot is enough - if (currentTestStep != null && TestStepState.RUN == currentTestStepState) { - createScreenshot(recordingInterval); - createCallStack(recordingInterval); - } - } - try { - Thread.sleep(recordingInterval); - } catch (final InterruptedException e) { - // ignore, because #createScreenshot will make sure that a minimal recording interval is retained - } - } - } - }); - captureThread.start(); - } - - /** - * Stops the asynchronous thread from creating screenshots. - */ - @SuppressFBWarnings("AT_STALE_THREAD_WRITE_OF_PRIMITIVE") - private void stop() { - captureThreadRunning = false; - try { - captureThread.join(); - } catch (final InterruptedException e) { - // ignore: the capture thread is no longer important and shall silently be removed - } - } - - /** - * Creates a new screenshot, if the last screenshot is older than the minimal recording interval. - */ - private void createScreenshot() { - createScreenshot(MINIMAL_RECORDING_INTERVAL); - } - - /** - * Creates a new screenshot, if the last screenshot is older than the minimal recording interval. - */ - private void createCallStack() { - createCallStack(MINIMAL_RECORDING_INTERVAL); - } - - /** - * Creates a new screenshot, if the last screenshot is older than the provided captureInterval. - * - * @param captureInterval - * the minimal interval in milliseconds which determines if the screenshot is created - */ - private void createScreenshot(final long captureInterval) { - synchronized (lock) { - if (System.currentTimeMillis() > lastCaptureTime + captureInterval) { - lastCaptureTime = System.currentTimeMillis(); // save time at beginning, so that a failed screenshot results in a skipped frame - final BufferedImage image = capture(); - process(image); - try { - store(image); - } catch (IOException e) { - LOGGER.warning("Could not create screenshot: " + getImageFileName()); - } - } - } - } - - /** - * Creates a new call stack record, if the last record is older than the provided captureInterval. - * - * @param captureInterval - * the minimal interval in milliseconds which determines if the call stack is created - */ - private void createCallStack(final long captureInterval) { - synchronized (lock) { - if (System.currentTimeMillis() > lastCaptureTime + captureInterval) { - lastCaptureTime = System.currentTimeMillis(); // save time at beginning - final String image = captureCallStack(); - try { - storeCallStack(image); - } catch (IOException e) { - LOGGER.warning("Could not create screenshot: " + getImageFileName()); - } - } - } - } - - /** - * Captures a screenshot. - * - * @return the {@link BufferedImage}, never {@code null} - */ - private BufferedImage capture() { - final Rectangle screenBounds = new Rectangle(toolkit.getScreenSize()); - return robot.createScreenCapture(screenBounds); - } - - /** - * Captures a call stack. - * - * @return the call stack with test status information, never {@code null} - */ - @SuppressWarnings("PMD.InsufficientStringBufferDeclaration") - private String captureCallStack() { - StringBuilder trace = new StringBuilder(); - // Same info as on the screenshot - trace.append("TEST: ").append(testClassName).append('.').append(testMethodName).append(NEW_LINE); - if (testStepStarted && currentTestStep != null && currentTestStepState != null) { - String colonWithSpace = ": "; - trace.append("TEST STEP: ").append(currentTestStep.getName()).append(colonWithSpace).append(currentTestStepState.toString()).append(NEW_LINE); - if (testFailed && exception != null) { - trace.append("TEST FAILED: ").append(exception.getClass().getSimpleName()).append(colonWithSpace).append(exception.getMessage()).append(NEW_LINE); - } - } - trace.append(getThreadInfo()); - return trace.toString(); - } - - /** - * Gets the task name. - * - * @param id - * the id - * @param name - * the name - * @return the task name - */ - private static String getTaskName(final long id, final String name) { - if (name == null) { - return Long.toString(id); - } - return id + " (" + name + ")"; - } - - /** - * Gathers the thread ids of threads that look deadlocked. - * - * @return the thread ids of deadlocked threads - */ - private static Set getDeadlockThreadIds() { - Set deadlockedThreads = Sets.newHashSet(); - long[] dead = THREAD_BEAN.findDeadlockedThreads(); - if (dead != null) { - deadlockedThreads.addAll(Longs.asList(dead)); - } - dead = THREAD_BEAN.findMonitorDeadlockedThreads(); - if (dead != null) { - deadlockedThreads.addAll(Longs.asList(dead)); - } - return deadlockedThreads; - } - - /** - * Prints the owned locks for the thread. - * Must have explicitly obtained Monitor and Synchronizer information for anything to show up from here. - * - * @param info - * the {@code:ThreadInfo} for the thread in question, must not be {@code:null} - * @return the owned lock information - */ - private static String getOwnedLockInfo(final ThreadInfo info) { - StringBuilder trace = new StringBuilder(" Holding locks for:").append(NEW_LINE); - for (LockInfo lock : info.getLockedSynchronizers()) { - trace.append(" ").append(lock.toString()).append(NEW_LINE); - } - for (MonitorInfo monitor : info.getLockedMonitors()) { - trace.append(" ").append(monitor.toString()).append(NEW_LINE); - } - return trace.toString(); - } - - /** - * Print the thread's stack trace. - * - * @param info - * the {@code:ThreadInfo} for the thread in question, must not be {@code:null} - * @return the thread stack trace - */ - @SuppressWarnings("PMD.InsufficientStringBufferDeclaration") - private static String getCallStackTrace(final ThreadInfo info) { - StringBuilder trace = new StringBuilder(" Stack:").append(NEW_LINE); - int frameCount = 0; - for (StackTraceElement frame : info.getStackTrace()) { - trace.append(" ").append(frame.toString()).append(NEW_LINE); - // Can't request lock information AND limit call stack size with only one ThreadInfo request. Fake it instead. - if (frameCount++ >= STACK_DEPTH) { - trace.append(" ...").append(NEW_LINE); - break; - } - } - return trace.toString(); - } - - /** - * Print all of the thread's information and stack traces. - * - * @return the thread info - */ - private static String getThreadInfo() { - boolean contention = THREAD_BEAN.isThreadContentionMonitoringEnabled(); - Set deadlockedThreads = getDeadlockThreadIds(); - ThreadInfo[] threadInfos = THREAD_BEAN.dumpAllThreads(true, true); - // CHECKSTYLE:OFF MagicNumber - StringBuilder trace = new StringBuilder(200).append(threadInfos.length).append(" active threads").append(NEW_LINE); - // CHECKSTYLE:ON - for (ThreadInfo info : threadInfos) { - if (info == null) { - trace.append(" Inactive").append(NEW_LINE); - continue; - } - boolean isDeadlocked = deadlockedThreads.contains(info.getThreadId()); - trace.append("Thread ").append(getTaskName(info.getThreadId(), info.getThreadName())); - if (isDeadlocked) { - trace.append(" <>"); - } - trace.append(NEW_LINE); - Thread.State state = info.getThreadState(); - trace.append(" State: ").append(state).append(NEW_LINE); - trace.append(" Blocked count: ").append(info.getBlockedCount()).append(NEW_LINE); - trace.append(" Waited count: ").append(info.getWaitedCount()).append(NEW_LINE); - if (contention) { - trace.append(" Blocked time: ").append(info.getBlockedTime()).append(NEW_LINE); - trace.append(" Waited time: ").append(info.getWaitedTime()).append(NEW_LINE); - } - if (state == Thread.State.WAITING) { - trace.append(" Waiting on ").append(info.getLockName()).append(NEW_LINE); - } else if (state == Thread.State.BLOCKED) { - trace.append(" Blocked on ").append(info.getLockName()).append(NEW_LINE); - trace.append(" Blocked by ").append(getTaskName(info.getLockOwnerId(), info.getLockOwnerName())).append(NEW_LINE); - } - if (state == Thread.State.WAITING || state == Thread.State.BLOCKED || isDeadlocked) { - trace.append(getOwnedLockInfo(info)); - } - trace.append(getCallStackTrace(info)); - } - return trace.toString(); - } - - /** - * Processes the given screenshot by adding corresponding information to the image. - * - * @param image - * the {@link BufferedImage}, must not be {@code null} - */ - private void process(final BufferedImage image) { - if (image == null) { - return; - } - final Graphics2D graphics = image.createGraphics(); - // draw mouse location - if (mouseEvent != null) { - drawCrosshair(graphics, mouseEvent.x, mouseEvent.y); - } - // draw capture information - int x = image.getWidth() / 2; - int y = 0; - drawInfoBox(graphics, testClassName + "." + testMethodName, x, y); - if (testStepStarted && currentTestStep != null && currentTestStepState != null) { - y += getInfoBoxHeight() + INFO_BORDER_SIZE; - drawInfoBox(graphics, currentTestStep.getName() + ": " + currentTestStepState.toString(), x, y); - if (testFailed && exception != null) { - final String message = exception.getMessage(); - if (message != null) { - y += getInfoBoxHeight() + INFO_BORDER_SIZE; - drawInfoBox(graphics, exception.getClass().getSimpleName() + ": " + message, x, y); - } - } - } - } - - /** - * Draws a crosshair at the given screen coordinates on the captured image. - * - * @param graphics - * the {@link Graphics2D}, must not be {@code null} - * @param x - * the x screen coordinate - * @param y - * the y screen coordinate - */ - private void drawCrosshair(final Graphics2D graphics, final int x, final int y) { - if (mouseClicked) { - graphics.setColor(Color.GREEN); - } else if (mousePressedDown) { - graphics.setColor(Color.RED); - } else { - graphics.setColor(Color.BLUE); - } - graphics.drawOval(x - MOUSE_POINTER_SIZE / 2, y - MOUSE_POINTER_SIZE / 2, MOUSE_POINTER_SIZE, MOUSE_POINTER_SIZE); - graphics.drawOval(x - MOUSE_POINTER_SIZE / 2 + 1, y - MOUSE_POINTER_SIZE / 2 + 1, MOUSE_POINTER_SIZE - 2, MOUSE_POINTER_SIZE - 2); - graphics.drawLine(x, y - MOUSE_POINTER_SIZE / 2, x, y + MOUSE_POINTER_SIZE / 2); - graphics.drawLine(x - MOUSE_POINTER_SIZE / 2, y, x + MOUSE_POINTER_SIZE / 2, y); - } - - /** - * Draws a black box with an info text on the captured image. - * - * @param graphics - * the {@link Graphics2D}, must not be {@code null} - * @param info - * the text to draw, must not be {@code null} - * @param x - * the center x coordinate of the box on the screenshot image - * @param y - * the top y coordinate of the box on the screenshot image - */ - private void drawInfoBox(final Graphics2D graphics, final String info, final int x, final int y) { - graphics.setFont(new Font("Arial", Font.PLAIN, INFO_FONT_SIZE)); - final int stringWidth = graphics.getFontMetrics().stringWidth(info); - final int leftCoord = x - stringWidth / 2; - graphics.setColor(Color.BLACK); - graphics.fillRoundRect(leftCoord - INFO_BORDER_SIZE, y, stringWidth + 2 * INFO_BORDER_SIZE, INFO_FONT_SIZE - + 2 * INFO_BORDER_SIZE, INFO_BORDER_SIZE, INFO_BORDER_SIZE); - if (testFailed) { - graphics.setColor(Color.RED); - } else { - graphics.setColor(Color.GREEN); - } - graphics.drawString(info, leftCoord, y + INFO_FONT_SIZE + INFO_BORDER_SIZE); - } - - /** - * Returns the height an info box has. - * - * @return the height an info box has - */ - private int getInfoBoxHeight() { - return INFO_FONT_SIZE + 2 * INFO_BORDER_SIZE; - } - - /** - * Stores the captured image. - * - * @param image - * the {@link BufferedImage}, must not be {@code null} - * @throws IOException - * if the image could not be written to file. - */ - private void store(final BufferedImage image) throws IOException { - final File screenshotDirectory = new File(getScreenshotDirectory()); - screenshotDirectory.mkdirs(); - ImageIO.write(image, IMAGE_TYPE, new File(getImageFileName())); - } - - /** - * Stores the call stack. - * - * @param trace - * the call stack information to store, must not be {@code null} - * @throws IOException - * if the call stack could not be written to file. - */ - private void storeCallStack(final String trace) throws IOException { - // Put it next to screenshots (intentional) - final File screenshotDirectory = new File(getScreenshotDirectory()); - screenshotDirectory.mkdirs(); - PrintWriter out = new PrintWriter(getCallStackFileName(), StandardCharsets.UTF_8); - out.print(trace); - out.close(); - } - - /** - * Returns the image file name to use for the current screenshot. - * - * @return the image file name to use for the current screenshot, never {@code null} - */ - private String getImageFileName() { - return getFileNamePrefix() + IMAGE_FILE_EXTENSION; - } - - /** - * Returns the image file name to use for the current screenshot. - * - * @return the image file name to use for the current screenshot, never {@code null} - */ - private String getCallStackFileName() { - return getFileNamePrefix() + CALL_STACK_FILE_EXTENSION; - } - - /** - * Returns a prefix for a file name. - * - * @return Prefix for trace file names - */ - private String getFileNamePrefix() { - final StringBuilder result = new StringBuilder(getScreenshotDirectory()); - final Date date = new Date(lastCaptureTime); // NOPMD ReplaceJavaUtilDate - final DateFormat formatter = new SimpleDateFormat("HH.mm.ss.SSS", Locale.UK); - final String formattedDate = formatter.format(date); - result.append(formattedDate); - if (testStepStarted && currentTestStep != null) { - result.append(FILE_INFORMATION_SEPARATOR).append(currentTestStep.getName()); - if (currentTestStepState != null) { - result.append(FILE_INFORMATION_SEPARATOR).append(currentTestStepState.toString()); - } - } else if (testFailed) { - result.append("__FAILED"); - } - return result.toString(); - } - - /** - * Returns the screenshot directory for the current test method. - * - * @return the screenshot directory for the current test method, never {@code null} - */ - private String getScreenshotDirectory() { - return getTestClassScreenshotDirectory() + testMethodName + File.separatorChar; - } - - /** - * Returns the screenshot directory for the current test class. - * - * @return the screenshot directory for the current test class, never {@code null} - */ - private String getTestClassScreenshotDirectory() { - return screenshotFolder + File.separatorChar + testClassName + File.separatorChar; - } - - /** - * Deletes all screenshots taken with this {@link TestRunRecording} for the current test. - * If the containing folder remains empty, then it is removed too. - */ - private void deleteScreenshots() { - deleteDirectory(new File(getScreenshotDirectory())); - // delete the directory of the test class too (works only if there are no other test method directories) - new File(getTestClassScreenshotDirectory()).delete(); - } - - /** - * Deletes a directory and all its children. - * - * @param path - * the {@link File} folder to delete - * @return {@code true} if successful, {@code false} otherwise - */ - private boolean deleteDirectory(final File path) { - final boolean success = deleteDirectoryContents(path); - return success && path.delete(); - } - - /** - * Deletes all contents (recursively) of a directory. - * - * @param path - * the {@link File} folder for which the contents are to be deleted - * @return {@code true} if successful, {@code false} otherwise - */ - private boolean deleteDirectoryContents(final File path) { - boolean success = true; - if (path.exists()) { - final File[] files = path.listFiles(); - for (final File file : files) { - if (file.isDirectory()) { - success &= deleteDirectory(file); - } else { - success &= file.delete(); - } - } - } - return success; - } - - @Override - public void mouseDoubleClick(final MouseEvent e) { - // do nothing special, it will be displayed as 1 total click. - } - - @Override - public void mouseDown(final MouseEvent e) { - mouseEvent = e; - mousePressedDown = true; - } - - @Override - public void mouseUp(final MouseEvent e) { - mousePressedDown = false; - mouseEvent = e; - mouseClicked = true; - createScreenshot(); - createCallStack(); - mouseClicked = false; - } -} diff --git a/com.avaloq.tools.ddk.xtext.export.test/src/com/avaloq/tools/ddk/xtext/test/export/ExportTestSuite.java b/com.avaloq.tools.ddk.xtext.export.test/src/com/avaloq/tools/ddk/xtext/test/export/ExportTestSuite.java index 767126c1d..6439fdfb3 100644 --- a/com.avaloq.tools.ddk.xtext.export.test/src/com/avaloq/tools/ddk/xtext/test/export/ExportTestSuite.java +++ b/com.avaloq.tools.ddk.xtext.export.test/src/com/avaloq/tools/ddk/xtext/test/export/ExportTestSuite.java @@ -16,6 +16,7 @@ import com.avaloq.tools.ddk.xtext.export.exporting.ExportExportingTest; import com.avaloq.tools.ddk.xtext.export.formatting.ExportFormattingTest; import com.avaloq.tools.ddk.xtext.export.scoping.ExportScopingTest; +import com.avaloq.tools.ddk.xtext.export.validation.ExportValidationOkTest; import com.avaloq.tools.ddk.xtext.export.validation.ExportValidationTest; @@ -23,6 +24,6 @@ * Empty class serving only as holder for JUnit4 annotations. */ @Suite -@SelectClasses({ExportFormattingTest.class, ExportValidationTest.class, ExportScopingTest.class, ExportExportingTest.class}) +@SelectClasses({ExportFormattingTest.class, ExportValidationTest.class, ExportValidationOkTest.class, ExportScopingTest.class, ExportExportingTest.class}) public class ExportTestSuite { } diff --git a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/test/generator/GeneratorTestSuite.java b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/test/generator/GeneratorTestSuite.java index 337612d31..efbd202e4 100644 --- a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/test/generator/GeneratorTestSuite.java +++ b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/test/generator/GeneratorTestSuite.java @@ -18,6 +18,7 @@ import com.avaloq.tools.ddk.xtext.generator.expression.ExpressionsExtentionsTest; import com.avaloq.tools.ddk.xtext.generator.test.util.EClassComparatorTest; import com.avaloq.tools.ddk.xtext.generator.test.util.GraphTest; +import com.avaloq.tools.ddk.xtext.generator.xbase.test.XbaseGeneratorFragmentTest; /** @@ -30,7 +31,8 @@ CompilationContextTest.class, ExpressionsExtentionsTest.class, EClassComparatorTest.class, - GraphTest.class + GraphTest.class, + XbaseGeneratorFragmentTest.class // @Format-On }) diff --git a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/xbase/test/XbaseGeneratorFragmentTest.xtend b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/xbase/test/XbaseGeneratorFragmentTest.xtend index 54acad8d6..9a95fd615 100644 --- a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/xbase/test/XbaseGeneratorFragmentTest.xtend +++ b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/xbase/test/XbaseGeneratorFragmentTest.xtend @@ -13,7 +13,11 @@ package com.avaloq.tools.ddk.xtext.generator.xbase.test import com.avaloq.tools.ddk.test.core.jupiter.BugTest import org.eclipse.emf.common.util.BasicEList +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.ecore.EClass import org.eclipse.emf.ecore.EPackage +import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.emf.ecore.resource.ResourceSet import org.eclipse.xtext.AbstractElement import org.eclipse.xtext.AbstractMetamodelDeclaration import org.eclipse.xtext.AbstractRule @@ -25,6 +29,8 @@ import org.eclipse.xtext.RuleCall import org.eclipse.xtext.TypeRef import org.eclipse.xtext.XtextPackage +import static org.mockito.ArgumentMatchers.any +import static org.mockito.ArgumentMatchers.eq import static org.mockito.Mockito.mock import static org.mockito.Mockito.when import org.eclipse.xtext.xtext.generator.xbase.XbaseUsageDetector @@ -52,6 +58,16 @@ class XbaseGeneratorFragmentTest { val detector = new XbaseUsageDetector + // Shared EClass instance used as the resolved Xtype::XImportSection. EcoreUtil2.isAssignableFrom + // returns true via reference equality when the rule's type classifier is this same instance. + val EClass mockXtypeXImportSectionEClass = mock(EClass) + val ResourceSet mockResourceSet = mock(ResourceSet) => [ + when(getEObject(any(URI), eq(true))).thenReturn(mockXtypeXImportSectionEClass) + ] + val Resource mockResource = mock(Resource) => [ + when(resourceSet).thenReturn(mockResourceSet) + ] + /** * Set expectations prior to calling usesXImportSection.apply(). * @@ -70,6 +86,19 @@ class XbaseGeneratorFragmentTest { when(mockType.metamodel).thenReturn(mockMetamodel) when(mockMetamodel.EPackage).thenReturn(mockEPackage) when(mockEPackage.name).thenReturn(packageName) + + // The current XbaseUsageDetector navigates rule.eResource().getResourceSet() to look up the + // Xtype XImportSection EClass, then compares it (via EcoreUtil2.isAssignableFrom) against the + // rule's type classifier. Treat (xtypePackageName + XImportSection) as the canonical match + // by giving such rules the same EClass instance the resource-set lookup returns; everything + // else gets a fresh distinct mock so isAssignableFrom evaluates false. + when(mockRule.eResource).thenReturn(mockResource) + val EClass classifier = if (xtypePackageName.equals(packageName) && xImportSectionRuleName.equals(ruleName)) { + mockXtypeXImportSectionEClass + } else { + mock(EClass) + } + when(mockType.classifier).thenReturn(classifier) } /** diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractTest.java deleted file mode 100644 index 9b3115d2f..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractTest.java +++ /dev/null @@ -1,462 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.common.util.WrappedException; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.actions.WorkspaceModifyOperation; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.runner.RunWith; - -import com.avaloq.tools.ddk.test.core.AfterAll; -import com.avaloq.tools.ddk.test.core.BeforeAll; -import com.avaloq.tools.ddk.test.core.BugTestAwareRule; -import com.avaloq.tools.ddk.test.core.IssueAwareRule; -import com.avaloq.tools.ddk.test.core.LoggingRule; -import com.avaloq.tools.ddk.test.core.mock.ExtensionRegistryMock; -import com.avaloq.tools.ddk.test.core.mock.ServiceMock; -import com.avaloq.tools.ddk.xtext.test.junit.runners.XtextClassRunner; -import com.google.common.collect.ImmutableList; - - -/** - * Provides a test class specific custom test framework for tests that run in the ACF environment. - * All exceptions are wrapped and handed over to the JUnit framework. - */ -@RunWith(XtextClassRunner.class) -@SuppressWarnings("nls") -public abstract class AbstractTest { - - /** - * Prefix for customer sources. - * Consider also: com.avaloq.tools.asmd.testbase.TestUtil.CUSTR_PREFIX - * The duplicated definition of the prefix must be harmonized based on harmonization of test plugins. - */ - protected static final String CUSTOMER_SOURCE_PREFIX = "custr_"; - - protected static final String PROJECT_NAME = "SDK"; - - private static final String HIGH_LATENCY_PROPERTY = "com.avaloq.tools.hl.supported"; - - private static final String LANGUAGE_VERSION_CACHING_PROPERTY = "com.avaloq.tools.LanguageConfigCaching"; - - private static Map, TestInformation> testInformationMap = new HashMap, TestInformation>(); - - @Rule - // CHECKSTYLE:CHECK-OFF Visibility MethodRules cannot be private - public final LoggingRule watchman = LoggingRule.getInstance(); - // CHECKSTYLE:CHECK-ON Visibility - - /** - * Enables support for unresolved bug tests. - */ - @Rule - // CHECKSTYLE:CHECK-OFF Visibility MethodRules cannot be private - public BugTestAwareRule bugTestRule = BugTestAwareRule.getInstance(); - // CHECKSTYLE:CHECK-ON Visibility - - @Rule - // CHECKSTYLE:CHECK-OFF VisibilityModifier - public final IssueAwareRule issueRule = IssueAwareRule.getInstance(); - - // CHECKSTYLE:CHECK-ON VisibilityModifier - - protected ITestProjectManager getTestProjectManager() { - return getTestUtil().getTestProjectManager(); - } - - /** - * Returns the URI for the given target source file. - * - * @param fullSourceName - * full source name - * @return URI of source - */ - public URI getTargetSourceUri(final String fullSourceName) { - return getTestProjectManager().createTestSourceUri(fullSourceName); - } - - /** - * Returns a list of all kernel source file names that this test will require. - * This can be overridden to extend or replace the returned list. - * - * @return list of all required kernel source file names - */ - protected List getRequiredSourceFileNames() { - List requiredSources = new LinkedList(); - String testSourceFileName = getTestSourceFileName(); - if (testSourceFileName != null && testSourceFileName.length() > 0) { - requiredSources.add(testSourceFileName); - } - return requiredSources; - } - - /** - * Registers all required sources for this test. - * This method can be overridden to register other required source files. - */ - protected void registerRequiredSources() { - addSourcesToWorkspace(getRequiredSourceFileNames()); - } - - /** - * Non-static instance set up before all tests. - */ - @BeforeAll - public final void setUp() { - synchronized (testInformationMap) { - if (!testInformationMap.containsKey(this.getClass())) { - testInformationMap.put(this.getClass(), new TestInformation()); - beforeAllTests(); - } - } - } - - /** - * Non-static instance tear down after all tests. - */ - @AfterAll - public final void tearDown() { - synchronized (testInformationMap) { - afterAllTests(); - ExtensionRegistryMock.assertUnMocked(); - ServiceMock.assertAllMocksRemoved(); - testInformationMap.remove(this.getClass()); - } - } - - /** - * This method prepares the test environment for a test. It is called by the JUnit framework before each test. - * If it is run the first time, it calls the beforeClass method first. Do not call this method manually! - * All exceptions are wrapped and handed over to the JUnit framework. - */ - @Before - public final void before() { - beforeEachTest(); - } - - /** - * This method cleans up the test environment after a test. It is called by the JUnit framework after each test. - * If no more tests are to be run, it calls the afterClass method. Do not call this method manually! - * All exceptions are wrapped and handed over to the JUnit framework. - */ - @After - public final void after() { - afterEachTest(); - } - - /** - * Prepares the test class after the test class has been instantiated. This method can be used to setup the test class before any test is run. - * Do not use JUnit annotation. - * Exceptions are wrapped and handed over to the JUnit framework. - */ - protected void beforeAllTests() { - // check method annotations to ensure test framework policies - System.setProperty(HIGH_LATENCY_PROPERTY, Boolean.FALSE.toString()); - System.setProperty(LANGUAGE_VERSION_CACHING_PROPERTY, Boolean.FALSE.toString()); - enforceAnnotationPolicies(); - getTestProjectManager().setup(ImmutableList. of()); - registerRequiredSources(); - getTestProjectManager().build(); - } - - /** - * After the last task has run but before the test class instance has been garbage collected, this method is called to clean up the test environment. - * Do not use JUnit annotation. - * All exceptions are wrapped and handed over to the JUnit framework. - */ - protected void afterAllTests() { - getTestProjectManager().teardown(); - System.clearProperty(LANGUAGE_VERSION_CACHING_PROPERTY); - System.setProperty(HIGH_LATENCY_PROPERTY, Boolean.TRUE.toString()); - } - - /** - * Prepares for the next test. This method can be used to (re-)initialize the test environment before a (next) test is run. Resource allocations must be dealt - * with in {@link afterEachTest}. - * Do not use JUnit annotation. - * All exceptions are wrapped and handed over to the JUnit framework. - */ - @SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract") - protected void beforeEachTest() { - // empty - } - - /** - * Called after each test to clean up initializations done in {@link beforeEachTest}. - * Do not use JUnit annotation. - * All exceptions are wrapped and handed over to the JUnit framework. - */ - @SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract") - protected void afterEachTest() { - // empty - } - - /** - * Registers a source that is required by the test class. - * The source will be removed from the system when {@link afterAllTests} is called. - * All exceptions are wrapped and handed over to the JUnit framework. - * - * @param sourceFileName - * the name of the file where the source is located, and where the content of the source shall be written to. This is the source name available - * during the test. - */ - protected void addSourceToWorkspace(final String sourceFileName) { - addSourceToWorkspace(sourceFileName, getResourceContent(sourceFileName)); - } - - /** - * Registers a kernel source that is required by the test class. - * The source will be removed from the system when {@link afterAllTests} is called. - * All exceptions are wrapped and handed over to the JUnit framework. - * - * @param sourceFileName - * the name of the file where the content of the source shall be written to. This is the source name available - * during the test. - * @param sourceContent - * the content of the source that shall be written to the file in workspace. - */ - protected void addKernelSourceToWorkspace(final String sourceFileName, final CharSequence sourceContent) { - addSourceToWorkspace(sourceFileName, sourceContent.toString()); - } - - /** - * Registers a customer source that is required by the test class. - * The source will be removed from the system when {@link afterAllTests} is called. - * All exceptions are wrapped and handed over to the JUnit framework. - * - * @param sourceFileName - * the name of the file where the content of the source shall be written to. This is the source name available - * during the test. - * @param sourceContent - * the content of the source that shall be written to the file in workspace. - */ - protected void addCustomerSourceToWorkspace(final String sourceFileName, final CharSequence sourceContent) { - addSourceToWorkspace(CUSTOMER_SOURCE_PREFIX.concat(sourceFileName), sourceContent.toString()); - } - - /** - * Registers a source that is required by the test class. - * The source will be removed from the system when {@link afterAllTests} is called. - * All exceptions are wrapped and handed over to the JUnit framework. - * - * @param sourceFileName - * the name of the file where the content of the source shall be written to. This is the source name available - * during the test. - * @param sourceContent - * the content of the source that shall be written to the file in workspace. - */ - private void addSourceToWorkspace(final String sourceFileName, final String sourceContent) { - createTestSource(sourceFileName, sourceContent); - } - - /** - * Returns the string contents of the loaded resource with the given name. - * - * @param sourceFileName - * the file name - * @return the string contents of the loaded resource - */ - protected String getResourceContent(final String sourceFileName) { - return TestSource.getResourceContent(this.getClass(), sourceFileName); - } - - /** - * Registers a set of sources that is required by the test class. - * The sources will be removed from the system when {@link afterAllTests} is called. - * All exceptions are wrapped and handed over to the JUnit framework. - * - * @param sourceFileNames - * the names of the files where the sources are located, and where the content of the sources shall be written to. - */ - private void addSourcesToWorkspace(final List sourceFileNames) { - try { - new WorkspaceModifyOperation() { - @Override - protected void execute(final IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { - for (String sourceFileName : sourceFileNames) { - addSourceToWorkspace(sourceFileName); - } - } - }.run(new NullProgressMonitor()); - } catch (InvocationTargetException e) { - throw new WrappedException("failed adding sources to workspace", e); - } catch (InterruptedException e) { - throw new WrappedException("adding sources to workspace interrupted", e); - } - } - - protected Collection getTestSources() { - return getTestProjectManager().getTestSources(); - } - - /** - * Returns the kernel {@link TestSource} for the given sourceFileName. - * - * @param sourceFileName - * the file name of the {@link TestSource} - * @return the {@link TestSource} for the given sourceFileName - */ - protected XtextTestSource getTestSource(final String sourceFileName) { - return (XtextTestSource) getTestProjectManager().getTestSource(sourceFileName); - } - - /** - * Returns the kernel {@link TestSource} for this test class. - * - * @return the {@link TestSource} for this test class - */ - protected TestSource getTestSource() { - return getTestProjectManager().getTestSource(getTestSourceFileName()); - } - - /** - * Get the name of the main test source file. - * - * @return the file name of the main test source file - */ - protected abstract String getTestSourceFileName(); - - /** - * The default implementation returns the name of the test class for the model name of the test source. - * A test class needs to override this, if the name of the main test source model differs from the default. - * - * @return the name of the main test source model - */ - protected String getTestSourceModelName() { - return this.getClass().getSimpleName(); - } - - /** - * Goes through all methods of this object and checks the annotations. - *

- * Methods with a {@link @BeforeClass} or {@link @AfterClass} annotation will cause an exception to be thrown. For that purpose, use the {@link - * beforeAllTests()} and {@link afterAllTests()} methods only. - *

- */ - private void enforceAnnotationPolicies() { - for (Method method : this.getClass().getMethods()) { - // use this policy to not allow BeforeClass or AfterClass annotations. - if (method.isAnnotationPresent(BeforeClass.class) || method.isAnnotationPresent(AfterClass.class)) { - throw new IllegalJUnitAnnotation(); - } - // use this policy to not allow Before or After annotations in subclasses. - // if (!method.getDeclaringClass().equals(AbstractXtextTest.class) && (method.isAnnotationPresent(Before.class) || - // method.isAnnotationPresent(After.class))) { - // throw new - // IllegalJUnitAnnotation("Invalid annotation found. Before and After annotations are not permitted when using the AbstractXtextTest framework. Override - // the methods 'before' and 'after' instead."); - // } - } - } - - /** - * Wait for validation jobs to finish. - */ - protected void waitForValidation() { - waitForJobsOfFamily(org.eclipse.xtext.ui.editor.validation.ValidationJob.XTEXT_VALIDATION_FAMILY); - } - - /** - * Wait for jobs of a given family to finish. - * - * @param family - * to wait for. - */ - protected void waitForJobsOfFamily(final Object family) { - getTestUtil().waitForJobsOfFamily(family); - } - - /** - * Wait for synchronization jobs on opening/closing the editor. - * - * @param editor - * editor part - */ - protected void waitForEditorJobs(final IEditorPart editor) { - getTestUtil().waitForEditorJobs(editor); - } - - /** - * Wait for jobs of a given family to appear. A {@code null} family will - * cause this to wait for any job. - * - * @param family - * to wait for, may be {@code null} - * @param timeout - * ms to wait for. - */ - protected void waitForJobOfFamilyToAppear(final Object family, final long timeout) { - final long timeLimit = System.currentTimeMillis() + timeout; - do { - if (Job.getJobManager().find(family).length > 0) { - return; - } - } while (System.currentTimeMillis() < timeLimit); - } - - /** - * Returns the test information for the current test class. - * - * @return information for the current test class - */ - protected TestInformation getTestInformation() { - synchronized (testInformationMap) { - return testInformationMap.get(this.getClass()); - } - } - - /** - * Create a test source for testing from an existing file. - * - * @param sourceFileName - * file name for source - * @param content - * content of source - * @return a new {@link TestSource} with the given parameters - */ - - protected TestSource createTestSource(final String sourceFileName, final String content) { - TestSource testSource = new TestSource(sourceFileName, content); - getTestProjectManager().addSourceToProject(testSource); - return testSource; - } - - /** - * Get the test class utility for this test. The minimum functionality is given by - * AbstractTestUtil, which does not require that any methods be overridden. Tests - * that require more than this minimal functionality must override this method. - *

- * This method is expected to always return the same instance, even when invoked on different instances of the test class. This is because the associated - * {@link ITestProjectManager} is stateful and required by {@link #beforeAllTests()}, {@link #afterAllTests()}, and {@link #getTestSources()}. - * - * @return the test class utility for this test. - */ - protected abstract AbstractTestUtil getTestUtil(); - -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextMarkerBasedTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextMarkerBasedTest.java deleted file mode 100644 index 8d21509bc..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextMarkerBasedTest.java +++ /dev/null @@ -1,656 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.SortedMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.CrossReference; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; -import org.eclipse.xtext.xbase.lib.Procedures; -import org.junit.Assert; - -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; - - -/** - * Abstract class that supports Xtend based test implementations to use markers in the test sources. - */ -@SuppressWarnings("nls") -public abstract class AbstractXtextMarkerBasedTest extends AbstractXtextTest { - - private static final String INVALID_TEST_CONFIGURATION = "Invalid test configuration. Missing org.eclipse.xtend.lib in MANIFEST.MF in this plugin?"; //$NON-NLS-1$ - private static final String LINE_BREAK = "\n"; - private static final String MARKER_START_GUARD = "##"; - private static final String MARKER_END_GUARD = "#"; - private static final String SPLITTING_LINE = "-------------------------------------------------\n"; - protected static final Pattern PATTERN = Pattern.compile(MARKER_START_GUARD + "(\\d+)" + MARKER_END_GUARD); - - /** The tag id. */ - private int localMarkerIdCounter; - - private final Map assertions = Maps.newHashMap(); - private final SortedMap errorsOnPosition = Maps.newTreeMap(); - /** Used Tags to find Duplicates. */ - private final Set usedTags = Sets.newHashSet(); - - /** - * Indicates if a testing source is a kernel or customer source. - */ - protected enum TestSourceType { - CLIENT_ALL, - CLIENT_CUSTOMER - } - - // -------------------------------------------------------------------------- - // AbstractModelAssertion - // -------------------------------------------------------------------------- - - /** - * Interface for testing assertions on a given source position. - */ - protected abstract class AbstractModelAssertion implements Procedures.Procedure2 { - - @Override - public abstract void apply(EObject semanticModel, Integer pos); - - } - - // -------------------------------------------------------------------------- - // Methods of testing framework - // -------------------------------------------------------------------------- - - @Override - protected void afterEachTest() { - getMarkerTagsInfo().clearTags(localMarkerIdCounter); - super.afterEachTest(); - assertions.clear(); - usedTags.clear(); - errorsOnPosition.clear(); - } - - @Override - protected void beforeEachTest() { - localMarkerIdCounter = 0; - super.beforeEachTest(); - Assert.assertFalse(INVALID_TEST_CONFIGURATION, getMarkerTagsInfo().isInvalidTestClass()); - } - - // -------------------------------------------------------------------------- - // Methods to be used by the actual testing classes - // -------------------------------------------------------------------------- - - /** - * {@inheritDoc} - */ - @Override - protected void addKernelSourceToWorkspace(final String sourceFileName, final CharSequence sourceContent) { - String processedContent = processContentAndRegisterOffsets(sourceFileName, sourceContent); - super.addKernelSourceToWorkspace(sourceFileName, processedContent); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addCustomerSourceToWorkspace(final String sourceFileName, final CharSequence sourceContent) { - String processedContent = processContentAndRegisterOffsets(CUSTOMER_SOURCE_PREFIX + sourceFileName, sourceContent); - super.addCustomerSourceToWorkspace(sourceFileName, processedContent); - } - - /** - * Removes tags and stores them in the test info object. - * - * @param sourceFileName - * Source file name, must not be {@code null} - * @param sourceContent - * Content of the test source (may contain tags), must not be {@code null} - * @return Content without tags, never {@code null} - */ - private String processContentAndRegisterOffsets(final String sourceFileName, final CharSequence sourceContent) { - Map offsets = Maps.newHashMap(); - String content = removeMarkersFromContent(sourceContent, offsets); - for (Entry tag : offsets.entrySet()) { - getMarkerTagsInfo().registerRequiredSourceTag(tag.getKey(), sourceFileName, tag.getValue()); - } - return content; - } - - /** - * Removes the Xtend markers from a source. - * - * @param sourceContent - * the source content, not {@code null} - * @param tagToOffset - * Map to be populated with tag to offset pairs, must not be {@code null} - * @return the content without markers, never {@code null} - */ - private String removeMarkersFromContent(final CharSequence sourceContent, final Map tagToOffset) { - StringBuffer withoutMarkers = new StringBuffer(sourceContent.length()); - Matcher m = PATTERN.matcher(sourceContent); - while (m.find()) { - m.appendReplacement(withoutMarkers, ""); - tagToOffset.put(Integer.parseInt(m.group(1)), withoutMarkers.length()); - } - m.appendTail(withoutMarkers); - return withoutMarkers.toString(); - } - - /** - * Returns the model for the given source name and string. - * - * @param sourceFileName - * the file name that should be associated with the parsed content, must not be {@code null} - * @param sourceContent - * source, must not be {@code null} - * @return Model element for the parsed source, may be {@code null} - */ - protected EObject getModel(final String sourceFileName, final CharSequence sourceContent) { - Map offsets = Maps.newHashMap(); - String content = removeMarkersFromContent(sourceContent, offsets); - EObject root = null; - try { - root = getXtextTestUtil().getModel(sourceFileName, content); - INode node = NodeModelUtils.getNode(root); - for (Entry tag : offsets.entrySet()) { - INode leafNode = NodeModelUtils.findLeafNodeAtOffset(node, tag.getValue() + 1); - EObject context = NodeModelUtils.findActualSemanticObjectFor(leafNode); - // Search for cross reference - CrossReference crossReference = null; - while (leafNode != null) { - if (leafNode.getGrammarElement() instanceof CrossReference) { - crossReference = (CrossReference) leafNode.getGrammarElement(); - break; - } - leafNode = leafNode.getParent(); - } - getMarkerTagsInfo().registerLocalTag(tag.getKey(), context, crossReference); - } - } catch (IOException e) { - fail("Exception while creating model from input string: " + e.getMessage()); //$NON-NLS-1$ - } - return root; - } - - /** - * Does the same as get model, but returns void. - * - * @param sourceFileName - * the file name that should be associated with the parsed content - * @param sourceContent - * source - */ - protected void registerModel(final String sourceFileName, final CharSequence sourceContent) { - getModel(sourceFileName, sourceContent); - } - - /** - * Validate a kernel source given by a file name and content. - * All not expected diagnostics are ignored. - * - * @param sourceFileName - * the file name that should be associated with the parsed content, not {@code null} - * @param sourceContent - * source, not {@code null} - */ - protected void validateKernelSource(final String sourceFileName, final CharSequence sourceContent) { - validate(sourceFileName, TestSourceType.CLIENT_ALL, sourceContent); - } - - /** - * Validate a customer source given by a file name and content. - * - * @param sourceFileName - * the file name that should be associated with the parsed content, not {@code null} - * @param sourceContent - * source, not {@code null} - */ - protected void validateCustomerSource(final String sourceFileName, final CharSequence sourceContent) { - validate(sourceFileName, TestSourceType.CLIENT_CUSTOMER, sourceContent); - } - - // -------------------------------------------------------------------------- - // Methods to be used by more specific abstract classes - // -------------------------------------------------------------------------- - - /** - * Add assertion to the list of assertions and return the corresponding string marker. - * - * @param assertion - * assertion to be added, not {@code null} - * @return - * string marker corresponding to the added assertion - */ - protected String addAssertion(final AbstractModelAssertion assertion) { - // Assertions are given a local tag as they are local markers affecting and declared in only one source - Integer markerId = getTag(); - assertions.put(markerId, assertion); - return mark(markerId); - } - - /** - * Creates a mark with the given id. Use this method in tests to insert marks. - *

- * If the given mark id is 0 then it means that the test was not well configured. All the id-s that are generated by {@link #getTag} start with 1, all the - * global ids start with {@value com.avaloq.tools.asmd.testbase.scoping.TagCompilationParticipant#COUNTER_BASE}. Since it is unlikely to get local ids wrong, - * the most common reason for global ids to be wrong is that they were not initialized. Global ids get initialized with active annotation - * {@link com.avaloq.tools.asmd.testbase.scoping.Tag} that executes {@link com.avaloq.tools.asmd.testbase.scoping.TagCompilationParticipant}. Active - * annotations do not get executed if {@code org.eclipse.xtend.lib} is missing in {@code MANIFEST.MF}. Thus we report this common mistake to the user via an - * assertion. - *

- * - * @param id - * Mark id - * @return Mark text to be inserted in the source file, never {@code null} - */ - protected String mark(final int id) { - Assert.assertFalse("Tag with " + id + " used to mark more than one location.", usedTags.contains(id)); //$NON-NLS-1$ //$NON-NLS-2$ - usedTags.add(id); - if (id < 1) { - getMarkerTagsInfo().setTestClassInvalid(); - throw new AssertionError(INVALID_TEST_CONFIGURATION); - } - return MARKER_START_GUARD + id + MARKER_END_GUARD; - } - - /** - * Memorize an error that was detected during the validation of the current testing file. - * - * @param position - * position of the error in file, not {@code null} - * @param error - * string with error, not {@code null} - */ - protected void memorizeErrorOnPosition(final Integer position, final String error) { - if (!errorsOnPosition.containsKey(position)) { - errorsOnPosition.put(position, new StringBuilder()); - } - errorsOnPosition.get(position).append(error); - } - - /** - * Processes all the inserted markers in a given source and creates a new {@link XtextTestSource} without the markers. - * - * @param sourceFileName - * the name of the source, may be {@code null} - * @param sourceType - * the type of the source, may be {@code null} - * @param sourceContent - * the content of the source, must not be {@code null} - * @return the {@link XtextTestSource} created. - */ - protected XtextTestSource processMarkers(final String sourceFileName, final TestSourceType sourceType, final CharSequence sourceContent) { - StringBuilder withoutMarkers = new StringBuilder(); - final Multimap positionToAssertionMap = LinkedHashMultimap.create(); - Matcher m = PATTERN.matcher(sourceContent); - int lastEnd = 0; - while (m.find()) { - withoutMarkers.append(sourceContent.subSequence(lastEnd, m.start())); - lastEnd = m.end(); - int markerId = Integer.parseInt(m.group(1)); - // save the position of the marker only if we are dealing with an assertion marker - AbstractModelAssertion assertionMarker = assertions.get(markerId); - if (assertionMarker != null) { - positionToAssertionMap.put(withoutMarkers.length(), assertionMarker); - } - } - // Add the rest part of input string - withoutMarkers.append(sourceContent.subSequence(lastEnd, sourceContent.length())); - - // Calculate source name - String fullSourceFileName = ""; - if (sourceType == TestSourceType.CLIENT_CUSTOMER) { - fullSourceFileName = CUSTOMER_SOURCE_PREFIX; - } - fullSourceFileName = fullSourceFileName.concat(sourceFileName); - - EObject semanticModel; - String withoutMarkersAsString = withoutMarkers.toString(); - XtextTestSource testSource = createTestSource(fullSourceFileName, withoutMarkersAsString); - semanticModel = testSource.getModel(); - beforeApplyAssertions(testSource); - // Run validations on markers - for (Map.Entry entry : positionToAssertionMap.entries()) { - entry.getValue().apply(semanticModel, entry.getKey()); - } - return testSource; - } - - /** - * Validate a source given by a file name and content. - * - * @param sourceFileName - * the file name that should be associated with the parsed content, not {@code null} - * @param sourceType - * defines if the source is a kernel or customer source, not {@code null} - * @param sourceContent - * source, not {@code null} - */ - protected void validate(final String sourceFileName, final TestSourceType sourceType, final CharSequence sourceContent) { - XtextTestSource testSource = processMarkers(sourceFileName, sourceType, sourceContent); - processErrorsFound(testSource.getContent()); - afterValidate(); - } - - /** - * Processes all the diagnostics in a given source. - * - * @param sourceWithoutMarkers - * the source to process, must not be {@code null} - */ - protected void processErrorsFound(final String sourceWithoutMarkers) { - if (!errorsOnPosition.isEmpty()) { - // CHECKSTYLE:OFF MagicNumber - StringBuilder sb = new StringBuilder(100); - // CHECKSTYLE:ON - sb.append(memorizedErrorsToString(sourceWithoutMarkers)); - sb.append(SPLITTING_LINE); - sb.append("List of all found diagnostics:\n"); - sb.append(getAdditionalErrorMessageInformation()); - assertEquals("Errors found. Consider compare view.", sourceWithoutMarkers, sb.toString()); - } - } - - /** - * Inject memorized errors into the input file on positions where they were detected. - * - * @param source - * text of the input testing source, not {@code null} - * @return - * input testing source with injected errors, never {@code null} - */ - private String memorizedErrorsToString(final String source) { - StringBuilder result = new StringBuilder(120); - StringBuilder errorBuffer = new StringBuilder(120); - // Sort positions - List positions = Lists.newArrayList(errorsOnPosition.keySet()); - - int posIdx = 0; - int sourceIdx = 0; - - while (sourceIdx < source.length()) { - int lineBreakIndex = source.indexOf('\n', sourceIdx); - if (lineBreakIndex < 0) { - lineBreakIndex = source.length(); - } - while (posIdx < positions.size() && positions.get(posIdx) < lineBreakIndex) { - int nextPos = positions.get(posIdx); - result.append(source.substring(sourceIdx, nextPos)); - result.append("'); - // Add error message to buffer - errorBuffer.append(SPLITTING_LINE); - errorBuffer.append("FAILURE "); - errorBuffer.append(posIdx + 1); - errorBuffer.append(": "); - errorBuffer.append(errorsOnPosition.get(nextPos)); - sourceIdx = nextPos; - posIdx++; - } - if (errorBuffer.length() > 0) { - errorBuffer.append(SPLITTING_LINE); - } - result.append(source.substring(sourceIdx, lineBreakIndex)); - result.append(errorBuffer); - errorBuffer = new StringBuilder(); - result.append(LINE_BREAK); - sourceIdx = lineBreakIndex + 1; - } - result.append(source.substring(sourceIdx)); - result.append(errorBuffer); - return result.toString(); - } - - /** - * Searches an object for the given tag. First checks local tags. If not found then searches this tag in the required sources. - * - * @param tag - * Tag - * @return EObject or {@code null} - */ - protected EObject getObjectForTag(final int tag) { - EObject object = getMarkerTagsInfo().getModel(tag); - if (object == null) { - // Not in source under test - String sourceName = getMarkerTagsInfo().getSource(tag); - if (sourceName != null) { - INode node = NodeModelUtils.findActualNodeFor(getTestSource(sourceName).getModel()); - INode leafNode = NodeModelUtils.findLeafNodeAtOffset(node, getMarkerTagsInfo().getOffset(tag)); - object = NodeModelUtils.findActualSemanticObjectFor(leafNode); - } - } - assertNotNull("Tag " + tag + " should mark an object. Use «mark(TAG)» in a code snippet.", object); //$NON-NLS-1$//$NON-NLS-2$ - return object; - } - - /** - * Return the offset for the given tag. - * - * @param tag - * The tag for which to find the offset - * @return the offset found or {@code null} if the given tag is not marking an object - */ - protected Integer getOffsetForTag(final int tag) { - Integer offset = getMarkerTagsInfo().getOffset(tag); - assertNotNull("Tag " + tag + " should mark an object. Use «mark(TAG)» in a code snippet.", offset); //$NON-NLS-1$//$NON-NLS-2$ - return offset; - } - - /** - * Generate a unique tag id. - *

- * Use this method for local tags only. Global tags must use @Tag annotation to ensure the same value over multiple instances of the test class. - *

- * - * @return Tag id - */ - public int getTag() { - localMarkerIdCounter++; - assertTrue("Too many local tags. Must be less than " + TagCompilationParticipant.COUNTER_BASE //$NON-NLS-1$ - + " per test method", localMarkerIdCounter < TagCompilationParticipant.COUNTER_BASE); //$NON-NLS-1$ - return localMarkerIdCounter; - } - - /** - * Return the {@link MarkerTagsInfo} associated to one test class. - * - * @return the associated {@link MarkerTagsInfo} - */ - protected MarkerTagsInfo getMarkerTagsInfo() { - MarkerTagsInfo info = (MarkerTagsInfo) getTestInformation().getTestObject(MarkerTagsInfo.class); - if (info == null) { - info = new MarkerTagsInfo(); - getTestInformation().putTestObject(MarkerTagsInfo.class, info); - } - return info; - } - - /** - * This class preserves information about tags in the required sources - * for all tests within one test class. Tags for current test are also stored here. - * One may prefer in the future to be able to clean tags for the current test after the test. - */ - @SuppressWarnings("PMD.PublicMemberInNonPublicType") // Public methods needed for subclass access in other packages - protected class MarkerTagsInfo { - - // For sources under test - /** The tag to model. */ - private final Map tagToModel = Maps.newHashMap(); - - /** The tag to cross reference. */ - private final Map tagToCrossReference = Maps.newHashMap(); - - // For sources added early (only object referencing, no need to search cross references) - /** The tag to source. */ - private final Map tagToSource = Maps.newHashMap(); - - /** The tag to offset. */ - private final Map tagToOffset = Maps.newHashMap(); - - private boolean invalidTestClass; - - /** - * Registers one tag for the source under test. - * This source is actually used for testing and we need both: outgoing cross references as well as declared elements. - * - * @param tag - * New id for the tag - * @param context - * Current object that will correspond to this tag - * @param crossReference - * the cross reference {@code null} for declaration or the corresponding cross reference in the grammar for a reference - */ - public void registerLocalTag(final int tag, final EObject context, final CrossReference crossReference) { - tagToModel.put(tag, context); - if (crossReference != null) { - tagToCrossReference.put(tag, crossReference); - } - } - - /** - * Register a tag in the required source. Only declarations are supported. - * - * @param tag - * New tag (must be unique) - * @param sourceName - * Source name - * @param offset - * Offset within the source - */ - public void registerRequiredSourceTag(final int tag, final String sourceName, final int offset) { - tagToSource.put(tag, sourceName); - tagToOffset.put(tag, offset); - } - - /** - * Returns the context model element for local tags. - * - * @param tag - * Tag - * @return Model element - */ - public EObject getModel(final int tag) { - return tagToModel.get(tag); - } - - /** - * Returns cross references for local tags. - * - * @param tag - * Tag - * @return Cross reference grammar element - */ - public CrossReference getCrossReference(final int tag) { - return tagToCrossReference.get(tag); - } - - /** - * Returns source name for global tag. - * - * @param tag - * Tag - * @return Source name - */ - public String getSource(final int tag) { - return tagToSource.get(tag); - } - - /** - * Returns offsets for global tags. - * - * @param tag - * Tag - * @return Offset - */ - public Integer getOffset(final int tag) { - return tagToOffset.get(tag); - } - - /** - * Clear local tags from current test. Clears all tags up to the id passed, but not outside the range reserved for local tags. - * - * @param maxId - * the tag id - */ - public void clearTags(final long maxId) { - for (int i = 1; i <= Math.min(maxId, TagCompilationParticipant.COUNTER_BASE - 1); i++) { - tagToCrossReference.remove(i); - tagToModel.remove(i); - tagToOffset.remove(i); - tagToSource.remove(i); - } - } - - public boolean isInvalidTestClass() { - return invalidTestClass; - } - - /** - * Marks the current test class as invalid. All tests within this class will report same error. - */ - public void setTestClassInvalid() { - this.invalidTestClass = true; - } - } - - /** - * Before apply assertions. - * - * @param testSource - * the test source, not {@code null} - */ - @SuppressWarnings("PMD.UnusedFormalParameter") - protected void beforeApplyAssertions(final XtextTestSource testSource) { - } - - /** - * Gets additional error message information. - * - * @return additional error message information, never {@code null} - */ - protected String getAdditionalErrorMessageInformation() { - return ""; - } - - /** - * Returns an unmodifiable view of the tags generated by {@link getTag()}. - * - * @return the unmodifiable view of the {@link #usedTags} set - */ - public Set getUsedTagsItems() { - return Collections.unmodifiableSet(usedTags); - } - - /** - * Processing after validations. - */ - protected void afterValidate() { - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextTest.java deleted file mode 100644 index 2ad786cb6..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.resource.XtextResource; - - -/** - * Provides a test class specific custom test framework for xtext languages. Provides a language specific {@link AbstractXtextTestUtil}. - * All exceptions are wrapped and handed over to the JUnit framework. - */ -public abstract class AbstractXtextTest extends AbstractTest { - - /** - * Returns a language specific {@link AbstractXtextTestUtil}. Test classes specify which TestUtil to use by implementing this method. - * - * @return a language specific {@link AbstractXtextTestUtil} - */ - protected abstract AbstractXtextTestUtil getXtextTestUtil(); - - @Override - protected final AbstractXtextTestUtil getTestUtil() { - return getXtextTestUtil(); - } - - @Override - protected XtextTestSource getTestSource() { - return (XtextTestSource) super.getTestSource(); - } - - @Override - protected XtextTestSource createTestSource(final String sourceFileName, final String content) { - XtextTestSource testSource = new XtextTestSource(sourceFileName, content, getTestUtil().getResourceSet()); - getTestProjectManager().addSourceToProject(testSource); - return testSource; - } - - /** - * The default implementation returns the name of the source model calling {@link getTestSourceModelName} and adds the default file extension for the grammar - * of this test. A test class needs to override this, if the name of the main test source file differs from the default. - * - * @return the name of the main test source file - */ - @Override - protected String getTestSourceFileName() { - return this.getTestSourceModelName() + '.' + getXtextTestUtil().getFileExtension(); - } - - /** - * Returns the xtext resource loaded by {@link loadPrimarySource}. - * - * @return - * the xtext resource loaded by {@link loadPrimarySource}. - */ - protected XtextResource getXtextTestResource() { - return getTestSource(getTestSourceFileName()).getXtextResource(); - } - - /** - * Returns the semantic model from the xtext resource loaded by {@link loadPrimarySource}. - * - * @return - * the semantic model from the xtext resource loaded by {@link loadPrimarySource}. - */ - protected EObject getSemanticModel() { - return getXtextTestResource().getParseResult().getRootASTElement(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addKernelSourceToWorkspace(final String sourceFileName, final CharSequence sourceContent) { - refreshSourceContent(sourceFileName, sourceContent.toString()); - super.addKernelSourceToWorkspace(sourceFileName, sourceContent); - } - - /** - * {@inheritDoc} - */ - @Override - protected void addCustomerSourceToWorkspace(final String sourceFileName, final CharSequence sourceContent) { - refreshSourceContent(CUSTOMER_SOURCE_PREFIX + sourceFileName, sourceContent.toString()); - super.addCustomerSourceToWorkspace(sourceFileName, sourceContent); - } - - /** - * Refresh the source content if it had already been added previously to the workspace. - * - * @param sourceFileName - * the source file name - * @param content - * the content - */ - private void refreshSourceContent(final String sourceFileName, final String content) { - XtextTestSource xtextTestSource = getTestSource(sourceFileName); - if (xtextTestSource != null) { - xtextTestSource.setContent(content); - } - } - -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextTestUtil.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextTestUtil.java deleted file mode 100644 index b6be5ddae..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextTestUtil.java +++ /dev/null @@ -1,367 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test; //NOPMD - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; - -import org.eclipse.emf.common.util.Diagnostic; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.common.util.WrappedException; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EStructuralFeature; -import org.eclipse.emf.ecore.util.Diagnostician; -import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.xtext.diagnostics.AbstractDiagnostic; -import org.eclipse.xtext.nodemodel.ICompositeNode; -import org.eclipse.xtext.nodemodel.ILeafNode; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; -import org.eclipse.xtext.resource.FileExtensionProvider; -import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.resource.XtextResourceSet; -import org.eclipse.xtext.serializer.ISerializer; -import org.eclipse.xtext.ui.editor.model.IXtextDocument; -import org.eclipse.xtext.ui.testing.util.ResourceLoadHelper; -import org.eclipse.xtext.util.StringInputStream; -import org.eclipse.xtext.validation.AbstractValidationDiagnostic; -import org.eclipse.xtext.validation.FeatureBasedDiagnostic; -import org.eclipse.xtext.validation.Issue; -import org.eclipse.xtext.validation.RangeBasedDiagnostic; -import org.eclipse.xtext.xbase.lib.Pair; - -import com.avaloq.tools.ddk.xtext.test.model.ModelUtil; -import com.avaloq.tools.ddk.xtext.test.validation.ValidationHelper; -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.inject.Injector; - - -/** - * Utility for Xtext tests. - */ -@SuppressWarnings("nls") -public abstract class AbstractXtextTestUtil extends AbstractTestUtil implements ResourceLoadHelper /* , IInjectorProvider */ { - - private final ModelUtil modelUtil = new ModelUtil(); - private final ValidationHelper validationHelper = new ValidationHelper(); - - private static final String ERROR = "ERROR %d: "; - private static final String ERROR_MARKER = ""; - private static final String SPLITTER = "------------------------\n"; - - /** - * Returns the current injector used in this plugin. - * - * @return the current injector for this language - * @deprecated this method should be implemented only and not called directly; use {@link #get(Class)} instead - */ - @Deprecated - protected abstract Injector getInjector(); - - /** - * Parses the given instance and returns the model representation. - * - * @param sourceFileName - * to associate the source with, used to determine the source content type - * @param content - * String representation of the content to parse - * @return model representation - * @throws IOException - * may be thrown if the instance cannot be parsed - */ - public final EObject getModel(final String sourceFileName, final String content) throws IOException { - XtextResource resource = getResource(getTestProjectManager().createTestSourceUri(sourceFileName), content); - return resource.getParseResult().getRootASTElement(); - } - - /** - * Creates a resource with the given URI and content. - * - * @param uri - * to associate the model with - * @param content - * String representation of the create a resource from - * @return {@link XtextResource} created - * @throws IOException - * may be thrown when trying to load the given content - */ - protected final XtextResource getResource(final URI uri, final String content) throws IOException { - StringInputStream instanceStream = new StringInputStream(content); - XtextResourceSet rs = getResourceSet(); - XtextResource resource = (XtextResource) rs.createResource(uri); - rs.getResources().add(resource); - resource.load(instanceStream, null); - EcoreUtil.resolveAll(resource); - return resource; - } - - public String getDefaultSourceName() { - return "mytestmodel"; - } - - @Override - public XtextResource getResourceFor(final InputStream stream) { - XtextResourceSet rs = getResourceSet(); - XtextResource resource = (XtextResource) rs.createResource(getTestProjectManager().createTestSourceUri(getDefaultSourceName() + '.' + getFileExtension())); - rs.getResources().add(resource); - try { - resource.load(stream, null); - } catch (IOException e) { - throw new WrappedException("Could not create XtextResource from input stream.", e); - } - EcoreUtil.resolveAll(resource); - return resource; - } - - /** - * Validates the provided document and returns a list of issues found. - * - * @param document - * to validate - * @return list of issues found - */ - public List getIssues(final IXtextDocument document) { - return validationHelper.getIssues(document); - } - - /** - * Use current injector to get a class instance. - * - * @param - * Class type - * @param clazz - * class of object to get. - * @return object of given class - */ - public T get(final Class clazz) { - return getInjector().getInstance(clazz); - } - - /** - * File extension associated with the instance's grammar. - * - * @return grammar specific file extension - */ - public String getFileExtension() { - return get(FileExtensionProvider.class).getPrimaryFileExtension(); - } - - /** - * Creates a grammar specific instance of {@link XtextResourceSet}. - * - * @return grammar specific {@link XtextResourceSet} - */ - public XtextResourceSet getResourceSet() { - return get(XtextResourceSet.class); - } - - /** - * Creates a grammar specific instance of {@link ISerializer}. - * - * @return grammar specific {@link ISerializer} - */ - public ISerializer getSerializer() { - return get(ISerializer.class); - } - - /** - * Creates a grammar specific instance of {@link Diagnostician}. - * - * @return grammar specific {@link Diagnostician} - */ - public Diagnostician getDiagnostician() { - return get(Diagnostician.class); - } - - /** - * Gets the first instance of given type containing a given structural feature with given value using a given context object. - * - * @param - * the generic type - * @param context - * the context object - * @param type - * the type - * @return the first instance found or null if none found matching given criteria - */ - public T getFirstInstanceOf(final EObject context, final Class type) { - return modelUtil.getFirstInstanceOf(context, type); - } - - /** - * Gets the all instances of given type type having given value value on structural feature feature. - * - * @param - * the generic type - * @param context - * the context - * @param type - * the type - * @param feature - * the feature - * @param value - * the value - * @return the all instances of - */ - public Iterable getAllInstancesOf(final EObject context, final Class type, final EStructuralFeature feature, final Object value) { - return modelUtil.getAllInstancesOf(context, type, feature, value); - } - - /** - * Gets the first instance of given type containing a given structural feature with given value using a given context object. - * - * @param - * the generic type - * @param context - * the context object - * @param type - * the type - * @param feature - * the structural feature - * @param value - * the value - * @return the first instance found or null if none found matching given criteria - */ - public T getFirstInstanceOf(final EObject context, final Class type, final EStructuralFeature feature, final Object value) { - return modelUtil.getFirstInstanceOf(context, type, feature, value); - } - - /** - * Validates a source with a given name and content. - * - * @param sourceFileName - * source name - * @param sourceContent - * content - */ - public void validateSource(final String sourceFileName, final CharSequence sourceContent) { - String sourceContentAsString = sourceContent.toString(); - EObject root; - try { - root = getModel(sourceFileName, sourceContentAsString); - } catch (IOException e) { - fail("Model creation failed: " + e.getMessage()); - return; - } - - // Store all the validation errors - Set errors = Sets.newHashSet(); - errors.addAll(Collections2.filter(getDiagnostician().validate(root).getChildren(), new Predicate() { - @Override - public boolean apply(final Diagnostic input) { - return input.getSeverity() == Diagnostic.ERROR; - } - })); - - Map errorMessages = Maps.newHashMap(); - for (Diagnostic diagnostic : errors) { - Pair result = processDiagnostic(diagnostic); - if (result != null) { - errorMessages.put(result.getKey(), result.getValue()); - } - } - - // Store all the resource errors - for (AbstractDiagnostic diagnostic : Iterables.filter(root.eResource().getErrors(), AbstractDiagnostic.class)) { - errorMessages.put(diagnostic.getOffset(), diagnostic.getMessage()); - } - - List offsets = Lists.newArrayListWithExpectedSize(errorMessages.size()); - offsets.addAll(errorMessages.keySet()); - Collections.sort(offsets); - - // Append all the error messages to the end of the source - int errorNumber = 1; - ListIterator offsetIterator = offsets.listIterator(); - StringBuilder sourceContentWithErrors = new StringBuilder(sourceContent); - while (offsetIterator.hasNext()) { - sourceContentWithErrors.append(SPLITTER); - sourceContentWithErrors.append(String.format(ERROR, errorNumber++)); - sourceContentWithErrors.append(errorMessages.get(offsetIterator.next())).append('\n'); - } - if (errorNumber > 1) { - sourceContentWithErrors.append(SPLITTER); - } - - // Insert all the error markers () to the source - // Done in inverse order to avoid the confusion with offset adjustments - while (offsetIterator.hasPrevious()) { - sourceContentWithErrors.insert(offsetIterator.previous(), String.format(ERROR_MARKER, --errorNumber)); - } - - assertEquals("Errors found: ", sourceContentAsString, sourceContentWithErrors.toString()); - } - - /** - * Gets the offset and the error message for a given {@link Diagnostic}. - * - * @param diagnostic - * instance of {@link Diagnostic} - * @return - * offset and error message - */ - private Pair processDiagnostic(final Diagnostic diagnostic) { - // CHECKSTYLE:OFF MagicNumber - StringBuilder errorMessage = new StringBuilder(50); - // CHECKSTYLE:ON - if (diagnostic instanceof AbstractValidationDiagnostic) { - AbstractValidationDiagnostic avd = (AbstractValidationDiagnostic) diagnostic; - errorMessage.append("Unexpected issue found. Code '"); - errorMessage.append(avd.getIssueCode()).append("'\n"); - errorMessage.append(avd.getMessage()); - if (avd instanceof FeatureBasedDiagnostic && ((FeatureBasedDiagnostic) avd).getFeature() != null) { - List nodes = NodeModelUtils.findNodesForFeature(avd.getSourceEObject(), ((FeatureBasedDiagnostic) avd).getFeature()); - if (nodes != null && !nodes.isEmpty()) { - return new Pair(findFirstNonHiddenLeafNode(nodes.get(0)).getTotalOffset(), errorMessage.toString()); - } - } else if (avd instanceof RangeBasedDiagnostic) { - return new Pair(((RangeBasedDiagnostic) avd).getOffset(), errorMessage.toString()); - } else { - return new Pair(NodeModelUtils.getNode(avd.getSourceEObject()).getTotalOffset(), errorMessage.toString()); - } - } - return null; - } - - /** - * Given an AST node, find the first non-hidden leaf node among child nodes using deep search. - * For the sake of compatibility the method can handle LeafNodes and CompositeNodes. - * In case of a LeafNode the result is the input node itself. - * - * @param node - * entry point - * @return - * first node for which isHidden() is false or the original node - */ - public INode findFirstNonHiddenLeafNode(final INode node) { - if (node instanceof ICompositeNode) { - for (ILeafNode leaf : node.getLeafNodes()) { - if (!leaf.isHidden()) { - return leaf; - } - } - } - return node; - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/contentassist/AbstractAcfContentAssistTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/contentassist/AbstractAcfContentAssistTest.java deleted file mode 100644 index af4c27998..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/contentassist/AbstractAcfContentAssistTest.java +++ /dev/null @@ -1,357 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.contentassist; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.fail; - -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.eclipse.emf.common.util.WrappedException; -import org.eclipse.jface.text.contentassist.ICompletionProposal; - -import com.avaloq.tools.ddk.xtext.test.AbstractXtextMarkerBasedTest; -import com.avaloq.tools.ddk.xtext.test.AcfContentAssistProcessorTestBuilder; -import com.avaloq.tools.ddk.xtext.test.TagCompilationParticipant; -import com.google.common.collect.Maps; -import com.google.inject.Injector; - - -/** - * The Class AcfContentAssistTest provides utility operations for non-UI content assist - * tests. It allows testing content assist on non-complete models step by step, as a user - * would also use it when defining a new model in the corresponding editor. - */ -// CHECKSTYLE:OFF -@SuppressWarnings("nls") -public abstract class AbstractAcfContentAssistTest extends AbstractXtextMarkerBasedTest { - private final AcfContentAssistMarkerTagsInfo acfContentAssistMarkerTagInfo = new AcfContentAssistMarkerTagsInfo(); - private static final String AT_LEAST_ONE_PROPOSAL_WAS_PROVIDED = "At least one proposal was provided"; - private static final String EXPECTED_PROPOSALS_NOT_SET = "No expected proposals provided to check against actual proposals."; - // CHECKSTYLE:ON - private static final String SEPARATOR = ", "; - - /** - * Creates a new completion proposal builder. - * - * @return the content assist processor test builder - */ - protected AcfContentAssistProcessorTestBuilder newBuilder() { - try { - return new AcfContentAssistProcessorTestBuilder(getXtextTestUtil().get(Injector.class), getTestUtil()); - // CHECKSTYLE:OFF - } catch (Exception e) { - // CHECKSTYLE:ON - throw new WrappedException("Could not create ContentAssistProcessorTestBuilder.", e); - } - } - - /** - * Gets the completion proposal display strings. - * - * @param computedProposals - * the computed proposals - * @return the completion proposal display strings - */ - private String getCompletionProposalDisplayStrings(final ICompletionProposal... computedProposals) { - String result = ""; - for (ICompletionProposal p : computedProposals) { - result += p.getDisplayString() + SEPARATOR; // NOPMD - } - if (result != null && result.length() > SEPARATOR.length()) { - return result.substring(0, result.length() - SEPARATOR.length()); - } - return null; - } - - /** - * Iterate over given computed completion proposals and compare resulting display string with each expected proposal text. Fail if one or more expected - * proposals not found. - * - * @param computedProposals - * the computed proposals - * @param positiveTest - * indicates the type of test. - * TRUE: the test is positive and must fail if proposals not found. - * FALSE: the test is negative and must fail if proposals found. - * @param proposals - * the expected proposals as display strings - */ - private void assertCompletionProposal(final ICompletionProposal[] computedProposals, final boolean positiveTest, final String... proposals) { - assertNotEquals(AT_LEAST_ONE_PROPOSAL_WAS_PROVIDED, proposals.length, 0); - for (final String s : proposals) { - boolean foundProposal = false; - for (ICompletionProposal p : computedProposals) { - if (s.equals(p.getDisplayString())) { - foundProposal = true; - break; - } - } - if (positiveTest && !foundProposal) { - fail(MessageFormat.format("Expected to find proposal \"{0}\" but could only find \"{1}\"", s, getCompletionProposalDisplayStrings(computedProposals))); - } else if (!positiveTest && foundProposal) { - fail(MessageFormat.format("Not expected to find proposal \"{0}\" but found \"{1}\"", s, getCompletionProposalDisplayStrings(computedProposals))); - } - } - } - - /** - * Iterate over given computed completion proposals and compare resulting display string with each expected proposal text. Fail if one or more expected - * proposals not found. - * - * @param computedProposals - * the computed proposals - * @param expectedProposals - * the expected proposals as display strings - */ - protected void assertCompletionProposal(final ICompletionProposal[] computedProposals, final String... expectedProposals) { - assertCompletionProposal(computedProposals, true, expectedProposals); - } - - /** - * Iterate over given computed completion proposals and compare resulting display string with each expected proposal text. Fail if one or more expected - * proposals not found. - * - * @param cursorPosition - * the cursor position within the main test source - * @param expectedProposals - * the expected proposals as display strings - */ - protected void assertCompletionProposal(final int cursorPosition, final String... expectedProposals) { - assertCompletionProposal(newBuilder().computeCompletionProposals(getTestSource(), cursorPosition), expectedProposals); - } - - /** - * Iterate over given computed completion proposals and compare resulting display string with each expected proposal text. Fail if one or more not expected - * proposals found. - * - * @param computedProposals - * the computed proposals - * @param expectedProposals - * the not expected proposals as display strings - */ - protected void assertNotCompletionProposal(final ICompletionProposal[] computedProposals, final String... expectedProposals) { - assertCompletionProposal(computedProposals, false, expectedProposals); - } - - /** - * Ensures that the list of expected proposals corresponds exactly the list of computed proposals. - * - * @param computedProposals - * the computed proposals - * @param expectedProposals - * the expected proposals as display strings - */ - protected void assertExactlyCompletionProposal(final ICompletionProposal[] computedProposals, final String... expectedProposals) { - assertNotEquals(AT_LEAST_ONE_PROPOSAL_WAS_PROVIDED, expectedProposals.length, 0); - - Set computedProposalsAsSet = new HashSet(); - for (ICompletionProposal p : computedProposals) { - computedProposalsAsSet.add(p.getDisplayString()); - } - - Set expectedProposalsAsSet = new HashSet(); - expectedProposalsAsSet.addAll(Arrays.asList(expectedProposals)); - - if (computedProposalsAsSet.size() != expectedProposalsAsSet.size()) { - // Calculate missing templates - Set missing = new HashSet(expectedProposalsAsSet); - missing.removeAll(computedProposalsAsSet); - if (!missing.isEmpty()) { - fail(MessageFormat.format("Proposals not found: \"{0}\".", missing.toString())); - } - // Calculate false positives - Set notExpected = new HashSet(computedProposalsAsSet); - notExpected.removeAll(expectedProposalsAsSet); - if (!notExpected.isEmpty()) { - fail(MessageFormat.format("Not expected: \"{0}\".", notExpected.toString())); - } - } - } - - /** - * Asserts the expected valid and invalid proposals based on the given registered source filename. - * - * @param sourceFileName - * the filename of the test source that the proposals were to be computed from, must not be {@code null} - */ - private void assertSourceProposals(final String sourceFileName) { - try { - AcfContentAssistProcessorTestBuilder builder = newBuilder().append(getTestSource(sourceFileName).getContent()); - assertFalse(EXPECTED_PROPOSALS_NOT_SET, (acfContentAssistMarkerTagInfo.expectedProposalMap.isEmpty() - && acfContentAssistMarkerTagInfo.notExpectedProposalMap.isEmpty() && acfContentAssistMarkerTagInfo.expectedExactlyProposalMap.isEmpty())); - for (int markerId : getUsedTagsItems()) { - final ICompletionProposal[] proposals = builder.computeCompletionProposals(getOffsetForTag(markerId)); - if (acfContentAssistMarkerTagInfo.expectedProposalMap.containsKey(markerId)) { - assertCompletionProposal(proposals, acfContentAssistMarkerTagInfo.expectedProposalMap.get(markerId)); - } - if (acfContentAssistMarkerTagInfo.notExpectedProposalMap.containsKey(markerId)) { - assertNotCompletionProposal(proposals, acfContentAssistMarkerTagInfo.notExpectedProposalMap.get(markerId)); - } - if (acfContentAssistMarkerTagInfo.expectedExactlyProposalMap.containsKey(markerId)) { - assertExactlyCompletionProposal(proposals, acfContentAssistMarkerTagInfo.expectedExactlyProposalMap.get(markerId)); - } - } - // CHECKSTYLE:OFF - } catch (Exception e) { - // CHECKSTYLE:ON - throw new WrappedException("Could not assert the expected valid and invalid proposals.", e); - } - } - - /** - * Asserts the proposals for the given kernel test filename and content. - * - * @param sourceFileName - * the filename of the test source that the proposals were to be computed from, must not be {@code null} - * @param sourceContent - * the content of the test source, must not be {@code null} - */ - protected void assertKernelSourceProposals(final String sourceFileName, final CharSequence sourceContent) { - addKernelSourceToWorkspace(sourceFileName, sourceContent); - assertSourceProposals(sourceFileName); - } - - /** - * Asserts the proposals for the given customer test filename and content. - * - * @param sourceFileName - * the filename of the test source that the proposals were to be computed from, must not be {@code null} - * @param sourceContent - * the content of the test source, must not be {@code null} - */ - protected void assertCustomerSourceProposals(final String sourceFileName, final CharSequence sourceContent) { - addCustomerSourceToWorkspace(sourceFileName, sourceContent); - assertSourceProposals(sourceFileName); - } - - /** - * Registers the marker with the given proposals. Each of the expected proposal must have a match with the computed proposals on the marked position. - * - * @param proposals - * the expected proposals, must not be {@code null} - * @return mark text to be inserted in the source file, never {@code null} - */ - protected String expected(final String... proposals) { - Integer markerId = getTag(); - acfContentAssistMarkerTagInfo.expectedProposalMap.put(markerId, proposals); - return mark(markerId); - } - - /** - * Registers the marker with the given proposals. The expected proposals must EXACTLY match the computed proposals on the marked position. - * - * @param proposals - * the exact expected proposals, must not be {@code null} - * @return mark text to be inserted in the source file, never {@code null} - */ - protected String expectedExactly(final String... proposals) { - Integer markerId = getTag(); - acfContentAssistMarkerTagInfo.expectedExactlyProposalMap.put(markerId, proposals); - return mark(markerId); - } - - /** - * Registers the marker with the given proposals. Each of the expected proposal must NOT match with any of the computed proposals on the marked position. - * - * @param proposals - * the expected invalid proposals, must not be {@code null} - * @return mark text to be inserted in the source file, never {@code null} - */ - protected String notExpected(final String... proposals) { - Integer markerId = getTag(); - acfContentAssistMarkerTagInfo.notExpectedProposalMap.put(markerId, proposals); - return mark(markerId); - } - - /** - * Stores the valid proposals in a corresponding map using the given marker id. - * This method will return the marker id that will be used to {@link #mark()} the position in - * which the valid proposals are expected. - * Use {@link #getTag()} for the marker id when accessing locally. For global declarations, use @Tag annotation for marker id. - * - * @param markerId - * the unique marker id, must not be {@code null}} - * @param proposals - * the expected valid proposals, must not be {@code null} - * @return the marker id, never {@code null} - */ - protected int expected(final int markerId, final String... proposals) { - acfContentAssistMarkerTagInfo.expectedProposalMap.put(markerId, proposals); - return markerId; - } - - /** - * Stores the invalid proposals in a corresponding map using the given marker id. - * This method will return the marker id that will be used to {@link #mark()} the position in which the proposals are not expected. - * Use {@link #getTag()} for the marker id when accessing locally. For global declarations, use @Tag annotation for marker id. - * - * @param markerId - * the unique marker id, must not be {@code null}} - * @param proposals - * the expected invalid proposals, must not be {@code null} - * @return the marker id, never {@code null} - */ - protected int notExpected(final int markerId, final String... proposals) { - acfContentAssistMarkerTagInfo.notExpectedProposalMap.put(markerId, proposals); - return markerId; - } - - /** - * Stores the exact valid proposals in a corresponding map using the given marker id. - * This method will return the marker id that will be used to {@link #mark()} the position in which the exact proposals are expected. - * Use {@link #getTag()} for the marker id when accessing locally. For global declarations, use @Tag annotation for marker id. - * - * @param markerId - * the unique marker id, must not be {@code null}} - * @param proposals - * the expected exact proposals, must not be {@code null} - * @return the marker id, never {@code null} - */ - protected int expectedExactly(final int markerId, final String... proposals) { - acfContentAssistMarkerTagInfo.expectedExactlyProposalMap.put(markerId, proposals); - return markerId; - } - - /** - * This class preserves information about tags in the sources for all tests within - * a content assist test class. - */ - private final class AcfContentAssistMarkerTagsInfo extends MarkerTagsInfo { - private final Map expectedProposalMap = Maps.newHashMap(); - private final Map notExpectedProposalMap = Maps.newHashMap(); - private final Map expectedExactlyProposalMap = Maps.newHashMap(); - - @Override - public void clearTags(final long maxId) { - super.clearTags(maxId); - expectedProposalMap.entrySet().removeIf(entry -> entry.getKey() < TagCompilationParticipant.COUNTER_BASE); - notExpectedProposalMap.entrySet().removeIf(entry -> entry.getKey() < TagCompilationParticipant.COUNTER_BASE); - expectedExactlyProposalMap.entrySet().removeIf(entry -> entry.getKey() < TagCompilationParticipant.COUNTER_BASE); - } - } - - @Override - protected AcfContentAssistMarkerTagsInfo getMarkerTagsInfo() { - AcfContentAssistMarkerTagsInfo info = (AcfContentAssistMarkerTagsInfo) getTestInformation().getTestObject(AcfContentAssistMarkerTagsInfo.class); - if (info == null) { - info = acfContentAssistMarkerTagInfo; - getTestInformation().putTestObject(AcfContentAssistMarkerTagsInfo.class, acfContentAssistMarkerTagInfo); - } - return info; - } - -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/conversion/AbstractValueConverterServiceTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/conversion/AbstractValueConverterServiceTest.java deleted file mode 100644 index a29ad22df..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/conversion/AbstractValueConverterServiceTest.java +++ /dev/null @@ -1,281 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.conversion; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.eclipse.xtext.AbstractRule; -import org.eclipse.xtext.IGrammarAccess; -import org.eclipse.xtext.conversion.IValueConverterService; -import org.eclipse.xtext.conversion.ValueConverterException; - -import com.avaloq.tools.ddk.xtext.grammar.KeywordCollector; -import com.avaloq.tools.ddk.xtext.test.AbstractXtextTest; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - - -/** - * Base class for testing a language {@link IValueConverterService} implementation. - */ -@SuppressWarnings("nls") -public abstract class AbstractValueConverterServiceTest extends AbstractXtextTest { - /** ID regular expression does not allow digits as first character. */ - public static final String INVALID_ID_NUMBER = "1something"; - /** ID regular expression does not allow #. */ - public static final String INVALID_ID_NONALPHANUM = "#"; - /** ID regular expression does not allow any '.'. */ - public static final String INVALID_ID_WITHDOT = "someValidIdentifier.something"; - protected static final Set INVALID_IDENTIFIERS = ImmutableSet.of( -// @Format-Off - INVALID_ID_NUMBER, - INVALID_ID_NONALPHANUM, - INVALID_ID_WITHDOT -// @Format-On - ); - - private final Map keywordCollectors = Maps.newHashMap(); - - /** - * Returns the {@link IGrammarAccess}. - * - * @return the {@link IGrammarAccess}, never {@code null} - */ - protected IGrammarAccess getGrammarAccess() { - final IGrammarAccess grammarAccess = getXtextTestUtil().get(IGrammarAccess.class); - assertNotNull("The IGrammarAccess must be registered in order to test the IValueConverterService.", grammarAccess); - return grammarAccess; - } - - /** - * Returns whether keyword case is ignored. - *

- * Note: The default is {@code true}, i.e. keyword case is ignored. - *

- * - * @return whether keyword case is ignored - */ - protected boolean isIgnoreCase() { - return true; - } - - /** - * Gets or creates the {@link KeywordCollector} instance for the given {@link AbstractRule}. - * - * @param rule - * the {@link AbstractRule}, must not be {@code null} - * @return the {@link KeywordCollector} instance for the given {@link AbstractRule}, never {@code null} - */ - protected KeywordCollector getKeywordCollector(final AbstractRule rule) { - KeywordCollector keywordCollector = keywordCollectors.get(rule); - if (keywordCollector == null) { - keywordCollector = new KeywordCollector(rule, isIgnoreCase()); - keywordCollectors.put(rule, keywordCollector); - } - return keywordCollector; - } - - /** - * Returns the {@link IValueConverterService}. - * - * @return the {@link IValueConverterService}, never {@code null} - */ - protected IValueConverterService getValueConverterService() { - final IValueConverterService valueConverterService = getXtextTestUtil().get(IValueConverterService.class); - assertNotNull("The IValueConverterService must be registered in order to test it.", valueConverterService); - return valueConverterService; - } - - /** - * Asserts that the value-to-string conversion resulted in a string that is equal to the specified input value. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - * @param value - * the value to be converted, must not be {@code null} - */ - protected void assertToStringConvertedEquals(final AbstractRule rule, final String value) { - final String actual = getValueConverterService().toString(value, rule.getName()); - assertEquals("Converted value must be equal to input value.", value, actual); - } - - /** - * Asserts that the value-to-string conversion resulted in a string that is equal to the specified input value surrounded by double-quotes. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - * @param value - * the value to be converted, must not be {@code null} - */ - protected void assertToStringConvertedQuoted(final AbstractRule rule, final String value) { - final String actual = getValueConverterService().toString(value, rule.getName()); - assertEquals("Quotes expected", '"' + value + '"', actual); - } - - /** - * Asserts that the value-to-string conversion for all the specified values results in strings that are equal to the specified input values surrounded by - * double-quotes. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - * @param values - * values to be converted, must not be {@code null} - */ - protected void assertToStringConvertedQuoted(final AbstractRule rule, final Iterable values) { - List failures = Lists.newArrayList(); - for (String value : values) { - String actual = getValueConverterService().toString(value, rule.getName()); - if (!actual.equals('"' + value + '"')) { - failures.add(value); - } - } - if (!failures.isEmpty()) { - fail("All specified values must be quoted. The following invalid identifiers were not enquoted: " + Joiner.on(", ").join(failures)); - } - } - - /** - * Asserts that the value-to-string conversion for the keywords listed by the specified rule (or one of rules it calls) leaves them unchanged. - * - * @param rule - * the rule used for the value-to-string conversion and whose keywords are tested, must not be {@code null} - */ - protected void assertToStringLeavesRuleKeywordsUnchanged(final AbstractRule rule) { - assertToStringLeavesRuleKeywordsUnchanged(rule, null); - } - - /** - * Asserts that the value-to-string conversion for the keywords listed by the specified rule (or one of rules it calls) leaves them unchanged. - * - * @param rule - * the rule used for the value-to-string conversion and whose keywords are tested, must not be {@code null} - * @param exceptions - * the keywords used in the rule that are not valid by themselves (e.g. '.' or other special characters that cannot stand alone), may be {@code null} - */ - protected void assertToStringLeavesRuleKeywordsUnchanged(final AbstractRule rule, final Collection exceptions) { - final Collection keywords = getKeywordCollector(rule).getKeywords(); - if (exceptions != null) { - keywords.removeAll(exceptions); - } - for (final String keyword : keywords) { - assertToStringConvertedEquals(rule, keyword); - } - } - - /** - * Asserts that the value-to-string conversion for values that do not match the ID terminal rule regular expression results in the values surrounded by - * double-quotes. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - */ - protected void assertToStringNonIDQuoted(final AbstractRule rule) { - assertToStringConvertedQuoted(rule, INVALID_IDENTIFIERS); - } - - /** - * Asserts that the value-to-string conversion for all of the specified values results in a {@link ValueConverterException} thrown. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - * @param valueList - * the list of values to be converted, must not be {@code null} - */ - protected void assertToStringFails(final AbstractRule rule, final Iterable valueList) { - List failures = Lists.newArrayList(valueList); - for (String value : valueList) { - try { - getValueConverterService().toString(value, rule.getName()); - } catch (ValueConverterException e) { - failures.remove(value); - } - } - if (!failures.isEmpty()) { - fail("The following invalid identifiers did not cause a ValueConverterException: " + Joiner.on(", ").join(failures)); - } - } - - /** - * Asserts that the value-to-string conversion for values that do not match the ID terminal rule regular expression results in a - * {@link ValueConverterException} thrown. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - */ - protected void assertToStringNonIDFails(final AbstractRule rule) { - assertToStringFails(rule, INVALID_IDENTIFIERS); - } - - /** - * Asserts that the value-to-string conversion for values that are part of the rule language but are not listed by the rule results in a - * {@link ValueConverterException} thrown. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - */ - protected void assertToStringOtherKeywordsFail(final AbstractRule rule) { - assertToStringFails(rule, getKeywordCollector(rule).getOtherKeywords()); - } - - /** - * Asserts that the value-to-string conversion for values that are part of the rule language but are not listed by the rule results in quoted strings. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - */ - protected void assertToStringOtherKeywordsQuoted(final AbstractRule rule) { - assertToStringConvertedQuoted(rule, getKeywordCollector(rule).getOtherKeywords()); - } - - /** - * Asserts that the value-to-string conversion for values that keywords listed by the rule leaves them unchanged while other language keywords or invalid IDs - * result in a {@link ValueConverterException} thrown. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - */ - protected void assertToStringOnlyListedKeywordsAllowed(final AbstractRule rule) { - assertToStringLeavesRuleKeywordsUnchanged(rule); - assertToStringOtherKeywordsFail(rule); - assertToStringNonIDFails(rule); - } - - /** - * Asserts that boolean values convert to "true" for {@link Boolean#TRUE} and "false" for {@link Boolean#FALSE}. - * - * @param rule - * the rule used for the value-to-string conversion, must not be {@code null} - */ - protected void assertBooleanValueToString(final AbstractRule rule) { - final String actualTrue = getValueConverterService().toString(Boolean.TRUE, rule.getName()); - assertEquals("Converted value must be \"true\".", Boolean.TRUE.toString(), actualTrue); - final String actualFalse = getValueConverterService().toString(Boolean.FALSE, rule.getName()); - assertEquals("Converted value must be \"false\".", Boolean.FALSE.toString(), actualFalse); - // Check that the converter is for the correct type - try { - getValueConverterService().toString("false", rule.getName()); - throw new AssertionError("Converter must not accept String."); - } catch (final ClassCastException expected) { - // expected; do nothing - } catch (final ValueConverterException expected) { - // expected; do nothing - } - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/formatting/AbstractFormattingTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/formatting/AbstractFormattingTest.java deleted file mode 100644 index 14bc96dd2..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/formatting/AbstractFormattingTest.java +++ /dev/null @@ -1,173 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.formatting; - -import java.util.List; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.formatting.INodeModelFormatter; -import org.eclipse.xtext.formatting.INodeModelFormatter.IFormattedRegion; -import org.eclipse.xtext.nodemodel.ICompositeNode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; -import org.eclipse.xtext.resource.SaveOptions; -import org.junit.Assert; -import org.junit.Test; - -import com.avaloq.tools.ddk.xtext.test.AbstractXtextTest; - - -/** - * Base class for formatting tests. - */ -@SuppressWarnings("nls") -public abstract class AbstractFormattingTest extends AbstractXtextTest { - - private static final String CR_LF = "\r\n"; - private static final String LF = "\n"; - - @Override - protected List getRequiredSourceFileNames() { - List result = super.getRequiredSourceFileNames(); - result.add(getExpectedTestSourceFileName()); - return result; - } - - @Override - protected String getTestSourceModelName() { - return super.getTestSourceModelName() + "Input"; - } - - /** - * The default implementation returns the default source model name for the test class and adds 'Expected'. - * this test. A test class needs to override this, if the name of the expected formatting test source model differs from the default. - * - * @return the name of the expected formatting test source model - */ - private String getExpectedTestSourceModelName() { - return super.getTestSourceModelName() + "Expected"; - } - - /** - * The default implementation returns the default source model name for the test class and adds 'Expected' and the default file extension for the grammar of - * this test. A test class needs to override this, if the name of the expected formatting test source file differs from the default. - * - * @return the name of the expected formatting test source file - */ - protected String getExpectedTestSourceFileName() { - return getExpectedTestSourceModelName() + "." + getXtextTestUtil().getFileExtension(); - } - - /** - * Test formatting based on the NodeModel. - */ - @Test - public void formattedNodeModel() { - assertFormattedNodeModel(); - } - - /** - * Test formatting based on the ParseTreeConstructor. - */ - public void formattedParseTreeConstructor() { - assertFormattedParseTreeConstructor(); - } - - /** - * Test preservation of formatting using ParseTreeConstructor. - */ - public void preservedParseTreeConstructor() { - assertPreservedParseTreeConstructor(); - } - - /** - * Test preservation of formatting using NodeModelFormatter. - */ - @Test - public void preservedNodeModel() { - assertPreservedNodeModel(); - } - - /** - * Test formatting based on the ParseTreeConstructor. - */ - protected final void assertFormattedParseTreeConstructor() { - assertFormattedParseTreeConstructor(getSemanticModel(), getTestSource(getExpectedTestSourceFileName()).getContent()); - } - - /** - * Test formatting based on the NodeModel. - * - * @param offset - * Offset from which to start formatting - * @param length - * Length of region to format - */ - private void assertFormattedNodeModel(final int offset, final int length) { - assertFormattedNodeModel(getSemanticModel(), getTestSource().getContent(), getTestSource(getExpectedTestSourceFileName()).getContent(), offset, length); - } - - /** - * Test formatting based on the NodeModel. - */ - private void assertFormattedNodeModel() { - assertFormattedNodeModel(0, getTestSource().getContent().length()); - } - - /** - * Test preservation of formatting. - */ - private void assertPreservedNodeModel() { - String expectedContent = getTestSource(getExpectedTestSourceFileName()).getContent(); - assertFormattedNodeModel(getTestSource(getExpectedTestSourceFileName()).getModel(), expectedContent, expectedContent, 0, expectedContent.length()); - } - - /** - * Test preservation of formatting. - */ - protected final void assertPreservedParseTreeConstructor() { - assertFormattedParseTreeConstructor(getTestSource(getExpectedTestSourceFileName()).getModel(), getTestSource(getExpectedTestSourceFileName()).getContent()); - } - - /** - * Test formatting based on the ParseTreeConstructor. - * - * @param model - * the model to be serialized and compared with expected string - * @param expected - * Expected formatted String - */ - private void assertFormattedParseTreeConstructor(final EObject model, final String expected) { - String actual = getXtextTestUtil().getSerializer().serialize(model, SaveOptions.newBuilder().format().getOptions()); - Assert.assertEquals("Formatted ParseTree", expected.replaceAll(CR_LF, LF), actual.replaceAll(CR_LF, LF)); - } - - /** - * Test formatting based on the NodeModel. - * - * @param model - * the model to check - * @param input - * String representing a serialized model - * @param expected - * Expected formatted String - * @param offset - * Offset from which to start formatting - * @param length - * Length of region to format - */ - private void assertFormattedNodeModel(final EObject model, final String input, final String expected, final int offset, final int length) { - ICompositeNode node = NodeModelUtils.getNode(model).getRootNode(); - IFormattedRegion region = getXtextTestUtil().get(INodeModelFormatter.class).format(node, offset, length); - String actual = input.substring(0, offset) + region.getFormattedText() + input.substring(length + offset); - Assert.assertEquals("Formatted NodeModel", expected.replaceAll(CR_LF, LF), actual.replaceAll(CR_LF, LF)); - } - -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/generator/AbstractGeneratorTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/generator/AbstractGeneratorTest.java deleted file mode 100644 index 597ffcea2..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/generator/AbstractGeneratorTest.java +++ /dev/null @@ -1,423 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.generator; - -import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Sets.newHashSet; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; -import java.net.URL; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.emf.common.util.URI; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.pde.core.project.IBundleProjectDescription; -import org.eclipse.ui.actions.WorkspaceModifyOperation; -import org.eclipse.xtext.resource.FileExtensionProvider; -import org.eclipse.xtext.ui.XtextProjectHelper; -import org.eclipse.xtext.ui.testing.util.IResourcesSetupUtil; -import org.eclipse.xtext.ui.testing.util.JavaProjectSetupUtil; -import org.eclipse.xtext.ui.util.PluginProjectFactory; -import org.junit.AfterClass; -import org.junit.Assert; - -import com.google.common.base.Functions; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; -import com.google.common.io.CharStreams; -import com.google.common.io.Resources; -import com.google.inject.Inject; -import com.google.inject.Provider; - - -/** - * A base class for xtext generator tests. Allows creating a project and adding files. - */ -@SuppressWarnings({"PMD.AbstractClassWithoutAbstractMethod", "nls"}) -public abstract class AbstractGeneratorTest { - private static final String MESSAGE_GENERATED_FILE_MUST_EXIST = "Generated file ''{0}'' must exist."; - private static final String MESSAGE_GENERATED_CODE_MUST_BE_CORRECT = "Generated contents of ''{0}'' must be correct."; - - private static final String FORWARD_SLASH = "/"; //$NON-NLS-1$ - - private static final Logger LOGGER = LogManager.getLogger(AbstractGeneratorTest.class); - - private static Map projects = new HashMap(); - - private final Set files = newHashSet(); - protected static final List REQUIRED_BUNDLES = newArrayList(// - "org.eclipse.xtext.xbase.lib", //$NON-NLS-1$ - "org.eclipse.xtend.lib", // //$NON-NLS-1$ - "org.eclipse.emf.ecore", //$NON-NLS-1$ - "org.eclipse.xtext", // //$NON-NLS-1$ - "org.eclipse.osgi", //$NON-NLS-1$ - "org.eclipse.xtend", //$NON-NLS-1$ - "org.eclipse.core.runtime", //$NON-NLS-1$ - "org.eclipse.xtext.xbase" //$NON-NLS-1$ - ); - - @Inject - private FileExtensionProvider fileExtensionProvider; - - @Inject - private Provider projectFactoryProvider; - - /** - * Clean up after all tests have terminated. - */ - @AfterClass - public static void cleanUp() { - try { - IResourcesSetupUtil.cleanWorkspace(); - } catch (CoreException e) { - LOGGER.error(e.getMessage(), e); - } - } - - /** - * Initializes a project with the given name and sources. - * - * @param projectName - * the name of the project - * @param sourceFileNames - * the source file names, mapping input filename to output filename - * @throws CoreException - * the {@link CoreException} - */ - public void initializeProject(final String projectName, final Map sourceFileNames) throws CoreException { - initializeProject(projectName, null, REQUIRED_BUNDLES, null, null, sourceFileNames); - } - - /** - * Initializes a project with the given name, required bundles and sources. - * - * @param projectName - * the name of the project - * @param folders - * the folders to create (source and source-generated folders will be created unless methods are overridden and specified as null or empty), or - * {@code null} if none - * @param requiredBundles - * required bundles of the project to be created, or {@code null} if none - * @param importedPackages - * imported packages of the project to be created, or {@code null} if none - * @param exportedPackages - * exported packages of the project to be created, or {@code null} if none - * @param sourceFileNames - * the source file names, mapping input filename to output filename - * @throws CoreException - * the {@link CoreException} - */ - public void initializeProject(final String projectName, final List folders, final List requiredBundles, final List importedPackages, final List exportedPackages, final Map sourceFileNames) throws CoreException { - // a project must be created - createPluginProject(projectName, folders, requiredBundles, importedPackages, exportedPackages); - // sources are copied into the project and then built by the Xtext builder - addSourcesToWorkspace(projectName, sourceFileNames); - - // wait for build to finish, otherwise included catalog may not be resolvable - IResourcesSetupUtil.waitForBuild(); - } - - /** - * Gets the full file name. - * - * @param projectName - * the name of the project - * @param fileName - * the file name - * @return the full file name - */ - protected String getFullFileName(final String projectName, final String fileName) { - if (fileName.startsWith(FORWARD_SLASH)) { - // it is assumed that a full path is provided - return fileName; - } - if (fileName.contains(FORWARD_SLASH)) { - // it is assumed that a fully qualified file name in the boundaries of the current project is provided, only add the project name suffix - return '/' + projectName + '/' + fileName; - } - String extension = (fileName.indexOf('.') != -1) ? "" : '.' + fileExtensionProvider.getPrimaryFileExtension(); //$NON-NLS-1$ - return getSourceFolderPath(projectName) + fileName + extension; - } - - /** - * Gets the file with given file name. - * - * @param fileName - * the file name - * @return the file - */ - protected IFile getFile(final String fileName) { - final String fileNameWithExtension = fileName.indexOf('.') > 0 ? fileName : fileName + '.' + fileExtensionProvider.getPrimaryFileExtension(); - return Iterables.find(files, new Predicate() { - @Override - public boolean apply(final IFile input) { - return fileNameWithExtension.equals(input.getName()); - } - }); - } - - /** - * Gets the project with the given name or creates and returns it if it is non-existent. - * - * @param projectName - * the name of the project - * @return the project - * @throws CoreException - * the core exception - */ - protected IProject getOrCreatePluginProject(final String projectName) throws CoreException { - if (!projects.containsKey(projectName) || projects.get(projectName) == null) { - return createPluginProject(projectName, null, REQUIRED_BUNDLES, null, null); - } - return projects.get(projectName); - } - - /** - * Creates and returns a project with the given name and the given required bundles. - * - * @param projectName - * the name of the project - * @param folders - * the folders to create (source and source-generated folders will be created unless methods are overridden and specified as null or empty) - * @param requiredBundles - * required bundles of the project to be created, or {@code null} if none - * @param importedPackages - * imported packages of the project to be created, or {@code null} if none - * @param exportedPackages - * exported packages of the project to be created, or {@code null} if none - * @return the project - * @throws CoreException - * the core exception - */ - protected IProject createPluginProject(final String projectName, final List folders, final List requiredBundles, final List importedPackages, final List exportedPackages) throws CoreException { // NOPMD - // NPathComplexity - final PluginProjectFactory projectFactory = projectFactoryProvider.get(); - projectFactory.setProjectName(projectName); - String sourceFolderName = getSourceFolderName(); - if (sourceFolderName != null && sourceFolderName.length() > 0) { - projectFactory.addFolders(Arrays.asList(sourceFolderName)); - } - String sourceFolderGeneratedName = getSourceFolderGeneratedName(); - if (sourceFolderGeneratedName != null && sourceFolderGeneratedName.length() > 0) { - projectFactory.addFolders(Arrays.asList(sourceFolderGeneratedName)); - } - if (folders != null) { - projectFactory.addFolders(folders); - } - projectFactory.addBuilderIds(JavaCore.BUILDER_ID, "org.eclipse.pde.ManifestBuilder", "org.eclipse.pde.SchemaBuilder", XtextProjectHelper.BUILDER_ID); //$NON-NLS-1$ //$NON-NLS-2$ - projectFactory.addProjectNatures(JavaCore.NATURE_ID, IBundleProjectDescription.PLUGIN_NATURE, XtextProjectHelper.NATURE_ID); - if (requiredBundles != null) { - projectFactory.addRequiredBundles(requiredBundles); - } - if (importedPackages != null) { - projectFactory.addImportedPackages(importedPackages); - } - if (exportedPackages != null) { - projectFactory.addExportedPackages(exportedPackages); - } - final IProject[] result = new IProject[1]; - WorkspaceModifyOperation operation = new WorkspaceModifyOperation() { - - @SuppressWarnings("deprecation") - @Override - protected void execute(final IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { - result[0] = projectFactory.createProject(monitor, null); - IJavaProject javaProject = JavaCore.create(result[0]); - JavaProjectSetupUtil.makeJava5Compliant(javaProject); - JavaProjectSetupUtil.addJreClasspathEntry(javaProject); - } - }; - try { - operation.run(new NullProgressMonitor()); - } catch (InvocationTargetException e) { - return null; - } catch (InterruptedException e) { - return null; - } - return projects.put(projectName, result[0]); - } - - /** - * Returns the name of the source folder. - * - * @return the name of the source folder - */ - protected String getSourceFolderName() { - return "src"; //$NON-NLS-1$ - } - - /** - * Returns the name of the source folder for generated sources. - * - * @return the name of the source folder for generated sources - */ - protected String getSourceFolderGeneratedName() { - return "src-gen"; //$NON-NLS-1$ - } - - /** - * Adds sources with given file names to the {@link #getSourceFolderName()} folder of current project. If resources - * to be added to workspace do not contain a file extension in their file name, it is nevertheless added on files - * created. - * - * @param projectName - * the name of the project - * @param sourceFileNames - * the source file names - */ - public void addSourcesToWorkspace(final String projectName, final List sourceFileNames) { - addSourcesToWorkspace(projectName, Maps.uniqueIndex(sourceFileNames, Functions. identity())); - } - - /** - * Adds sources with given file names to the {@link #getSourceFolderName()} folder of current project. If resources - * to be added to workspace do not contain a file extension in their file name, it is nevertheless added on files - * created. - * - * @param projectName - * the name of the project - * @param sourceFileNames - * the source file names, mapping input filename to output filename - */ - public void addSourcesToWorkspace(final String projectName, final Map sourceFileNames) { - try { - new WorkspaceModifyOperation() { - @Override - protected void execute(final IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { - for (String inputFileName : sourceFileNames.keySet()) { - // Calculate output filename - final String outputFileName = sourceFileNames.get(inputFileName); - try { - final String contents = getContents(inputFileName); - // Create URI including file extension - URI resourceURI = URI.createPlatformResourceURI(getFullFileName(projectName, outputFileName), true); - IResourcesSetupUtil.createFile(resourceURI.toPlatformString(true), contents); - } catch (IOException e) { - LOGGER.error("failed adding file to workspace: " + outputFileName, e); //$NON-NLS-1$ - Assert.fail("Error adding file " + outputFileName + " to workspace: " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ - } - } - } - }.run(new NullProgressMonitor()); - } catch (InvocationTargetException e) { - Assert.fail("Error adding files to workspace: " + e.getMessage()); //$NON-NLS-1$ - } catch (InterruptedException e) { - Assert.fail("Error adding files to workspace: " + e.getMessage()); //$NON-NLS-1$ - } - } - - /** - * Gets the full path to the {@link #getSourceFolderName()} folder. - * - * @param projectName - * the name of the project - * @return the source folder path - */ - protected String getSourceFolderPath(final String projectName) { - return '/' + projectName + '/' + getSourceFolderName() + '/'; - } - - /** - * Gets the {@link IFile} of the given project and path. - * - * @param projectName - * the name of the project - * @param filePath - * the file path - * @return the {@link IFile} of the given project and path - */ - protected IFile getFileFromProject(final String projectName, final String filePath) { - return ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).getFile(filePath); - } - - /** - * Gets the contents of a given file. Contents are returned as a single string. - * - * @param file - * the file - * @return the file contents - * @throws CoreException - * thrown if file could not be read - * @throws IOException - * thrown if file could not be read - */ - public String getContents(final IFile file) throws IOException, CoreException { - try (InputStreamReader reader = new InputStreamReader(file.getContents(), StandardCharsets.UTF_8)) { - return normalizeLineBreaks(CharStreams.toString(reader)); - } - } - - /** - * Gets the contents of a specified resource using the default charset. - * - * @param resourceName - * the name of the resource - * @return the normalized contents of the specified resource - * @throws IOException - * if model could not be read - */ - public String getContents(final String resourceName) throws IOException { - URL resource = Resources.getResource(this.getClass(), resourceName); - return normalizeLineBreaks(Resources.toString(resource, Charset.defaultCharset())); - } - - /** - * Asserts that generation was successful, i.e. the given file exists, and the content is expected. - * - * @param projectName - * the name of the project - * @param fileName - * the name of the generated file - * @param expectedGeneratedContent - * the expected content of the generated file - * @throws IOException - * the {@link IOException} - * @throws CoreException - * the {@link CoreException} - */ - public void assertFileGenerated(final String projectName, final String fileName, final String expectedGeneratedContent) throws IOException, CoreException { - IFile generatedFile = getFileFromProject(projectName, fileName); - Assert.assertTrue(MessageFormat.format(MESSAGE_GENERATED_FILE_MUST_EXIST, generatedFile.toString()), generatedFile.exists()); - - String actualGeneratedContent = getContents(generatedFile); - Assert.assertEquals(MessageFormat.format(MESSAGE_GENERATED_CODE_MUST_BE_CORRECT, generatedFile.toString()), expectedGeneratedContent, actualGeneratedContent); - } - - /** - * Replaces all \r\n with \n. - * - * @param text - * input text - * @return the normalized test - */ - public String normalizeLineBreaks(final String text) { - return text.replaceAll("\r\n", "\n"); - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/junit/runners/SwtBotRecordingXtextTestRunner.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/junit/runners/SwtBotRecordingXtextTestRunner.java deleted file mode 100644 index 8ce51a3bc..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/junit/runners/SwtBotRecordingXtextTestRunner.java +++ /dev/null @@ -1,128 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.junit.runners; - -import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences; -import org.junit.internal.runners.statements.InvokeMethod; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; - -import com.avaloq.tools.ddk.test.core.AbstractSystemTest; -import com.avaloq.tools.ddk.test.ui.junit.runners.TestRunRecording; -import com.avaloq.tools.ddk.test.ui.swtbot.CoreSwtbotTools; - - -/** - * A {@link org.junit.runner.Runner} to use with swt bot tests featuring screenshot recording. - */ -// suppress warning restriction of org.junit.internal.runners.statements.InvokeMethod -@SuppressWarnings("restriction") -public class SwtBotRecordingXtextTestRunner extends XtextClassRunner { - /** - * Used to mark a test class or method to define the recording interval in milliseconds to use for the {@link SwtBotRecordingXtextTestRunner}. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.METHOD, ElementType.TYPE}) - public static @interface RecordingInterval { - /** - * Returns the recording interval value in milliseconds. - * - * @return the recording interval value in milliseconds - */ - long value(); - } - - private final TestRunRecording testRunRecording; - - /** - * Creates a new instance of {@link SwtBotRecordingXtextTestRunner}. - * Only called reflectively. Do not use programmatically. - * - * @param testClass - * the test class to run - * @throws InitializationError - * if any initialization failed - */ - public SwtBotRecordingXtextTestRunner(final Class testClass) throws InitializationError { - super(testClass); - CoreSwtbotTools.initializePreferences(); - testRunRecording = new TestRunRecording(getTestClass().getJavaClass(), SWTBotPreferences.SCREENSHOTS_DIR); - // TODO: add custom made {@link org.junit.runners.model.Statement} to check for exceptions during the test - // (by default test failures are reported only after the teardown has taken place) - } - - @Override - public void run(final RunNotifier notifier) { - try { - notifier.removeListener(testRunRecording); // remove existing listeners that could be added by suite or class runners - notifier.addListener(testRunRecording); - super.run(notifier); - } finally { - notifier.removeListener(testRunRecording); - } - } - - @Override - protected void runChild(final FrameworkMethod method, final RunNotifier notifier) { - testRunRecording.setRecordingInterval(getRecordingInterval(method)); - super.runChild(method, notifier); - } - - @Override - protected Statement methodInvoker(final FrameworkMethod method, final Object test) { - return new InvokeMethod(method, test) { - @Override - // CHECKSTYLE:CHECK-OFF IllegalThrow // inherited JUnit throw style - public void evaluate() throws Throwable { - // CHECKSTYLE:CHECK-ON IllegalThrow - try { - super.evaluate(); - // CHECKSTYLE:CHECK-OFF IllegalCatch // catching in order to act upon but then throwing the exception again - } catch (final Throwable throwable) { - // CHECKSTYLE:CHECK-ON IllegalCatch - testRunRecording.methodInvokeFailure(throwable); - throw throwable; - } - } - }; - } - - /** - * Computes the capture interval to use for recording the given test method. - * Checks first the annotation {@link RecordingInterval} of the method, - * then the annotations of the test class. - * Falls back to the default setting if no annotations were found. - * - * @param method - * the method under test - * @return the framerate to use for recording the given test method - */ - private long getRecordingInterval(final FrameworkMethod method) { - RecordingInterval recordingInterval = method.getAnnotation(RecordingInterval.class); - if (recordingInterval != null) { - return recordingInterval.value(); - } - for (Annotation annotation : getTestClass().getAnnotations()) { - if (annotation.annotationType().equals(RecordingInterval.class)) { - return ((RecordingInterval) annotation).value(); - } - } - return TestRunRecording.DEFAULT_RECORDING_INTERVAL; - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/junit/runners/XtextClassRunner.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/junit/runners/XtextClassRunner.java deleted file mode 100644 index ea9c0da38..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/junit/runners/XtextClassRunner.java +++ /dev/null @@ -1,353 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.junit.runners; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.eclipse.xtext.testing.XtextRunner; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.internal.AssumptionViolatedException; -import org.junit.internal.runners.model.EachTestNotifier; -import org.junit.internal.runners.statements.RunAfters; -import org.junit.internal.runners.statements.RunBefores; -import org.junit.runner.Description; -import org.junit.runner.manipulation.Filter; -import org.junit.runner.manipulation.NoTestsRemainException; -import org.junit.runner.manipulation.Sorter; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.ParentRunner; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; - -import com.avaloq.tools.ddk.test.core.AfterAll; -import com.avaloq.tools.ddk.test.core.BeforeAll; -import com.avaloq.tools.ddk.test.core.BugTest; -import com.avaloq.tools.ddk.test.core.IntegrationTest; -import com.avaloq.tools.ddk.test.core.ModuleTest; -import com.avaloq.tools.ddk.test.core.MultipleTestProblems; -import com.avaloq.tools.ddk.test.core.PerformanceTest; -import com.avaloq.tools.ddk.test.core.Retry; -import com.avaloq.tools.ddk.test.core.SystemTest; -import com.avaloq.tools.ddk.test.core.UnitTest; -import com.avaloq.tools.ddk.test.core.junit.runners.SorterUtil; -import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - - -/** - * A JUnit runner extending the {@link XtextRunner} with support for @BeforeAll and @AfterAll annotated methods. These methods will be run once for a - * given test class before the first test method and after the last test method respectively. - *

- *

Test Methods

Considered are all those methods of the test class that are annotated with one (or more) of the following test annotations: - *
    - *
  • {@link Test} - *
  • {@link UnitTest} - *
  • {@link ModuleTest} - *
  • {@link IntegrationTest} - *
  • {@link SystemTest} - *
  • {@link PerformanceTest} - *
  • {@link BugTest} - *
- *

- *

- *

Execution Order

The order of execution of the test methods is random by default. This can be changed by setting a specific sorter using the property - * 'com.avaloq.test.sorter'. Available sorters: - *
    - *
  • alphanumeric - *
- *

- *

- *

Multiple Test Runs

The number of runs for each test can be set using the system property {@value #PROPERTY_TEST_RUNS}. By default a test is run - * exactly once, i.e. the number is set to 1. - *

- *

- * The runner can be configured to retry a failing test. The system property {@value #PROPERTY_TEST_RETRIES} allows to specify how many times at most a failing - * test shall be retried. By default a failing test is not retried, i.e. the number is set to 0. Note: Test retries are ignored if a value greater than - * 1 has been set for {@value #PROPERTY_TEST_RUNS}, in which case the test will be run exactly the specified number of times, regardless of failures. - *

- *

- * A test which succeeds at least once is regarded as successful. To make a test fail in the case where it failed at least once, set the system property - * {@value #PROPERTY_UNSTABLE_FAIL} to {@code true} (default: {@code false}). - *

- */ -@SuppressWarnings({"restriction", "deprecation"}) -public class XtextClassRunner extends XtextRunner { - /** The system property for the number of test runs. */ - public static final String PROPERTY_TEST_RUNS = "com.avaloq.test.runs"; //$NON-NLS-1$ - /** The system property for the number of times a failing test shall be retried. */ - public static final String PROPERTY_TEST_RETRIES = "com.avaloq.test.retries"; //$NON-NLS-1$ - /** The system property to specify whether unstable tests shall fail. */ - public static final String PROPERTY_UNSTABLE_FAIL = "com.avaloq.test.unstablefail"; //$NON-NLS-1$ - /** Class-wide logger. */ - private static final Logger LOGGER = LogManager.getLogger(XtextClassRunner.class); - private static final List> TEST_ANNOTATIONS = Lists.newArrayList(Test.class, UnitTest.class, ModuleTest.class, IntegrationTest.class, SystemTest.class, PerformanceTest.class, BugTest.class); - private List expectedMethods; - private int currentMethodIndex; - private final int testRuns; - private final int testRetries; - private final boolean unstableFail; - private Description description; - private boolean descriptionOutdated = true; - - /** - * Creates a new test class runner. - * - * @param klass - * target test class, must not be {@code null} - * @throws InitializationError - * if the runner could not be initialized - */ - public XtextClassRunner(final Class klass) throws InitializationError { - super(klass); - SorterUtil.getInstance().initializeSorter(this); - testRuns = Integer.parseInt(System.getProperty(PROPERTY_TEST_RUNS, "1")); //$NON-NLS-1$ - testRetries = Integer.parseInt(System.getProperty(PROPERTY_TEST_RETRIES, "0")); //$NON-NLS-1$ - unstableFail = Boolean.parseBoolean(System.getProperty(PROPERTY_UNSTABLE_FAIL, "false")); //$NON-NLS-1$ - } - - /** - * Initializes this runner by initializing {@link #expectedMethods} with the list of methods which are expected to be called. This is then also checked by - * {@link #methodBlock(FrameworkMethod)} and allows identifying the first and last methods correctly. - */ - private void ensureInitialized() { - if (expectedMethods == null) { - try { - final Method getChildrenMethod = ParentRunner.class.getDeclaredMethod("getFilteredChildren"); //$NON-NLS-1$ - getChildrenMethod.setAccessible(true); - @SuppressWarnings("unchecked") - final Collection testMethods = (Collection) getChildrenMethod.invoke(this); - expectedMethods = ImmutableList.copyOf(Iterables.filter(testMethods, new Predicate() { - @Override - public boolean apply(final FrameworkMethod input) { - return input.getAnnotation(Ignore.class) == null; - } - })); - currentMethodIndex = 0; - // CHECKSTYLE:OFF - } catch (Exception e) { - // CHECKSTYLE:ON - throw new IllegalStateException(e); - } - } - } - - @Override - public Description getDescription() { - if (descriptionOutdated) { - description = super.getDescription(); - descriptionOutdated = false; - } - return description; - } - - @Override - protected void validateInstanceMethods(final List errors) { - validatePublicVoidNoArgMethods(AfterAll.class, false, errors); - validatePublicVoidNoArgMethods(BeforeAll.class, false, errors); - - super.validateInstanceMethods(errors); - } - - @Override - public void sort(final Sorter sorter) { - super.sort(sorter); - descriptionOutdated = true; - } - - @Override - public void filter(final Filter filter) throws NoTestsRemainException { - super.filter(filter); - descriptionOutdated = true; - } - - @Override - protected void runChild(final FrameworkMethod method, final RunNotifier notifier) { - ensureInitialized(); - final boolean ignored = method.getAnnotation(Ignore.class) != null; - if (!ignored) { - Assert.assertEquals("Method " + method.getName() + " not equal", expectedMethods.get(currentMethodIndex++), method); //$NON-NLS-1$//$NON-NLS-2$ - } - if (ignored || testRuns == 1 && testRetries == 0 && method.getAnnotation(Retry.class) == null) { - super.runChild(method, notifier); - } else { - runRepeatedly(method, createNotifier(method, notifier)); - } - } - - /** - * Runs the test method repeatedly according to the number of runs or retries. - * - * @param method - * the {@link FrameworkMethod}, must not be {@code null} - * @param eachNotifier - * the {@link EachTestNotifier}, must not be {@code null} - */ - @SuppressWarnings("PMD.NPathComplexity") - private void runRepeatedly(final FrameworkMethod method, final EachTestNotifier eachNotifier) { - - final Retry retryAnnotation = method.getAnnotation(Retry.class); - final int retryAnnotationValue = retryAnnotation != null ? retryAnnotation.value() : 0; - final int thisTestRetries = Math.max(retryAnnotationValue, testRetries); - - eachNotifier.fireTestStarted(); - try { - final MultipleTestProblems problems = new MultipleTestProblems(); - final Collection failures = Lists.newArrayList(); - final Collection errors = Lists.newArrayList(); - int run = 0; - int succeeded = 0; - while (run < testRuns || (testRuns == 1 && succeeded == 0 && run < thisTestRetries + 1)) { - try { - run++; - methodBlock(method).evaluate(); - succeeded++; - } catch (AssumptionViolatedException e) { - throw e; - } catch (AssertionError exception) { - failures.add(exception); - problems.addProblem(exception); - // CHECKSTYLE:CHECK-OFF IllegalCatch // we want to catch all possible errors - } catch (Throwable throwable) { - // CHECKSTYLE:CHECK-ON IllegalCatch - errors.add(throwable); - problems.addProblem(throwable); - } - } - final String testCase = getTestClass().getJavaClass().getSimpleName() + '.' + method.getName(); - if (run > 1) { - logRepeatedTestResult(testCase, run, succeeded, failures.size(), errors.size()); - } - if (succeeded == 0) { - problems.assertEmpty(); - } - if (problems.hasProblems()) { - if (unstableFail) { - problems.assertEmpty(); - } else { - final StringWriter stringWriter = new StringWriter(); - problems.printStackTrace(new PrintWriter(stringWriter)); - LOGGER.info(stringWriter.toString()); - } - } - } catch (AssumptionViolatedException e) { // NOPMD ExceptionAsFlowControl - eachNotifier.addFailedAssumption(e); - // CHECKSTYLE:CHECK-OFF IllegalCatch // we want to catch all possible errors - } catch (Throwable e) { - // CHECKSTYLE:CHECK-ON IllegalCatch - eachNotifier.addFailure(e); - } finally { - eachNotifier.fireTestFinished(); - } - } - - /** - * Logs the repeated test result. - * - * @param testCase - * the test case, must not be {@code null} - * @param runs - * the number of runs - * @param succeeded - * the number of succeeded runs - * @param failures - * the number of failed runs - * @param errors - * the number of errored runs - */ - public void logRepeatedTestResult(final String testCase, final int runs, final int succeeded, final int failures, final int errors) { - final StringBuilder testResult = new StringBuilder(testCase).append(" Repeated Test Result: "); //$NON-NLS-1$ - if (succeeded == runs) { - testResult.append("SUCCESS"); //$NON-NLS-1$ - } else if (succeeded == 0) { - testResult.append("FAILURE"); //$NON-NLS-1$ - } else { - testResult.append("UNSTABLE"); //$NON-NLS-1$ - } - testResult.append(" (").append(succeeded).append(" of ").append(runs).append(" succeeded"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - if (failures > 0) { - testResult.append(", ").append(failures).append(" failed"); //$NON-NLS-1$ //$NON-NLS-2$ - } - if (errors > 0) { - testResult.append(", ").append(errors).append(" errored"); //$NON-NLS-1$ //$NON-NLS-2$ - } - testResult.append(')'); - LOGGER.info(testResult.toString()); - } - - /** - * Creates a notifier for each test. - * - * @param method - * the {@link FrameworkMethod}, must not be {@code null} - * @param notifier - * the {@link RunNotifier}, must not be {@code null} - * @return an instance of {@link EachTestNotifier}, never {@code null} - */ - private EachTestNotifier createNotifier(final FrameworkMethod method, final RunNotifier notifier) { - return new EachTestNotifier(notifier, describeChild(method)); - } - - /** - * Adds any @BeforeAll methods to be run before the normal @Before annotated methods for the first test method only. - *

- * {@inheritDoc} - */ - @Override - protected Statement withBefores(final FrameworkMethod method, final Object target, final Statement stmt) { - ensureInitialized(); - Statement statement = super.withBefores(method, target, stmt); // NOPMD.CloseResource - if (method.equals(expectedMethods.get(0))) { - // reverse BeforeAll method order to get a 'runs top to bottom' order - final List befores = Lists.reverse(getTestClass().getAnnotatedMethods(BeforeAll.class)); - statement = befores.isEmpty() ? statement : new RunBefores(statement, befores, target); - } - return statement; - } - - /** - * Adds any @AfterAll methods to be run after the normal @After annotated methods for the last test method only. - *

- * {@inheritDoc} - */ - @Override - protected Statement withAfters(final FrameworkMethod method, final Object target, final Statement stmt) { - ensureInitialized(); - Statement statement = super.withAfters(method, target, stmt); // NOPMD.CloseResource - if (method.equals(Iterables.getLast(expectedMethods))) { - final List afters = getTestClass().getAnnotatedMethods(AfterAll.class); - statement = afters.isEmpty() ? statement : new RunAfters(statement, afters, target); - } - return statement; - } - - /** {@inheritDoc} */ - @Override - protected List computeTestMethods() { - final Collection result = Sets.newHashSet(); - for (final Class annotationClass : TEST_ANNOTATIONS) { - result.addAll(getTestClass().getAnnotatedMethods(annotationClass)); - } - return Lists.newArrayList(result); - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jvmmodel/AbstractJvmModelInferrerTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jvmmodel/AbstractJvmModelInferrerTest.java deleted file mode 100644 index f48c475ba..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jvmmodel/AbstractJvmModelInferrerTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.jvmmodel; - -import java.util.Set; - -import org.eclipse.emf.ecore.EObject; - -import com.avaloq.tools.ddk.xtext.test.modelinference.AbstractModelInferrerTest; - - -/** - * Base class for JVM model inference test implementations. - */ -public abstract class AbstractJvmModelInferrerTest extends AbstractModelInferrerTest { - - @Override - protected Set getInferredElements(final EObject sourceElement) { - return InferredJvmModelUtil.getInferredElements(sourceElement); - } - - /** - * Returns an inferred element of specified name and type for a given source element. - * - * @param sourceElement - * a source {@link EObject}, must not be {@code null} - * @param name - * the name of the element, must not be {@code null} - * @param clazz - * the type of the element, must not be {@code null} - * @return an inferred element, or {@code null} if nothing has been found - */ - @Override - protected EObject getInferredElement(final EObject sourceElement, final String name, final Class clazz) { - return InferredJvmModelUtil.getInferredElement(sourceElement, name, clazz); - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/linking/AbstractLinkingTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/linking/AbstractLinkingTest.java deleted file mode 100644 index 0f3e18ea3..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/linking/AbstractLinkingTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.linking; - -import static org.junit.Assert.assertEquals; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.util.EcoreUtil; - -import com.avaloq.tools.ddk.xtext.test.AbstractXtextTest; - - -/** - * Test linking of a given input file. - */ -@SuppressWarnings("PMD.AbstractClassWithoutAnyMethod") -public abstract class AbstractLinkingTest extends AbstractXtextTest { - private static final String NOT_RESOLVED_MESSAGE = "Cross-reference has not been resolved."; //$NON-NLS-1$ - - /** - * Asserts that the actualObject equals the expectedObject. - * - * @param expectedObject - * the expected object - * @param actualObject - * the actual object - */ - protected void assertReferenceResolved(final EObject expectedObject, final EObject actualObject) { - assertEquals(NOT_RESOLVED_MESSAGE, EcoreUtil.getURI(expectedObject), EcoreUtil.getURI(actualObject)); - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/modelinference/AbstractModelInferrerTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/modelinference/AbstractModelInferrerTest.java deleted file mode 100644 index 919ac60d1..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/modelinference/AbstractModelInferrerTest.java +++ /dev/null @@ -1,139 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.modelinference; - -import static org.junit.Assert.assertTrue; - -import java.util.List; -import java.util.Set; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.util.EcoreUtil; - -import com.avaloq.tools.ddk.typesystem.typemodel.INamedElement; -import com.avaloq.tools.ddk.xtext.test.AbstractXtextMarkerBasedTest; -import com.google.common.collect.Lists; - - -/** - * Abstract base class for model inference test implementations. - */ -public abstract class AbstractModelInferrerTest extends AbstractXtextMarkerBasedTest { - - private boolean oldAutoBuildState; - - @Override - protected void beforeAllTests() { - super.beforeAllTests(); - oldAutoBuildState = getTestProjectManager().setAutobuild(true); - } - - @Override - protected void afterAllTests() { - getTestProjectManager().setAutobuild(oldAutoBuildState); - super.afterAllTests(); - } - - /** - * Returns the inferred elements for the given source element. - * - * @param sourceElement - * a source {@link EObject}, must not be {@code null} * - * @return the inferred elements for the given source element, never {@code null} - */ - protected abstract Set getInferredElements(EObject sourceElement); - - /** - * Validates the list of sources for correctness. - * - * @param sourceNames - * list of sources, must not be {@code null} - */ - protected void validateSources(final String... sourceNames) { - for (String sourceName : sourceNames) { - getXtextTestUtil().validateSource(sourceName, getTestProjectManager().getTestSource(sourceName).getContent()); - } - } - - @Override - protected List getRequiredSourceFileNames() { - return Lists.newArrayList(); // Override the behavior of the parent - } - - /** - * Builds the workspace or waits until it is built. Updates the {@link TestState} after that. - */ - protected void build() { - getTestProjectManager().build(); - } - - /** - * Asserts that nothing has been inferred for the tagged element. - * - * @param tag - * A tag for an element - */ - protected void assertNoInference(final int tag) { - assertNoInference(getObjectForTag(tag)); - } - - /** - * Asserts that nothing has been inferred for a given source element. - * - * @param sourceElement - * a source {@link EObject}, must not be {@code null} - */ - protected void assertNoInference(final EObject sourceElement) { - final Set inferredElements = getInferredElements(sourceElement); - assertTrue("Unexpected inferred elements found: " + inferredElements.toString(), inferredElements.isEmpty()); //$NON-NLS-1$ - } - - /** - * Returns an inferred element of specified name and type for a given tag. - *

- * Note: Returns the first encountered, if multiple elements have the same name. - *

- * - * @param tag - * the tag for the element that should infer an element, must not be {@code null} - * @param name - * the name of the element, must not be {@code null} - * @param clazz - * the type of the element, must not be {@code null} - * @return an inferred element, or {@code null} if nothing has been found - */ - protected EObject getInferredElement(final int tag, final String name, final Class clazz) { - return getInferredElement(getObjectForTag(tag), name, clazz); - } - - /** - * Returns an inferred element of specified name and type for a given source element. - * - * @param sourceElement - * a source {@link EObject}, must not be {@code null} - * @param name - * the name of the element, must not be {@code null} - * @param clazz - * the type of the element, must not be {@code null} - * @return an inferred element, or {@code null} if nothing has been found - */ - protected EObject getInferredElement(final EObject sourceElement, final String name, final Class clazz) { - final Set inferredElements = getInferredElements(sourceElement); - EObject target = null; - for (final EObject obj : inferredElements) { - if (clazz.isAssignableFrom(obj.getClass()) && ((INamedElement) obj).getName().equals(name)) { - target = EcoreUtil.resolve(obj, sourceElement); - break; - } - } - return target; - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/resource/AbstractResourceDescriptionManagerTest.xtend b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/resource/AbstractResourceDescriptionManagerTest.xtend deleted file mode 100644 index 8f0c29679..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/resource/AbstractResourceDescriptionManagerTest.xtend +++ /dev/null @@ -1,198 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.resource; - -import com.avaloq.tools.ddk.xtext.test.AbstractXtextTest; -import org.eclipse.xtext.resource.IResourceDescription -import com.avaloq.tools.ddk.xtext.resource.AbstractCachingResourceDescriptionManager -import org.eclipse.xtext.resource.IResourceDescriptions -import org.eclipse.emf.common.util.URI -import java.util.Collection -import org.eclipse.xtext.resource.IResourceDescription.Delta -import com.google.common.collect.HashMultiset -import org.junit.Assert -import com.google.common.collect.Sets -import com.avaloq.tools.ddk.xtext.test.TestSource - -/** - * Abstract base class for {@link AbstractCachingResourceDescriptionManager} tests. - */ -abstract class AbstractResourceDescriptionManagerTest extends AbstractXtextTest { - - /** - * Simple unchanged {@link Delta} implementation with {@link URI}. - */ - static class UnchangedDelta implements Delta { - - val URI uri; - - new(URI uri) { - this.uri = uri; - } - - override getNew() { - null - } - - override getOld() { - null - } - - override getUri() { - uri - } - - override haveEObjectDescriptionsChanged() { - false - } - } - - val AbstractCachingResourceDescriptionManager resourceDescriptionManager = testUtil.get(IResourceDescription.Manager) as AbstractCachingResourceDescriptionManager; - val IResourceDescriptions index = testUtil.get(IResourceDescriptions); - - /** - * Returns the {@link AbstractCachingResourceDescriptionManager) to use in the test. - * - * @return the {@link AbstractCachingResourceDescriptionManager) to use in the test, never {@code null} - */ - def AbstractCachingResourceDescriptionManager getResourceDescriptionManager() { - return resourceDescriptionManager; - } - - /** - * Returns the {@link IResourceDescriptions) to use in the test. - * - * @return the {@link IResourceDescriptions) to use in the test, never {@code null} - */ - def IResourceDescriptions getResourceDescriptions() { - return index; - } - - /** - * Returns the candidates for the affected resource computations. - *

- * Note: By default, all registered sources are considered as candidates. - *

- * - * @return the candidates for the affected resource computation, never {@code null} - */ - def Collection getCandidates() { - var Collection candidates = testInformation.getTestObject(URI) as Collection; - if (candidates === null) { - candidates = Sets.newHashSet; - testInformation.putTestObject(URI, candidates); - } - return candidates; - } - - /** - * Creates a {@link TestSource} and considers it as a candidate by default. - * - * @param sourceFileName - * file name for the test source, must not be {@code null} - * @param content - * content of source, must not be {@code null} - * @return a new {@link TestSource} with the given parameters, never {@code null} - */ - override protected createTestSource(String sourceFileName, String content) { - val testSource = super.createTestSource(sourceFileName, content); - getCandidates().add(testSource.uri); - return testSource; - } - - /** - * Creates a {@link Delta} for the given {@link URI}. - *

- * Note: By default, the delta is an instance of {@link UnchangedDelta} - *

- * - * @param uri - * the delta {@link URI}, must not be {@code null} - * @return a new {@link Delta}, never {@code null} - */ - def Delta createDelta(URI uri) { - return new UnchangedDelta(uri); - } - - /** - * Returns the {@link URI} of the {@link TestSource} with the given file name. - * - * @param sourceFileName - * file name for the test source, must not be {@code null} - * @return the {@link URI} of the {@link TestSource} with the given file name, never {@code null} - */ - def URI getUri(String sourceFileName) { - return getTestSource(sourceFileName).uri; - } - - /** - * Asserts that the given delta causes the expected set of affected resources. - *

- * Note: Uses the candidates returned by {@link #getCandidates()}. - *

- * - * @param deltaSourceName - * the delta source name, must not be {@code null} - * @param expectedSourceNames - * the expected affected source names, must not be {@code null} - */ - def assertAffectedResources(String deltaSourceName, String... expectedSourceNames) { - val Collection expectedUris = Sets.newHashSet; - for (sourceName : expectedSourceNames) { - expectedUris.add(getUri(sourceName)); - } - assertAffectedResources(Sets.newHashSet(getUri(deltaSourceName)), getCandidates(), expectedUris); - } - - /** - * Asserts that the given set of deltas causes the expected set of affected {@link URI}s. - *

- * Note: Uses the candidates returned by {@link #getCandidates()}. - *

- * - * @param deltaUris - * the delta {@link URI}s, must not be {@code null} - * @param expectedUris - * the expected affected {@link URI}s, must not be {@code null} - */ - def assertAffectedResources(Collection deltaUris, Collection expectedUris) { - assertAffectedResources(deltaUris, getCandidates(), expectedUris); - } - - /** - * Asserts that the given set of deltas causes the expected set of affected {@link URI}s. - * - * @param deltaUris - * the delta {@link URI}s, must not be {@code null} - * @param candidates - * the potential candidates that can be affected, must not be {@code null} - * @param expectedUris - * the expected affected {@link URI}s, must not be {@code null} - */ - def assertAffectedResources(Collection deltaUris, Collection candidates, Collection expectedUris) { - assertDeltaAffectedResources(Sets.newHashSet(deltaUris.map[createDelta(it)]), candidates, expectedUris); - } - - /** - * Asserts that the given set of deltas causes the expected set of affected {@link URI}s. - * - * @param deltas - * the {@link Delta}s, must not be {@code null} - * @param candidates - * the potential candidates that can be affected, must not be {@code null} - * @param expectedUris - * the expected affected {@link URI}s, must not be {@code null} - */ - def assertDeltaAffectedResources(Collection deltas, Collection candidates, Collection expectedUris) { - val result = getResourceDescriptionManager().getAffectedResources(deltas, candidates, getResourceDescriptions()); - Assert.assertEquals("Affected URIs must be correct.", HashMultiset.create(expectedUris), HashMultiset.create(result)); - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/scoping/AbstractScopingTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/scoping/AbstractScopingTest.java deleted file mode 100644 index 989fb29ce..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/scoping/AbstractScopingTest.java +++ /dev/null @@ -1,877 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.scoping; - -import static com.avaloq.tools.ddk.xtext.linking.AbstractFragmentProvider.REP_SEPARATOR; -import static com.avaloq.tools.ddk.xtext.linking.AbstractFragmentProvider.SEGMENT_SEPARATOR; -import static com.avaloq.tools.ddk.xtext.resource.AbstractSelectorFragmentProvider.EQ_OP; -import static com.avaloq.tools.ddk.xtext.resource.AbstractSelectorFragmentProvider.SELECTOR_END; -import static com.avaloq.tools.ddk.xtext.resource.AbstractSelectorFragmentProvider.SELECTOR_START; -import static com.avaloq.tools.ddk.xtext.resource.AbstractSelectorFragmentProvider.UNIQUE; -import static com.avaloq.tools.ddk.xtext.resource.AbstractSelectorFragmentProvider.VALUE_SEP; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; -import java.util.regex.Pattern; - -import org.eclipse.core.runtime.Assert; -import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EClass; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.util.EObjectResolvingEList; -import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.osgi.util.NLS; -import org.eclipse.xtext.Assignment; -import org.eclipse.xtext.CrossReference; -import org.eclipse.xtext.EcoreUtil2; -import org.eclipse.xtext.naming.QualifiedName; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; -import org.eclipse.xtext.resource.IEObjectDescription; -import org.eclipse.xtext.resource.IResourceDescription; -import org.eclipse.xtext.resource.IResourceServiceProvider; -import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.scoping.IScope; -import org.eclipse.xtext.scoping.IScopeProvider; -import org.eclipse.xtext.util.Triple; -import org.eclipse.xtext.xbase.lib.Pair; - -import com.avaloq.tools.ddk.caching.Regexps; -import com.avaloq.tools.ddk.xtext.linking.AbstractFragmentProvider; -import com.avaloq.tools.ddk.xtext.naming.QualifiedNames; -import com.avaloq.tools.ddk.xtext.resource.IFingerprintComputer; -import com.avaloq.tools.ddk.xtext.scoping.ContainerQuery; -import com.avaloq.tools.ddk.xtext.scoping.IDomain; -import com.avaloq.tools.ddk.xtext.test.AbstractXtextMarkerBasedTest; -import com.google.common.base.Function; -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - - -/** - * Base class for scoping tests. - */ -@SuppressWarnings("nls") -public abstract class AbstractScopingTest extends AbstractXtextMarkerBasedTest { - private static final String PARAMETER_EXPECTED_OBJECTS = "expectedObjects"; - private static final String PARAMETER_REFERENCE = "reference"; - private static final String PARAMETER_CONTEXT = "context"; - public static final String TOP_LEVEL_OBJECT_FRAGMENT = SEGMENT_SEPARATOR + "0" + SEGMENT_SEPARATOR + "1"; - public static final String TOP_LEVEL_SURROGATE_FRAGMENT = SEGMENT_SEPARATOR + "0" + REP_SEPARATOR + "2"; - public static final String INFERRED_DATA_DICTIONARY_FRAGMENT = SEGMENT_SEPARATOR + "1"; - - private static final String NUMBER_OF_ELEMENTS_MESSAGE = "Incorrect number of elements in scope."; - private static final Splitter FRAGMENT_SEGMENT_SPLITTER = Splitter.onPattern("(? expectedLinkAssertions = new ArrayList(); - - /** - * Creates a new instance of {@link AbstractScopingTest}. - */ - public AbstractScopingTest() { - this(new IDomain.NullMapper()); - - } - - /** - * Creates a new instance of {@link AbstractScopingTest}. - * - * @param domainMapper - * the domainMapper to use - */ - public AbstractScopingTest(final IDomain.Mapper domainMapper) { - this.domainMapper = domainMapper; - } - - /** - * Returns all contents of the main {@link XtextTestResource}. - * - * @return all contents of the main {@link XtextTestResource} - */ - @SuppressWarnings("unchecked") - public Iterable getContents() { - return (Iterable) getTestInformation().getTestObject(Iterable.class); - } - - /** - * Set up scoping. - */ - @Override - protected void beforeAllTests() { - super.beforeAllTests(); - Iterable allContents = new Iterable() { - @Override - public Iterator iterator() { - return getXtextTestResource().getAllContents(); - } - }; - getTestInformation().putTestObject(Iterable.class, allContents); - } - - @Override - protected void afterEachTest() { - assertTrue("Expected links were set with link(int) but testLinking(String, CharSequence) was never called", expectedLinkAssertions.isEmpty()); - super.afterEachTest(); - } - - /** - * Returns the scope provider used for unit testing. - * - * @return the scope provider instance - */ - protected IScopeProvider getScopeProvider() { - return getXtextTestUtil().get(IScopeProvider.class); - } - - /** - * Check if scope expected is found in context provided. - * - * @param context - * element from which an element shall be referenced - * @param reference - * to be used to filter the elements - * @param expectedSourceName - * (upper case!) name of scope element to look for (kernel source) - * @param expectedSourceType - * type of scope element to look for - */ - protected void assertScope(final EObject context, final EReference reference, final String expectedSourceName, final String expectedSourceType) { - assertScope(context, reference, QualifiedNames.safeQualifiedName(expectedSourceName), getTargetSourceUri(expectedSourceName + '.' - + expectedSourceType).appendFragment(TOP_LEVEL_OBJECT_FRAGMENT)); - } - - /** - * Check if scope expected is found in context provided. - * - * @param context - * element from which an element shall be referenced - * @param reference - * to be used to filter the elements - * @param expectedElementName - * name of scope element to look for - * @param expectedSourceName - * (upper case!) name of the source within to look for the scope element - * @param expectedSourceType - * type of scope element to look for - * @param referenceElementType - * the type of the referenced element - */ - @SuppressWarnings("PMD.UseObjectForClearerAPI") - protected void assertScopeForElement(final EObject context, final EReference reference, final String expectedElementName, final String expectedSourceName, final String expectedSourceType, final String referenceElementType) { - assertScope(context, reference, QualifiedNames.safeQualifiedName(expectedElementName), getTargetSourceUri(expectedSourceName + '.' - + expectedSourceType).appendFragment(TOP_LEVEL_OBJECT_FRAGMENT + SEGMENT_SEPARATOR + referenceElementType)); - } - - /** - * Check if scope expected is found in context provided. - * - * @param context - * element from which an element shall be referenced - * @param reference - * to be used to filter the elements - * @param expectedUris - * of source referenced - */ - protected void assertScope(final EObject context, final EReference reference, final URI... expectedUris) { - assertScope(context, reference, Sets.newHashSet(expectedUris)); - } - - /** - * Check if scope expected is found in context provided. - * - * @param context - * element from which an element shall be referenced - * @param reference - * to be used to filter the elements - * @param expectedUriSet - * of source referenced - */ - protected void assertScope(final EObject context, final EReference reference, final Set expectedUriSet) { - IScope scope = getScopeProvider().getScope(context, reference); - for (IEObjectDescription description : scope.getAllElements()) { - expectedUriSet.remove(description.getEObjectURI()); - if (expectedUriSet.isEmpty()) { - return; - } - } - assertTrue("Expected URIs not found in scope: " + expectedUriSet, expectedUriSet.isEmpty()); - } - - /** - * Checks if the given objects are in scope of the given reference for the given context. - * - * @param context - * {@link EObject} element from which an element shall be referenced, must not be {@code null} - * @param reference - * the structural feature of {@code context} for which the scope should be asserted, must not be {@code null} and part of the context element - * @param expectedObjects - * for given scope, must not be {@code null} - */ - protected void assertScopedObjects(final EObject context, final EReference reference, final EObject... expectedObjects) { - Assert.isNotNull(expectedObjects, PARAMETER_EXPECTED_OBJECTS); - assertScopedObjects(context, reference, Lists.newArrayList(expectedObjects)); - } - - /** - * Checks if the given objects are in scope of the given reference for the given context. - * - * @param context - * {@link EObject} element from which an element shall be referenced, must not be {@code null} - * @param reference - * the structural feature of {@code context} for which the scope should be asserted, must not be {@code null} and part of the context element - * @param firstExpectedObjectCollection - * for given scope, must not be {@code null} - * @param furtherExpectedObjectCollections - * for given scope, must not be {@code null} - */ - @SuppressWarnings("unchecked") - protected void assertScopedObjects(final EObject context, final EReference reference, final Collection firstExpectedObjectCollection, final Collection... furtherExpectedObjectCollections) { - Assert.isNotNull(firstExpectedObjectCollection, "firstExpectedObjectCollection"); - Assert.isNotNull(furtherExpectedObjectCollections, "furtherExpectedObjectCollections"); - Collection consolidatedList = Lists.newArrayList(firstExpectedObjectCollection); - for (Collection expectedObjects : furtherExpectedObjectCollections) { - consolidatedList.addAll(expectedObjects); - } - assertScopedObjects(context, reference, consolidatedList); - } - - /** - * Checks if the scope of the given reference for the given context contains only the expected objects. - * In addition, checks that the reference of the given context references at least one of the expected - * objects. If the reference has multiplicity > 1, then every reference must reference at least - * one of the expected objects. - * - * @param context - * {@link EObject} from which the given objects shall be referenced, must not be {@code null} - * @param reference - * the structural feature of {@code context} for which the scope should be asserted, must not be {@code null} and part of the context element - * @param expectedObjects - * the objects expected in the scope, must not be {@code null} - */ - protected void assertScopedObjects(final EObject context, final EReference reference, final Collection expectedObjects) { - Assert.isNotNull(context, PARAMETER_CONTEXT); - Assert.isNotNull(reference, PARAMETER_REFERENCE); - Assert.isNotNull(expectedObjects, PARAMETER_EXPECTED_OBJECTS); - Assert.isTrue(context.eClass().getEAllReferences().contains(reference), String.format("Contract for argument '%s' failed: Parameter is not within specified range (Expected: %s, Actual: %s).", PARAMETER_CONTEXT, "The context object must contain the given reference.", "Reference not contained by the context object!")); - Set expectedUriSet = Sets.newHashSet(); - for (EObject object : expectedObjects) { - expectedUriSet.add(EcoreUtil.getURI(object)); - } - IScope scope = getScopeProvider().getScope(context, reference); - Iterable allScopedElements = scope.getAllElements(); - Set scopedUriSet = Sets.newHashSet(); - for (IEObjectDescription description : allScopedElements) { - URI uri = description.getEObjectURI(); - scopedUriSet.add(uri); - } - if (!expectedUriSet.equals(scopedUriSet)) { - fail("The scope must exactly consist of the expected URIs. Missing " + Sets.difference(expectedUriSet, scopedUriSet) + " extra " - + Sets.difference(scopedUriSet, expectedUriSet)); - } - // test that link resolving worked - boolean elementResolved; - if (reference.isMany()) { - @SuppressWarnings("unchecked") - EList objects = (EList) context.eGet(reference, true); - elementResolved = !objects.isEmpty(); // NOPMD - for (Iterator objectIter = objects.iterator(); objectIter.hasNext() && elementResolved;) { - EObject eObject = EcoreUtil.resolve(objectIter.next(), context); - elementResolved = expectedUriSet.contains(EcoreUtil.getURI(eObject)); - } - } else { - EObject resolvedObject = (EObject) context.eGet(reference, true); - elementResolved = expectedUriSet.contains(EcoreUtil.getURI(resolvedObject)); - } - assertTrue("Linking must have resolved one of the expected objects.", elementResolved); - } - - /** - * Check if scope expected is found in context provided. - * - * @param context - * element from which an element shall be referenced - * @param reference - * to be used to filter the elements - * @param expectedName - * name of scope element to look for - * @param expectedUri - * of source referenced - */ - private void assertScope(final EObject context, final EReference reference, final QualifiedName expectedName, final URI expectedUri) { - IScope scope = getScopeProvider().getScope(context, reference); - Iterable descriptions = scope.getElements(expectedName); - assertFalse("Description missing for: " + expectedName, Iterables.isEmpty(descriptions)); - URI currentUri = null; - for (IEObjectDescription desc : descriptions) { - currentUri = desc.getEObjectURI(); - if (currentUri.equals(expectedUri)) { - return; - } - } - assertEquals("Scope URI is not equal to expected URI", expectedUri, currentUri); - } - - /** - * Assert the scope for given elements. - * - * @param context - * the context - * @param reference - * the reference - * @param expectedSourceName - * the name of the referenced source (without file extension) - * @param expectedSourceType - * type of scope element to look for - * @param elementNames - * array of tuples with the name and uri of each element - */ - protected void assertScopeForElements(final EObject context, final EReference reference, final String expectedSourceName, final String expectedSourceType, final String[]... elementNames) { - for (String[] elementName : elementNames) { - assertScopeForElement(context, reference, elementName[0], expectedSourceName, expectedSourceType, elementName[1]); - } - int actualScopeSize = Iterables.size(getScopeProvider().getScope(context, reference).getAllElements()); - assertEquals(NUMBER_OF_ELEMENTS_MESSAGE, elementNames.length, actualScopeSize); - } - - /** - * Asserts the scope for the given context, reference, source type, and elements. - * - * @param context - * the context object - * @param reference - * the reference feature - * @param expectedSourceType - * the source-type name - * @param elements - * list of triples with the expected elements, each triple ordered as: {element name, source name, URI fragment} - */ - protected void assertScopeForElements(final EObject context, final EReference reference, final String expectedSourceType, final List> elements) { - Iterable allElements = getScopeProvider().getScope(context, reference).getAllElements(); - - // create a set containing the URIs (to avoid counting any duplicates the scope provider might have delivered) - Set uris = new HashSet(); - for (IEObjectDescription d : allElements) { - uris.add(d.getEObjectURI()); - } - - int actualScopeSizeWithoutDuplicates = uris.size(); - assertEquals(NUMBER_OF_ELEMENTS_MESSAGE, elements.size(), actualScopeSizeWithoutDuplicates); - for (Triple elementName : elements) { - assertScopeForElement(context, reference, elementName.getFirst(), elementName.getSecond(), expectedSourceType, elementName.getThird()); - } - } - - /** - * Asserts that the scope of the reference in the given context contains exactly the given sources. - * - * @param scopeContext - * the context of the scope test - * @param reference - * the reference to check its scope for - * @param modelElementClass - * the {@link EClass} of the model element to find - * @param sources - * the array of sources - */ - protected void assertScopeForSources(final EObject scopeContext, final EReference reference, final EClass modelElementClass, final String... sources) { - assertScope(scopeContext, reference, getExpectedURIs(scopeContext, modelElementClass, sources)); - int actualScopeSize = Iterables.size(getScopeProvider().getScope(scopeContext, reference).getAllElements()); - assertEquals(NUMBER_OF_ELEMENTS_MESSAGE, sources.length, actualScopeSize); - } - - /** - * Returns the expected uris for the given sources in the given context. - * - * @param context - * the context - * @param modelElementClass - * the class of the exported model element - * @param sources - * the sources to get the uris for - * @return the expected uris for the given sources in the given context - */ - private Set getExpectedURIs(final EObject context, final EClass modelElementClass, final String... sources) { - Set expectedURIs = new HashSet(); - for (String source : sources) { - expectedURIs.add(Iterables.get(getExportedObjects(context, modelElementClass, source), 0).getEObjectURI()); - } - return expectedURIs; - } - - /** - * Gets the exported objects. - * - * @param context - * the context - * @param type - * the type - * @param resourcePattern - * the resource pattern - * @return the exported objects - */ - public Iterable getExportedObjects(final EObject context, final EClass type, final String resourcePattern) { - Pattern regexp = Regexps.fromGlob(URI.encodeSegment(resourcePattern, true)); - return Iterables.filter(ContainerQuery.newBuilder(domainMapper, type).execute(context), (o) -> regexp.matcher(o.getEObjectURI().lastSegment()).matches()); - } - - /** - * Gets the exported names. - * - * @param execute - * the execute - * @return the exported names - */ - public List getExportedNames(final Iterable execute) { - return Lists.newArrayList(Iterables.transform(execute, new Function() { - @Override - public String apply(final IEObjectDescription from) { - return from.getName().toString(); - } - })); - } - - /** - * Checks if an object with given name (case sensitive) and type is exported. - * - * @param context - * the context - * @param name - * the name - * @param type - * the type - * @return true, if is exported - */ - public boolean isExported(final EObject context, final String name, final EClass type) { - return isExported(context, name, type, false); - } - - /** - * Checks if an object with given name, case sensitive or not, and type is exported. - * - * @param context - * the context - * @param name - * the name - * @param type - * the type - * @param ignoreCase - * the ignore case - * @return true, if is exported - */ - public boolean isExported(final EObject context, final String name, final EClass type, final boolean ignoreCase) { - List exportedNames = getExportedNames(ContainerQuery.newBuilder(domainMapper, type).execute(context)); - if (ignoreCase) { - return Iterables.contains(Iterables.transform(exportedNames, new Function() { - @Override - public String apply(final String from) { - return from.toLowerCase(); // NOPMD - } - }), name); - } else { - return exportedNames.contains(name); - } - } - - /** - * Gets the resource description for a given Xtext resource. - * - * @param resource - * the resource - * @return the resource description - */ - protected final IResourceDescription getResourceDescription(final XtextResource resource) { - final IResourceServiceProvider resourceServiceProvider = resource.getResourceServiceProvider(); - final IResourceDescription.Manager descriptionManager = resourceServiceProvider.getResourceDescriptionManager(); - return descriptionManager.getResourceDescription(resource); - } - - /** - * Gets the fingerprint for a given resource description, returns null if resource description does not export any objects or if a non-existing - * user data field was queried. - * - * @param description - * the description - * @return the fingerprint or null if no fingerprint found - */ - protected String getFingerprint(final IResourceDescription description) { - Iterable objects = description.getExportedObjects(); - if (!Iterables.isEmpty(objects)) { - IEObjectDescription objectDescription = Iterables.get(objects, 0); - return objectDescription.getUserData(IFingerprintComputer.RESOURCE_FINGERPRINT); - } - return null; - } - - /** - * Creates a top level URI fragment with a leading segment separator from the given segments - * taking into account repetitions. - * - * @param segments - * list of feature IDs, indexes (for multi valued features), and other fragment segments - * @return URI fragment - */ - public static String createTopLevelURIFragment(final Object... segments) { - return createURIFragment(true, segments); - } - - /** - * Creates a URI fragment from the given segments taking into account repetitions. - * - * @param segments - * list of feature IDs, indexes (for multi valued features), and other fragment segments - * @return URI fragment - */ - public static String createURIFragment(final Object... segments) { - return createURIFragment(false, segments); - } - - /** - * Creates a URI fragment from the given segments taking into account repetitions. - * - * @param topLevel - * whether the fragment is top level resulting in a leading segment separator. - * @param segments - * list of feature IDs, indexes (for multi valued features), and other fragment segments - * @return URI fragment - */ - @SuppressWarnings("PMD.UnusedPrivateMethod") - private static String createURIFragment(final boolean topLevel, final Object... segments) { - StringBuilder b = new StringBuilder(); - if (segments.length == 0) { - return b.toString(); - } - if (topLevel) { - b.append(SEGMENT_SEPARATOR); - } - List parsedSegments = Lists.newArrayList(); - for (Object segment : segments) { - Iterables.addAll(parsedSegments, FRAGMENT_SEGMENT_SPLITTER.split(segment.toString())); - } - - String lastSegment = parsedSegments.get(0); - int reps = 1; - for (int i = 1; i < parsedSegments.size(); i++) { - if (parsedSegments.get(i).equals(lastSegment)) { - reps++; - continue; - } - b.append(lastSegment); - if (reps > 1) { - b.append(REP_SEPARATOR).append(reps); - reps = 1; - } - b.append(SEGMENT_SEPARATOR); - lastSegment = parsedSegments.get(i); - } - b.append(lastSegment); - if (reps > 1) { - b.append(REP_SEPARATOR).append(reps); - } - return b.toString(); - } - - /** - * Creates a URI fragment list segment for the given feature selection string and list index. - * - * @param feature - * the feature selection string, must not be {@code null} or empty - * @param index - * the list index, must not be negative - * @return the URI fragment list segment, never {@code null} or empty - */ - public static String listFragmentSegment(final String feature, final int index) { - return feature + AbstractFragmentProvider.LIST_SEPARATOR + index; - } - - /** - * Creates a URI fragment list segment for the given feature id and list index. - * - * @param featureId - * the featureId, must not be negative - * @param index - * the list index, must not be negative - * @return the URI fragment list segment, never {@code null} or empty - */ - public static String listFragmentSegment(final int featureId, final int index) { - return listFragmentSegment(String.valueOf(featureId), index); - } - - /** - * Creates a URI fragment segment to be used for languages using the {@code AbstractSelectorFragmentProvider}. - * - * @param containmentFeature - * containment feature - * @param selectorFeature - * selector feature - * @param value - * value for selector feature - * @param unique - * if value is unique - * @return URI fragment segment - */ - public static String selectorFragmentSegment(final int containmentFeature, final int selectorFeature, final String value, final boolean unique) { - StringBuilder builder = new StringBuilder(); - builder.append(containmentFeature).append(SELECTOR_START).append(selectorFeature).append(EQ_OP).append(VALUE_SEP).append(value).append(VALUE_SEP); - if (unique) { - builder.append(UNIQUE); - } - builder.append(SELECTOR_END); - return builder.toString(); - } - - /** - * Creates an expectation of a link. Use this method in tests to insert an expectation that a cross reference does actually point to the object tagged by the - * target tag. Expectations can be tested by calling {@link #testExpectedLinking()}. Implicit items will be traversed. - * - * @see #mark(int) - * @see #testLinking(String, CharSequence) - * @param targetTag - * Tag pointing to the destination object - * @return Mark text to be inserted in the source file, never {@code null} - */ - protected String link(final int targetTag) { - return link(() -> getObjectForTag(targetTag)); - } - - /** - * Creates an expectation of a link. Use this method in tests to insert an expectation that a cross reference does actually point to the object tagged by the - * target tag. Expectations can be tested by calling {@link #testExpectedLinking()}. Implicit items will be traversed. - * - * @see #mark(int) - * @see #testLinking(String, CharSequence) - * @param getTargetObject - * supplier to get the destination object, must not be {@code null} - * @return Mark text to be inserted in the source file, never {@code null} - */ - protected String link(final Supplier getTargetObject) { - final int sourceTag = getTag(); - expectedLinkAssertions.add(() -> testLinking(sourceTag, getTargetObject.get())); - return mark(sourceTag); - } - - /** - * Performs linking test. Checks expectations which were set in a source using {@link #link(int)} or {@link #link(Function, int). - * - * @see #link(int) - * @see #link(Supplier) - * @see #testLinking(int, EObject) - * @param sourceFileNameAndContent - * the file name and content, given as the key and value of the pair, respectively, must not be {@code null} - */ - protected void testLinking(final Pair sourceFileNameAndContent) { - testLinking(sourceFileNameAndContent.getKey(), sourceFileNameAndContent.getValue()); - } - - /** - * Performs linking test. Checks expectations which were set in a source using {@link #link(int)} or {@link #link(Function, int). - * - * @see #link(int) - * @see #link(Supplier) - * @see #testLinking(int, EObject) - * @param sourceFileName - * the file name that should be associated with the parsed content, must not be {@code null} - * @param sourceContent - * source, must not be {@code null} - */ - protected void testLinking(final String sourceFileName, final CharSequence sourceContent) { - registerModel(sourceFileName, sourceContent); - expectedLinkAssertions.forEach(Runnable::run); - expectedLinkAssertions.clear(); - } - - /** - * Performs linking test. Checks that given cross reference tagged with sourceTag does actually point to the object tagged by the target tag. - * Detailed error reporting can be viewed in a compare view. Implicit items will be traversed. - * - * @param sourceTag - * Tag pointing to cross reference - * @param targetTag - * Tag pointing to the destination object - */ - protected void testLinking(final int sourceTag, final int targetTag) { - testLinking(sourceTag, getObjectForTag(targetTag), true); - } - - /** - * Performs linking test. Checks that the cross reference marked with sourceTag does actually point to the object provided as the second argument. Implicit - * items will be traversed. - * - * @param sourceTag - * Tag pointing to a cross reference - * @param targetObject - * Expected target object - */ - protected void testLinking(final int sourceTag, final EObject targetObject) { - testLinking(sourceTag, targetObject, true); - } - - /** - * Performs linking test. Checks that the source object is the same as the object pointed by targetTag provided as the second argument. Implicit - * items will be traversed. - * - * @param sourceObject - * Source object - * @param targetTag - * Tag to the expected target object - */ - protected void testLinking(final EObject sourceObject, final int targetTag) { - testLinking(sourceObject, targetTag, true); - } - - /** - * Performs linking test. Checks that the cross reference marked with sourceTag does actually point to the object provided as the second argument. - * - * @param sourceTag - * Tag pointing to a cross reference - * @param targetObject - * Expected target object, must not be {@code null} - * @param traverseImplicitItems - * If target of a reference is an implicit item and this parameter is set to true, the test will get and compare the original object from which this - * implicit item was created - */ - protected void testLinking(final int sourceTag, final EObject targetObject, final boolean traverseImplicitItems) { - assertNotNull("Target object must not be null.", targetObject); //$NON-NLS-1$ - CrossReference crossReference = getMarkerTagsInfo().getCrossReference(sourceTag); - EObject referencedSourceObject = getCrossReferencedObject(sourceTag, traverseImplicitItems, crossReference); - assertEObjectsAreEqual(referencedSourceObject, targetObject, crossReference); - } - - /** - * Performs linking test. Checks that the source object is the same as the object pointed by targetTag provided as the second argument. - * Does not deal with cross-referencing. - * - * @param sourceObject - * Source object, must not be {@code null} - * @param targetTag - * Tag to the referenced target object - * @param traverseImplicitItems - * If target of a reference is an implicit item and this parameter is set to true, the test will get and compare the original object from which this - * implicit item was created - */ - @SuppressWarnings("PMD.UnusedFormalParameter") - protected void testLinking(final EObject sourceObject, final int targetTag, final boolean traverseImplicitItems) { - assertNotNull("Source object must not be null.", sourceObject); //$NON-NLS-1$ - EObject referencedTargetObject = getObjectForTag(targetTag); - assertEObjectsAreEqual(sourceObject, referencedTargetObject, null); - } - - /** - * Asserts whether the two objects are equal. - * - * @param sourceObject - * First object needed for comparison. - * @param targetObject - * Target object needed for comparison. - * @param crossReference - * CrossReference object, can be {@code null} - */ - protected void assertEObjectsAreEqual(final EObject sourceObject, final EObject targetObject, final CrossReference crossReference) { - StringBuilder expected = new StringBuilder(80); - StringBuilder found = new StringBuilder(80); - if (crossReference != null) { - String crossReferenceText = "Cross reference:\n" + crossReference.toString() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ - expected.append(crossReferenceText); - found.append(crossReferenceText); - } - expected.append(LINKS_TO); - found.append(LINKS_TO); - URI targetUri = EcoreUtil.getURI(targetObject); - expected.append(targetUri); - String sourceObjectUri = EcoreUtil.getURI(sourceObject).toString(); - found.append(sourceObjectUri); - expected.append(WHICH_CORRESPONDS_TO); - INode node; - node = NodeModelUtils.findActualNodeFor(targetObject); - if (node != null) { - expected.append(NodeModelUtils.getTokenText(node)); - } else { - expected.append(NO_NODE_MODEL_COULD_BE_A_DERIVED_OBJECT); - } - found.append(WHICH_CORRESPONDS_TO); - node = NodeModelUtils.findActualNodeFor(sourceObject); - if (sourceObject.eIsProxy()) { - found.append(UNRESOLVED_REFERENCE); - } else if (node != null) { - found.append(NodeModelUtils.getTokenText(node)); - } else { - found.append(NO_NODE_MODEL_COULD_BE_A_DERIVED_OBJECT); - } - assertEquals("Errors found. Consider compare view.", expected.toString(), found.toString()); //$NON-NLS-1$ - } - - /** - * Returns the referenced {@link EObject} pointed to by the cross reference. - *

- * Note: For implicit item traversal to work, a custom implementation must be provided by overriding this method. - *

- * - * @param sourceTag - * the source tag - * @param traverseImplicitItems - * If target of a reference is an implicit item and this parameter is set to true, the test will get and compare the original object from which this - * implicit item was created. - * @param crossReference - * Cross reference to be resolved, must not be {@code null} - * @return the referenced {@link EObject}, must not be {@code null} - */ - @SuppressWarnings("PMD.UnusedFormalParameter") - protected EObject getCrossReferencedObject(final int sourceTag, final boolean traverseImplicitItems, final CrossReference crossReference) { - EObject context = getObjectForTag(sourceTag); - if (crossReference == null) { - throw new IllegalArgumentException(NLS.bind("Cross reference on object ''{0}'' could not be resolved.", context.toString())); //$NON-NLS-1$ - } - // We only handle references in assignments - Assignment assignment = EcoreUtil2.getContainerOfType(crossReference, Assignment.class); - EObject sourceObject; - String featureName = assignment.getFeature(); - EReference reference = (EReference) context.eClass().getEStructuralFeature(featureName); - if (reference.isMany()) { - Object featureValue = context.eGet(reference, false); - assertTrue("List must be of type EObjectResolvingEList", featureValue instanceof EObjectResolvingEList); //$NON-NLS-1$ - @SuppressWarnings("unchecked") - EList objects = (EObjectResolvingEList) context.eGet(reference, false); - if (objects.size() == 1) { - sourceObject = EcoreUtil.resolve(objects.get(0), context); - } else { - // TODO DSL-166: Handle this case when needed for tests. - throw new AssertionError("Multiple references not supported yet"); //$NON-NLS-1$ - } - } else { - sourceObject = (EObject) context.eGet(reference, true); - } - assertNotNull("Bad test. Referenced object is null.", sourceObject); //$NON-NLS-1$ - return sourceObject; - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/validation/AbstractValidationTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/validation/AbstractValidationTest.java deleted file mode 100644 index 7f529b690..000000000 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/validation/AbstractValidationTest.java +++ /dev/null @@ -1,1221 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test.validation; - -import static org.eclipse.xtext.validation.ValidationMessageAcceptor.INSIGNIFICANT_INDEX; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.eclipse.emf.common.util.BasicDiagnostic; -import org.eclipse.emf.common.util.Diagnostic; -import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.osgi.util.NLS; -import org.eclipse.xtext.EcoreUtil2; -import org.eclipse.xtext.diagnostics.AbstractDiagnostic; -import org.eclipse.xtext.linking.impl.XtextLinkingDiagnostic; -import org.eclipse.xtext.nodemodel.ICompositeNode; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; -import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.resource.XtextSyntaxDiagnostic; -import org.eclipse.xtext.util.CancelIndicator; -import org.eclipse.xtext.validation.AbstractValidationDiagnostic; -import org.eclipse.xtext.validation.FeatureBasedDiagnostic; -import org.eclipse.xtext.validation.RangeBasedDiagnostic; -import org.eclipse.xtext.xbase.lib.Pair; - -import com.avaloq.tools.ddk.xtext.test.AbstractXtextMarkerBasedTest; -import com.avaloq.tools.ddk.xtext.test.XtextTestSource; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import com.google.inject.Provider; - - -/** - * Base class for validation tests. - */ -@SuppressWarnings({"nls", "PMD.ExcessiveClassLength"}) -// CHECKSTYLE:CHECK-OFF MultipleStringLiterals -// CHECKSTYLE:OFF MagicNumber -public abstract class AbstractValidationTest extends AbstractXtextMarkerBasedTest { - - public static final int NO_ERRORS = 0; - - static final String NO_ERRORS_FOUND_ON_RESOURCE_MESSAGE = "Expecting no errors on resource"; - - private static final int SEVERITY_UNDEFINED = -1; - private static final Map CODE_TO_NAME = ImmutableMap.of(Diagnostic.INFO, "INFO", Diagnostic.WARNING, "WARNING", Diagnostic.ERROR, "ERROR"); - - private static final String LINE_BREAK = "\n"; - private static final String DOT_AND_LINEBREAK = "'." + LINE_BREAK; - - /** - * All diagnostics of the current testing file. - */ - private Diagnostic fileDiagnostics; - - /** - * During validation of a source we monitor diagnostics, that are found in the source but were not expected by the test. - * If the validation test is strict, then we will display these unexpected diagnostics as test error. - */ - private final Set unexpectedDiagnostics = Sets.newLinkedHashSet(); - private final Set unexpectedResourceDiagnostics = Sets.newLinkedHashSet(); - - /** - * validation results calculated during test setUp. - * - * @return the diagnostic for the primary test source file - */ - private Diagnostic getPrimaryDiagnostics() { - Object obj = getTestInformation().getTestObject(Diagnostic.class); - assertNotNull("getPrimaryDiagnostics(): Diagnostics of primary source not null.", obj); - return (Diagnostic) obj; - } - - /** - * Returns the unexpectedDiagnostics. - * - * @return the unexpectedDiagnostics - */ - protected Set getUnexpectedDiagnostics() { - return unexpectedDiagnostics; - } - - /** - * Returns the unexpectedDiagnostics. - * - * @return the unexpectedDiagnostics - */ - protected Set getUnexpectedResourceDiagnostics() { - return unexpectedResourceDiagnostics; - } - - /** - * Assertion testing for {@link AbstractValidationDiagnostic validation issues} at a given source position. - */ - protected class XtextDiagnosticAssertion extends AbstractModelAssertion { - - /** Issue code of the diagnostic. */ - private final String issueCode; - /** Issue message of the diagnostic. */ - private final String message; - /** - * Indicates whether the assertion must find the issue. - * Assertion creates an error if the existence of issue code for the target eobject doesn't correspond to the value of issueMustBeFound. - */ - private final boolean issueMustBeFound; - - private final int expectedSeverity; - - protected XtextDiagnosticAssertion(final String issueCode, final boolean issueMustBeFound) { - this(issueCode, issueMustBeFound, SEVERITY_UNDEFINED, null); - } - - protected XtextDiagnosticAssertion(final String issueCode, final boolean issueMustBeFound, final int severity, final String message) { - this.issueCode = issueCode; - this.issueMustBeFound = issueMustBeFound; - this.expectedSeverity = severity; - this.message = message; - } - - /** - * Check if the given issue code is found among issue codes for the object, located at the given position. - * - * @param root - * root object of the document - * @param pos - * position to locate the target object - */ - @Override - public void apply(final EObject root, final Integer pos) { - final Diagnostic diagnostics = validate(root); - final BasicDiagnostic diagnosticsOnTargetPosition = new BasicDiagnostic(); - boolean issueFound = false; - int actualSeverity = SEVERITY_UNDEFINED; - boolean expectedSeverityMatches = false; - boolean expectedMessageMatches = false; - String actualMessage = ""; - - for (AbstractValidationDiagnostic avd : Iterables.filter(diagnostics.getChildren(), AbstractValidationDiagnostic.class)) { - if (diagnosticPositionEquals(pos, avd)) { - // Add issue to the list of issues at the given position - diagnosticsOnTargetPosition.add(avd); - if (avd.getIssueCode().equals(issueCode)) { - issueFound = true; - actualSeverity = avd.getSeverity(); - // True if the expected severity is not set, or if matches with the actual one - expectedSeverityMatches = expectedSeverity == SEVERITY_UNDEFINED || expectedSeverity == actualSeverity; - actualMessage = avd.getMessage(); - // True if message matches with actual message or message is null - expectedMessageMatches = message == null || actualMessage.equals(message); - if (issueMustBeFound) { - // Remove the diagnostic from the list of non-expected diagnostics - getUnexpectedDiagnostics().remove(avd); - // Don't need to display error messages - if (expectedSeverityMatches && expectedMessageMatches) { - return; - } - } - } - } - } - - // Create error message - createErrorMessage(pos, diagnosticsOnTargetPosition, issueFound, expectedSeverityMatches, actualSeverity, expectedMessageMatches, actualMessage); - } - - /** - * Create an error message (if needed) based on the given input parameters. - * - * @param pos - * position in the source to associate the message with - * @param diagnosticsOnTargetPosition - * diagnostics on the specifies position - * @param issueFound - * specifies whether an issue has been found at the given position - * @param expectedSeverityMatches - * true if expected severity equals actual one, false otherwise - * @param actualSeverity - * actual severity - * @param expectedMessageMatches - * expected message matches - * @param actualMessage - * actual message - */ - private void createErrorMessage(final Integer pos, final BasicDiagnostic diagnosticsOnTargetPosition, final boolean issueFound, final boolean expectedSeverityMatches, final int actualSeverity, final boolean expectedMessageMatches, final String actualMessage) { - StringBuilder errorMessage = new StringBuilder(180); - if (issueMustBeFound && !issueFound) { - errorMessage.append("Expected issue not found. Code '").append(issueCode).append('\n'); - } else if (!issueMustBeFound && issueFound) { - errorMessage.append("There should be no issue with the code '").append(issueCode).append(DOT_AND_LINEBREAK); - } - if (issueFound && !expectedMessageMatches) { - errorMessage.append("Expected message does not match. Expected: '").append(message).append("', Actual: '").append(actualMessage).append('\n'); - } - // If the expected issue has been found, but the actual severity does not match with expected one - if (issueMustBeFound && issueFound && !expectedSeverityMatches) { - errorMessage.append("Severity does not match. Expected: ").append(CODE_TO_NAME.get(expectedSeverity)).append(". Actual: ").append(CODE_TO_NAME.get(actualSeverity)).append(".\n"); - } - // Memorize error message - if (errorMessage.length() > 0) { - if (!diagnosticsOnTargetPosition.getChildren().isEmpty()) { - errorMessage.append(" All issues at this position:\n"); - errorMessage.append(diagnosticsToString(diagnosticsOnTargetPosition, false)); - } - memorizeErrorOnPosition(pos, errorMessage.toString()); - } - } - - /** - * Compare if the position of the given diagnostic equals to the given position in text. - * - * @param pos - * position in text - * @param avd - * diagnostic that we check, if it has the same position as the given position in text - * @return - * TRUE if diagnostic has the same position as the given one, FALSE otherwise. - */ - protected boolean diagnosticPositionEquals(final Integer pos, final AbstractValidationDiagnostic avd) { - if (avd instanceof FeatureBasedDiagnostic && ((FeatureBasedDiagnostic) avd).getFeature() != null) { - List nodes = NodeModelUtils.findNodesForFeature(avd.getSourceEObject(), ((FeatureBasedDiagnostic) avd).getFeature()); - if (nodes.isEmpty()) { - INode node = NodeModelUtils.getNode(avd.getSourceEObject()); - INode firstNonHiddenLeafNode = getXtextTestUtil().findFirstNonHiddenLeafNode(node); - if (firstNonHiddenLeafNode == null) { - return issueMustBeFound; - } else if (firstNonHiddenLeafNode.getTotalOffset() == pos) { - return true; - } - } else { - int avdIndex = ((FeatureBasedDiagnostic) avd).getIndex(); - for (int i = 0; i < nodes.size(); i++) { - if (avdIndex == INSIGNIFICANT_INDEX || avdIndex == i) { - INode firstNonHiddenLeafNode = getXtextTestUtil().findFirstNonHiddenLeafNode(nodes.get(i)); - if (firstNonHiddenLeafNode == null) { - return issueMustBeFound; - } else if (firstNonHiddenLeafNode.getTotalOffset() == pos) { - return true; - } - } - } - } - } else if (avd instanceof RangeBasedDiagnostic) { - if (((RangeBasedDiagnostic) avd).getOffset() == pos) { - return true; - } - } else { - INode node = NodeModelUtils.getNode(avd.getSourceEObject()); - INode firstNonHiddenLeafNode = getXtextTestUtil().findFirstNonHiddenLeafNode(node); - if (firstNonHiddenLeafNode == null) { - return issueMustBeFound; - } else if (firstNonHiddenLeafNode.getTotalOffset() == pos) { - return true; - } - } - return false; - } - } - - /** - * Assertion testing for {@link AbstractValidationDiagnostic validation issues} at a given source position. - */ - private class ResourceDiagnosticAssertion extends AbstractModelAssertion { - - /** Issue code of the diagnostic. */ - private final String issueCode; - /** Issue message of the diagnostic. */ - private final String message; - /** - * Indicates whether the assertion must find the issue. - * Assertion creates an error if the existence of issue code for the target eobject doesn't correspond to the value of issueMustBeFound. - */ - private final boolean issueMustBeFound; - - private final int expectedSeverity; - - protected ResourceDiagnosticAssertion(final String issueCode, final boolean issueMustBeFound, final int severity, final String message) { - this.issueCode = issueCode; - this.issueMustBeFound = issueMustBeFound; - this.expectedSeverity = severity; - this.message = message; - } - - /** - * Check if the given issue code is found among issue codes for the object, located at the given position. - * - * @param root - * root object of the document - * @param pos - * position to locate the target object - */ - @Override - public void apply(final EObject root, final Integer pos) { - Iterable diagnostics = null; - switch (expectedSeverity) { - case Diagnostic.ERROR: - diagnostics = root.eResource().getErrors(); - break; - case Diagnostic.WARNING: - diagnostics = root.eResource().getWarnings(); - break; - case SEVERITY_UNDEFINED: - diagnostics = Iterables.concat(root.eResource().getErrors(), root.eResource().getWarnings()); - break; - } - final List diagnosticsOnTargetPosition = Lists.newArrayList(); - boolean issueFound = false; - int actualSeverity = expectedSeverity; - boolean expectedMessageMatches = false; - String actualMessage = ""; - - for (AbstractDiagnostic diag : Iterables.filter(diagnostics, AbstractDiagnostic.class)) { - if (diagnosticPositionEquals(pos, diag)) { - // Add issue to the list of issues at the given position - diagnosticsOnTargetPosition.add(diag); - if (diag.getCode() != null && diag.getCode().equals(issueCode)) { - issueFound = true; - if (expectedSeverity == SEVERITY_UNDEFINED) { - actualSeverity = root.eResource().getErrors().contains(diag) ? Diagnostic.ERROR : Diagnostic.WARNING; - } - actualMessage = diag.getMessage(); - // True if message matches with actual message or message is null - expectedMessageMatches = message == null || actualMessage.equals(message); - // Don't need to display error messages - if (issueMustBeFound) { - // Remove the diagnostic from the list of non-expected diagnostics - getUnexpectedResourceDiagnostics().remove(diag); - // Don't need to display error messages - if (expectedMessageMatches) { - return; - } - } - } - } - } - - // Create error message - createErrorMessage(pos, diagnosticsOnTargetPosition, issueFound, true, actualSeverity, expectedMessageMatches, actualMessage); - } - - /** - * Create an error message (if needed) based on the given input parameters. - * - * @param pos - * position in the source to associate the message with - * @param diagnosticsOnTargetPosition - * diagnostics on the specifies position - * @param issueFound - * specifies whether an issue has been found at the given position - * @param expectedSeverityMatches - * true if expected severity equals actual one, false otherwise - * @param actualSeverity - * actual severity - * @param expectedMessageMatches - * expected message matches - * @param actualMessage - * actual message - */ - private void createErrorMessage(final Integer pos, final List diagnosticsOnTargetPosition, final boolean issueFound, final boolean expectedSeverityMatches, final int actualSeverity, final boolean expectedMessageMatches, final String actualMessage) { - StringBuilder errorMessage = new StringBuilder(200); - if (issueMustBeFound && !issueFound) { - errorMessage.append("Expected issue not found. Code '").append(issueCode).append('\n'); - } else if (!issueMustBeFound && issueFound) { - errorMessage.append("There should be no issue with the code '").append(issueCode).append(DOT_AND_LINEBREAK); - } - if (issueFound && !expectedMessageMatches) { - errorMessage.append("Expected message does not match. Expected: '").append(message).append("', Actual: '").append(actualMessage).append('\n'); - } - // If the expected issue has been found, but the actual severity does not match with expected one - if (issueMustBeFound && issueFound && !expectedSeverityMatches) { - errorMessage.append("Severity does not match. Expected: ").append(CODE_TO_NAME.get(expectedSeverity)).append(". Actual: ").append(CODE_TO_NAME.get(actualSeverity)).append(".\n"); - } - // Memorize error message - if (errorMessage.length() > 0) { - if (!diagnosticsOnTargetPosition.isEmpty()) { - errorMessage.append(" All issues at this position:\n"); - errorMessage.append(diagnosticsToString(diagnosticsOnTargetPosition, false)); - } - memorizeErrorOnPosition(pos, errorMessage.toString()); - } - } - - /** - * Compare if the position of the given diagnostic equals to the given position in text. - * - * @param pos - * position in text - * @param diagnostic - * diagnostic that we check, if it has the same position as the given position in text - * @return - * {@code true} if diagnostic has the same position as the given one, {@code false} otherwise. - */ - private boolean diagnosticPositionEquals(final Integer pos, final AbstractDiagnostic diagnostic) { - return diagnostic.getOffset() == pos; - } - } - - /** - * Get a cached version of an object associated with the root object for a given key. - * - * @param - * type of the associated object - * @param root - * root EObject - * @param key - * key identifying the type of the associated object - * @param provider - * provider to deliver an object if there is no cached version - * @return - * cached version of the associated object - */ - protected T getCached(final EObject root, final String key, final Provider provider) { - XtextResource res = (XtextResource) root.eResource(); - return res.getCache().get(key, res, provider); - } - - /** - * Validate the model. - * - * @param root - * root EObject to validate - * @return - * validation results - */ - protected Diagnostic validate(final EObject root) { - return getCached(root, "DIAGNOSTIC", () -> getXtextTestUtil().getDiagnostician().validate(root)); - } - - /** - * Display the path from root object to the target EObject. - * - * @param eObject - * object to display the object path for - * @param offset - * string offset that is added in the beginning of each line - * @return - * object hierarchy as string (each object on a single line) - */ - private String pathFromRootAsString(final EObject eObject, final String offset) { - List hierarchy = Lists.newLinkedList(); - - EObject currentObject = eObject; - while (currentObject != null) { - hierarchy.add(0, offset + currentObject.toString()); - currentObject = currentObject.eContainer(); - } - - return String.join("\n", hierarchy); - } - - /** - * Persist list diagnostics into string to display the list of issue codes. - * - * @param diagnostics - * list of diagnostics - * @param displayPathToTargetObject - * if true, the path through the object hierarchy is printed out up to the root node - * @return - * string with list of issue codes, separated with a line break - */ - // TODO (ACF-4153) generalize for all kinds of errors and move to AbstractXtextTest - private String diagnosticsToString(final Diagnostic diagnostics, final boolean displayPathToTargetObject) { - StringBuilder sb = new StringBuilder(); - for (Diagnostic diagnostic : diagnostics.getChildren()) { - if (diagnostic instanceof AbstractValidationDiagnostic) { - AbstractValidationDiagnostic avd = (AbstractValidationDiagnostic) diagnostic; - sb.append(" "); - sb.append(avd.getIssueCode()); - if (displayPathToTargetObject) { - sb.append(" at line: "); - ICompositeNode compositeNode = NodeModelUtils.findActualNodeFor(avd.getSourceEObject()); - if (compositeNode != null) { - sb.append(compositeNode.getStartLine()); - } else { - sb.append("Unknown"); - } - sb.append(" on \n"); - sb.append(pathFromRootAsString(avd.getSourceEObject(), " ")); - } - sb.append(LINE_BREAK); - } - } - return sb.toString(); - } - - /** - * Persist list diagnostics into string to display the list of issue codes. - * - * @param diagnostics - * list of diagnostics - * @param displayPathToTargetObject - * if true, the path through the object hierarchy is printed out up to the root node - * @return - * string with list of issue codes, separated with a line break - */ - // TODO (ACF-4153) generalize for all kinds of errors and move to AbstractXtextTest - private String diagnosticsToString(final List diagnostics, final boolean displayPathToTargetObject) { - StringBuilder sb = new StringBuilder(25); - for (Resource.Diagnostic diagnostic : diagnostics) { - if (diagnostic instanceof AbstractDiagnostic) { - AbstractDiagnostic diag = (AbstractDiagnostic) diagnostic; - sb.append(" "); - sb.append(diag.getCode()); - if (displayPathToTargetObject) { - sb.append(" at line: "); - sb.append(diag.getLine()); - sb.append(" on \n"); - sb.append(" "); - sb.append(diag.getUriToProblem()); - } - sb.append(LINE_BREAK); - } - } - return sb.toString(); - } - - @Override - protected void beforeAllTests() { - super.beforeAllTests(); - if (getTestSource() != null) { - Diagnostic primaryDiagnostics = getXtextTestUtil().getDiagnostician().validate(getSemanticModel()); - getTestInformation().putTestObject(Diagnostic.class, primaryDiagnostics); - } - } - - /** - * Register a new validation marker with the given issue code. Expects an info. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String info(final String issueCode) { - return info(issueCode, null); - } - - /** - * Register a new validation marker with the given issue code and message. Expects an info. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @param message - * the expected issue message - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String info(final String issueCode, final String message) { - return addAssertion(new XtextDiagnosticAssertion(issueCode, true, Diagnostic.INFO, message)); - } - - /** - * Register a new validation marker with the given issue code. Expects a warning if the condition is {@code true}, no diagnostic otherwise. - * - * @param condition - * the condition when the marker is expected - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String warningIf(final boolean condition, final String issueCode) { - if (condition) { - return warning(issueCode); - } else { - return noDiagnostic(issueCode); - } - } - - /** - * Register a new validation marker with the given issue code. Expects a warning. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String warning(final String issueCode) { - return warning(issueCode, null); - } - - /** - * Register a new validation marker with the given issue code and message. Expects a warning. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @param message - * the expected issue message - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String warning(final String issueCode, final String message) { - return addAssertion(new XtextDiagnosticAssertion(issueCode, true, Diagnostic.WARNING, message)); - } - - /** - * Register a new validation marker with the given issue code. Expects an error if the condition is {@code true}, no diagnostic otherwise. - * - * @param condition - * the condition when the marker is expected - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String errorIf(final boolean condition, final String issueCode) { - if (condition) { - return error(issueCode); - } else { - return noDiagnostic(issueCode); - } - } - - /** - * Register a new validation marker with the given issue code. Expects an error. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String error(final String issueCode) { - return error(issueCode, null); - } - - /** - * Register a new validation marker with the given issue code and message. Expects an error. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @param message - * the expected issue message - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String error(final String issueCode, final String message) { - return addAssertion(new XtextDiagnosticAssertion(issueCode, true, Diagnostic.ERROR, message)); - } - - /** - * Register a new validation marker with the given issue code. - * The issue is expected to be found in the test file. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String diagnostic(final String issueCode) { - return diagnostic(issueCode, null); - } - - /** - * Register a new validation marker with the given issue code and message. - * The issue and message are expected to be found in the test file. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @param message - * the expected issue message - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String diagnostic(final String issueCode, final String message) { - return addAssertion(new XtextDiagnosticAssertion(issueCode, true, SEVERITY_UNDEFINED, message)); - } - - /** - * Register a new linking error validation marker. - * The issue is expected to be found in the test file. - * - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String linkingError() { - return linkingError(null); - } - - /** - * Register a new linking error validation marker with the given message. - * The issue is expected to be found in the test file. - * - * @param message - * issuethe expected issue message - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String linkingError(final String message) { - return addAssertion(new ResourceDiagnosticAssertion(org.eclipse.xtext.diagnostics.Diagnostic.LINKING_DIAGNOSTIC, true, Diagnostic.ERROR, message)); - } - - /** - * Register a new resource validation marker with the given issue code and message. - * The issue is expected to be found in the test file. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @param message - * the expected issue message - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String resourceDiagnostic(final String issueCode, final String message) { - return addAssertion(new ResourceDiagnosticAssertion(issueCode, true, Diagnostic.ERROR, message)); - } - - /** - * Register a new validation marker with the given issue code. - * The issue is expected NOT to be found in the test file. - * - * @param issueCode - * issue code (usually found as static constant of the JavaValidator class of the DSL being tested) - * @return - * unique marker that can be used in the input string to mark a position that should be validated - */ - protected String noDiagnostic(final String issueCode) { - return addAssertion(new XtextDiagnosticAssertion(issueCode, false)); - } - - @Override - protected void beforeApplyAssertions(final XtextTestSource testSource) { - super.beforeApplyAssertions(testSource); - EObject root = testSource.getModel(); - // Get all diagnostics of the current testing file - EcoreUtil2.resolveLazyCrossReferences(root.eResource(), CancelIndicator.NullImpl); - fileDiagnostics = validate(root); - getUnexpectedDiagnostics().addAll(fileDiagnostics.getChildren()); - getUnexpectedResourceDiagnostics().addAll(root.eResource().getErrors()); - getUnexpectedResourceDiagnostics().addAll(root.eResource().getWarnings()); - } - - @Override - protected String getAdditionalErrorMessageInformation() { - return diagnosticsToString(fileDiagnostics, true); - } - - @Override - protected void afterValidate() { - super.afterValidate(); - // Garbage collection - getUnexpectedDiagnostics().clear(); - getUnexpectedResourceDiagnostics().clear(); - } - - /** - * Assert that diagnosticList contains a diagnostic of the given issueCode. - * - * @param issueCode - * the code of the issue to look for - */ - protected void assertDiagnostic(final String issueCode) { - assertDiagnostic(getPrimaryDiagnostics(), issueCode); - } - - /** - * Assert that the given EObject model contains a diagnostic of the given issueCode. - * - * @param model - * the model in which to look for issues, may be {@code null} - * @param issueCode - * the code of the issue to look for - */ - protected void assertDiagnostic(final EObject model, final String issueCode) { - assertNotNull("Issue with code '" + issueCode + "' cannot be found because the model is null", model); - assertDiagnostic(getXtextTestUtil().getDiagnostician().validate(model), issueCode); - } - - /** - * Assert that diagnosticList does not contain a diagnostic of the given issueCode. - * - * @param issueCode - * the code of the issue to look for - */ - protected void assertNoDiagnostic(final String issueCode) { - assertNoDiagnostic(getPrimaryDiagnostics(), issueCode); - } - - /** - * Assert that the given EObject model does not contain a diagnostic of the given issueCode. - * - * @param model - * the model in which to look for issues, may be {@code null} - * @param issueCode - * the code of the issue to look for - */ - protected void assertNoDiagnostic(final EObject model, final String issueCode) { - assertNotNull("Issue with code '" + issueCode + "' cannot be found because the model is null", model); - assertNoDiagnostic(getXtextTestUtil().getDiagnostician().validate(model), issueCode); - } - - /** - * Assert that diagnosticList does not contain any diagnostic. - */ - protected void assertNoDiagnostics() { - assertNoDiagnostics(getPrimaryDiagnostics()); - } - - /** - * Assert that the given EObject model does not contain any diagnostic. - * - * @param model - * the model in which to look for issues, may be {@code null} - */ - protected void assertNoDiagnostics(final EObject model) { - assertNotNull("Assertion cannot be checked because the model is null", model); - assertNoDiagnostics(getXtextTestUtil().getDiagnostician().validate(model)); - } - - /** - * Assert that diagnosticList contains a diagnostic with the given message. - * - * @param message - * the message of the issue to look for - */ - protected void assertDiagnosticMessage(final String message) { - assertDiagnosticMessage(getPrimaryDiagnostics(), message); - } - - /** - * Assert that the given EObject model contains a diagnostic with the given message. - * - * @param model - * the model in which to look for issues, may be {@code null} - * @param message - * the message of the issue to look for - */ - protected void assertDiagnosticMessage(final EObject model, final String message) { - assertNotNull("Message '" + message + "' cannot be found because the model is null", model); - assertDiagnosticMessage(getXtextTestUtil().getDiagnostician().validate(model), message); - } - - /** - * Assert that diagnosticList contains a diagnostic with the given message. - * - * @param diagnostics - * the diagnostic to check for issues - * @param message - * the message of the issue to look for - */ - private static void assertDiagnosticMessage(final Diagnostic diagnostics, final String message) { - for (Diagnostic diagnostic : diagnostics.getChildren()) { - if (diagnostic.getMessage().equals(message)) { - return; - } - } - fail("Issue with message ' " + message + "' not found"); - } - - /** - * Assert that diagnosticList contains a diagnostic of the given issueCode. - * - * @param diagnostics - * the diagnostic to check for issues - * @param issueCode - * the code of the issue to look for - */ - private void assertDiagnostic(final Diagnostic diagnostics, final String issueCode) { - for (Diagnostic diagnostic : diagnostics.getChildren()) { - if (diagnostic instanceof AbstractValidationDiagnostic && ((AbstractValidationDiagnostic) diagnostic).getIssueCode().equals(issueCode)) { - return; - } - } - fail("Issue with code '" + issueCode + "' not found"); - } - - /** - * Assert that diagnosticList contains a diagnostic of the given issueCode on a given EObject. - * For performance reasons one can validate the root object and afterwards use this method - * to check that a particular diagnostic exists on one of the child objects of the validated model. - * - * @param diagnostics - * the diagnostic to check for issues - * @param targetObject - * the object that should have a diagnostic with the given issueCode - * @param issueCode - * the code of the issue to look for - */ - protected void assertDiagnosticOnObject(final Diagnostic diagnostics, final EObject targetObject, final String issueCode) { - for (Diagnostic diagnostic : diagnostics.getChildren()) { - if (diagnostic instanceof AbstractValidationDiagnostic) { - AbstractValidationDiagnostic avd = (AbstractValidationDiagnostic) diagnostic; - if (avd.getSourceEObject() == targetObject && avd.getIssueCode().equals(issueCode)) { - return; - } - } - } - fail("Issue with code '" + issueCode + "' not found"); - } - - /** - * Assert that diagnosticList does not contain a diagnostic of the given issueCode. - * - * @param diagnostics - * the diagnostic to check for issues - * @param issueCode - * the code of the issue to look for - */ - private void assertNoDiagnostic(final Diagnostic diagnostics, final String issueCode) { - for (Diagnostic diagnostic : diagnostics.getChildren()) { - if (((AbstractValidationDiagnostic) diagnostic).getIssueCode().equals(issueCode)) { - fail("Issue with code '" + issueCode + "' found"); - return; - } - } - } - - /** - * Assert that diagnosticList does not contain any diagnostic. - * - * @param diagnostics - * the diagnostic to check for issues - */ - private void assertNoDiagnostics(final Diagnostic diagnostics) { - assertEquals("Diagnostics should be in OK state.", diagnostics.getCode(), Diagnostic.OK); - assertTrue("There should be no diagnostics. Instead found " + diagnostics.getChildren().size(), diagnostics.getChildren().isEmpty()); - } - - /** - * Assert no errors on resource exist. - * - * @param object - * the object - */ - public static void assertNoErrorsOnResource(final EObject object) { - final EList errors = object.eResource().getErrors(); - if (!errors.isEmpty()) { - fail(AbstractValidationTest.NO_ERRORS_FOUND_ON_RESOURCE_MESSAGE + "; found " + Lists.transform(errors, Resource.Diagnostic::getMessage)); //$NON-NLS-1$ - } - } - - /** - * Assert no errors on resource with the given message exist. - * - * @param object - * the object - * @param messages - * the messages - */ - public static void assertNoErrorsOnResource(final EObject object, final String... messages) { - List messageList = Arrays.asList(messages); - final EList errors = object.eResource().getErrors(); - for (String errorMessage : Lists.transform(errors, Resource.Diagnostic::getMessage)) { - assertFalse(NO_ERRORS_FOUND_ON_RESOURCE_MESSAGE + " with message '" + errorMessage + "'.", messageList.contains(errorMessage)); - } - } - - /** - * Assert no linking errors on resource with the given message exist. - * - * @param object - * the object - * @param referenceType - * the type of the referenced elements - * @param referenceNames - * the names of the referenced elements - */ - @SuppressWarnings("PMD.UnusedFormalParameter") - public static void assertNoLinkingErrorsOnResource(final EObject object, final String referenceType, final String... referenceNames) { - final List linkingErrors = object.eResource().getErrors().stream().filter(error -> error instanceof XtextLinkingDiagnostic).collect(Collectors.toList()); - final List errorMessages = Lists.transform(linkingErrors, Resource.Diagnostic::getMessage); - for (final String referenceName : referenceNames) { - boolean found = false; - for (final String errMessage : errorMessages) { - if (errMessage.startsWith(referenceName)) { - found = true; - break; - } - } - assertFalse(NLS.bind("Expecting no linking errors on resource for \"{0}\".", referenceName), found); - } - } - - /** - * Assert linking errors on resource with the given message exist. - * - * @param object - * the object - * @param referenceType - * the type of the referenced elements - * @param referenceNames - * the names of the referenced elements - */ - @SuppressWarnings("PMD.UnusedFormalParameter") - public static void assertLinkingErrorsOnResourceExist(final EObject object, final String referenceType, final String... referenceNames) { - final List linkingErrors = object.eResource().getErrors().stream().filter(error -> error instanceof XtextLinkingDiagnostic).collect(Collectors.toList()); - final List errorMessages = Lists.transform(linkingErrors, Resource.Diagnostic::getMessage); - for (final String referenceName : referenceNames) { - boolean found = false; - for (final String errMessage : errorMessages) { - if (errMessage.contains(referenceName)) { - found = true; - break; - } - } - assertTrue(NLS.bind("Expected linking error on \"{0}\" but could not find it", referenceName), found); - } - } - - /** - * Expect the given linking error messages on the resource of the given model. - * - * @param object - * the object, must not be {@code null} - * @param errorStrings - * the expected linking error error messages, must not be {@code null} - */ - public static void assertLinkingErrorsWithCustomMessageOnResourceExist(final EObject object, final String... errorStrings) { - final List linkingErrors = object.eResource().getErrors().stream().filter(error -> error instanceof XtextLinkingDiagnostic).collect(Collectors.toList()); - final List errorMessages = Lists.transform(linkingErrors, Resource.Diagnostic::getMessage); - for (final String s : errorStrings) { - assertTrue(NLS.bind("Expected linking error \"{0}\" but could not find it", s), errorMessages.contains(s)); - } - } - - /** - * Assert no linking errors on resource with the given message exist. - * - * @param object - * the object, must not be {@code null} - * @param messages - * the linking error messages, must not be {@code null} - */ - public static void assertNoLinkingErrorsWithCustomMessageOnResource(final EObject object, final String... messages) { - List messageList = Arrays.asList(messages); - final List linkingErrors = object.eResource().getErrors().stream().filter(error -> error instanceof XtextLinkingDiagnostic).collect(Collectors.toList()); - for (String errorMessage : Lists.transform(linkingErrors, Resource.Diagnostic::getMessage)) { - assertFalse(NLS.bind("Expecting no linking errors on resource with message \"{0}\".", errorMessage), messageList.contains(errorMessage)); - } - } - - /** - * Expect given error messages on the resource of given model. - * - * @param object - * the object - * @param errorStrings - * the error strings - */ - public static void assertErrorsOnResourceExist(final EObject object, final String... errorStrings) { - final EList errors = object.eResource().getErrors(); - final List errorMessages = Lists.transform(errors, Resource.Diagnostic::getMessage); - for (final String s : errorStrings) { - assertTrue(NLS.bind("Expected error \"{0}\" but could not find it", s), errorMessages.contains(s)); - } - } - - /** - * Validates if there is a syntax error present in the source content. - * - * @param sourceFileName - * the file name that should be associated with the parsed content, must not be {@code null} - * @param sourceContent - * source, must not be {@code null} - */ - protected void assertNoSyntaxErrorsOnResource(final String sourceFileName, final CharSequence sourceContent) { - final XtextTestSource testSource = createTestSource(sourceFileName, sourceContent.toString()); - final List errors = testSource.getModel().eResource().getErrors().stream().filter(error -> error instanceof XtextSyntaxDiagnostic).collect(Collectors.toList()); - if (!errors.isEmpty()) { - StringBuilder sb = new StringBuilder("Syntax error is present in the test source.\nList of all found syntax errors:"); - errors.forEach(err -> sb.append("\n\t ").append(err.getMessage())); - throw new AssertionError(sb.toString()); - } - } - - /** - * Memorize the position and issue code of each resource error that appears in the file. - * - * @param root - * root node of the model to be analyzed - */ - protected void memorizeUnexpectedResourceErrors() { - for (Resource.Diagnostic diagnostic : getUnexpectedResourceDiagnostics()) { - if (diagnostic instanceof AbstractDiagnostic) { - AbstractDiagnostic diag = (AbstractDiagnostic) diagnostic; - // Create error message - StringBuilder sb = new StringBuilder(50); - sb.append("Unexpected diagnostic found. Code '"); - sb.append(diag.getCode()); - sb.append(DOT_AND_LINEBREAK); - // Retrieve the position and add the error - memorizeErrorOnPosition(diag.getOffset(), sb.toString()); - } else { - // Create error message - StringBuilder sb = new StringBuilder(50); - sb.append("Unexpected diagnostic found. '"); - sb.append(diagnostic.toString()); - sb.append(DOT_AND_LINEBREAK); - // Add error message - memorizeErrorOnPosition(0, sb.toString()); - } - } - } - - /** - * Memorize the position and issue code of each unexpected diagnostic that appears in the file. - * A diagnostic is considered as expected if a marker with the issue code in the test file was set. - */ - protected void memorizeUnexpectedErrors() { - for (Diagnostic diagnostic : getUnexpectedDiagnostics()) { - if (diagnostic instanceof AbstractValidationDiagnostic) { - AbstractValidationDiagnostic avd = (AbstractValidationDiagnostic) diagnostic; - // Create error message - StringBuilder sb = new StringBuilder(50); - sb.append("Unexpected issue found. Code '"); - sb.append(avd.getIssueCode()); - sb.append(DOT_AND_LINEBREAK); - // Retrieve the position and add the error - if (avd instanceof FeatureBasedDiagnostic && ((FeatureBasedDiagnostic) avd).getFeature() != null) { - List nodes = NodeModelUtils.findNodesForFeature(avd.getSourceEObject(), ((FeatureBasedDiagnostic) avd).getFeature()); - if (nodes.isEmpty()) { - INode node = NodeModelUtils.getNode(avd.getSourceEObject()); - memorizeErrorOnPosition(getXtextTestUtil().findFirstNonHiddenLeafNode(node).getTotalOffset(), sb.toString()); - } else { - for (INode node : nodes) { - memorizeErrorOnPosition(getXtextTestUtil().findFirstNonHiddenLeafNode(node).getTotalOffset(), sb.toString()); - } - } - } else if (avd instanceof RangeBasedDiagnostic) { - memorizeErrorOnPosition(((RangeBasedDiagnostic) avd).getOffset(), sb.toString()); - } else { - memorizeErrorOnPosition(NodeModelUtils.getNode(avd.getSourceEObject()).getTotalOffset(), sb.toString()); - } - } else { - // Create error message - StringBuilder sb = new StringBuilder(50); - sb.append("Unexpected diagnostic found. '"); - sb.append(diagnostic.toString()); - sb.append(DOT_AND_LINEBREAK); - // Add error message - memorizeErrorOnPosition(0, sb.toString()); - } - } - } - - /** - * Strictly validates a source given by a file name and content. - * - * @param sourceFileName - * the file name that should be associated with the parsed content, must not be {@code null} - * @param sourceType - * defines if the source is a kernel or customer source, must not be {@code null} - * @param sourceContent - * source, must not be {@code null} - */ - protected void validateStrictly(final String sourceFileName, final TestSourceType sourceType, final CharSequence sourceContent) { - XtextTestSource testSource = processMarkers(sourceFileName, sourceType, sourceContent); - memorizeUnexpectedErrors(); - memorizeUnexpectedResourceErrors(); - processErrorsFound(testSource.getContent()); - afterValidate(); - } - - /** - * Strictly validate a kernel source given by a {@link Pair} of file name and content. - * All not expected diagnostics are considered as an error. - * - * @param sourceFileNameAndContent - * the file name and content, given as the key and value of the pair, respectively, must not be {@code null} - */ - protected void validateKernelSourceStrictly(final Pair sourceFileNameAndContent) { - validateKernelSourceStrictly(sourceFileNameAndContent.getKey(), sourceFileNameAndContent.getValue()); - } - - /** - * Strictly validate a customer source given by a {@link Pair} of file name and content. - * All not expected diagnostics are considered as an error. - * - * @param sourceFileNameAndContent - * the file name and content, given as the key and value of the pair, respectively, must not be {@code null} - */ - protected void validateCustomerSourceStrictly(final Pair sourceFileNameAndContent) { - validateCustomerSourceStrictly(sourceFileNameAndContent.getKey(), sourceFileNameAndContent.getValue()); - } - - /** - * Strictly validate a kernel source given by a file name and content. - * All not expected diagnostics are considered as an error. - * - * @param sourceFileName - * the file name that should be associated with the parsed content, must not be {@code null} - * @param sourceContent - * source, must not be {@code null} - */ - protected void validateKernelSourceStrictly(final String sourceFileName, final CharSequence sourceContent) { - validateStrictly(sourceFileName, TestSourceType.CLIENT_ALL, sourceContent); - } - - /** - * Strictly validate a customer source given by a file name and content. - * All not expected diagnostics are considered as an error. - * - * @param sourceFileName - * the file name that should be associated with the parsed content, must not be {@code null} - * @param sourceContent - * source, must not be {@code null} - */ - protected void validateCustomerSourceStrictly(final String sourceFileName, final CharSequence sourceContent) { - validateStrictly(sourceFileName, TestSourceType.CLIENT_CUSTOMER, sourceContent); - } -} diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/validation/ValidationHelper.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/validation/ValidationHelper.java index 3acda5d55..3000474bb 100644 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/validation/ValidationHelper.java +++ b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/validation/ValidationHelper.java @@ -10,20 +10,28 @@ *******************************************************************************/ package com.avaloq.tools.ddk.xtext.test.validation; +import static org.junit.jupiter.api.Assertions.fail; + import java.util.List; +import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtext.testing.validation.ValidationTestHelper; import org.eclipse.xtext.ui.editor.model.IXtextDocument; import org.eclipse.xtext.validation.CheckMode; import org.eclipse.xtext.validation.Issue; +import com.google.common.collect.Lists; + /** * Helper methods relating to model validation. */ public class ValidationHelper extends ValidationTestHelper { + private static final String NO_ERRORS_FOUND_ON_RESOURCE_MESSAGE = "Expected no errors on resource"; //$NON-NLS-1$ + /** * Validates the provided document and returns a list of issues found. * @@ -42,7 +50,10 @@ public List getIssues(final IXtextDocument document) { * the object to test for errors */ public void assertNoSyntaxOrLinkingErrors(final EObject obj) { - AbstractValidationTest.assertNoErrorsOnResource(obj); + final EList errors = obj.eResource().getErrors(); + if (!errors.isEmpty()) { + fail(NO_ERRORS_FOUND_ON_RESOURCE_MESSAGE + "; found " + Lists.transform(errors, Resource.Diagnostic::getMessage)); //$NON-NLS-1$ + } } } diff --git a/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/AbstractFormatterTest.java b/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/AbstractFormatterTest.java deleted file mode 100644 index e1e12db5b..000000000 --- a/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/AbstractFormatterTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.formatter; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.formatting.INodeModelFormatter; -import org.eclipse.xtext.formatting.INodeModelFormatter.IFormattedRegion; -import org.eclipse.xtext.nodemodel.ICompositeNode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; -import org.eclipse.xtext.resource.SaveOptions; -import org.eclipse.xtext.serializer.ISerializer; - -import com.avaloq.tools.ddk.xtext.test.AbstractXtextTest; - - -/** - * Base class for formatting tests. - * The assertXyz methods are essentially copied from {@link com.avaloq.tools.ddk.xtext.test.formatting.AbstractAcfFormattingTest}. - */ -@SuppressWarnings("nls") -public abstract class AbstractFormatterTest extends AbstractXtextTest { - - /** - * Loads a model from a string representation of a source. - * - * @param input - * String representing a serialized model - * @return Loaded model - */ - private EObject getModel(final String input) throws IOException { - return getXtextTestUtil().getModel("mytestmodel." + getXtextTestUtil().getFileExtension(), input); - } - - /** - * Gets the Guice injected serializer. - * - * @return Serializer the DI serializer - */ - protected ISerializer getSerializer() { - return getXtextTestUtil().getSerializer(); - } - - // test formatting based on the ParseTreeConstructorin - protected void assertFormattedPTC(final String expected, final String model) throws IOException { - EObject m = getModel(model); - String res = getSerializer().serialize(m, SaveOptions.newBuilder().format().getOptions()); - assertEquals(expected, res, "Serialization not equal"); - } - - protected void assertPreserved(final String model) throws IOException { - EObject m = getModel(model); - String res = getSerializer().serialize(m, SaveOptions.newBuilder().getOptions()); - assertEquals(model, res, "Preserved node model"); - } - - // test formatting based on the NodeModel - protected void assertFormattedNM(final String expected, final String model, final int offset, final int length) throws IOException { - ICompositeNode node = NodeModelUtils.getNode(getModel(model)).getRootNode(); - IFormattedRegion r = getXtextTestUtil().get(INodeModelFormatter.class).format(node, offset, length); - String actual = model.substring(0, r.getOffset()) + r.getFormattedText() + model.substring(r.getLength() + r.getOffset()); - assertEquals(expected, actual, "Formatting based on the NodeModel"); - } - -} diff --git a/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/FormatterTest.java b/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/FormatterTest.java deleted file mode 100644 index b8e7b1041..000000000 --- a/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/FormatterTest.java +++ /dev/null @@ -1,507 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.formatter; - -import java.io.IOException; - -import org.eclipse.xtext.resource.SaveOptions; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import com.avaloq.tools.ddk.xtext.formatter.formatterTestLanguage.Decl; -import com.avaloq.tools.ddk.xtext.formatter.formatterTestLanguage.FormatterTestLanguageFactory; -import com.avaloq.tools.ddk.xtext.formatter.formatterTestLanguage.TestLinewrapMinMax; -import com.avaloq.tools.ddk.xtext.formatter.util.FormatterTestUtil; - - -/** - * This class tests the Acs Formatter framework. The tests are basically a copy - * of the Xtext Formatter tests. - */ -@SuppressWarnings("nls") -public class FormatterTest extends AbstractFormatterTest { - @Override - protected FormatterTestUtil getXtextTestUtil() { - return FormatterTestUtil.getInstance(); - } - - /** - * This test class does not have a test source file. {@inheritDoc} - */ - @Override - protected String getTestSourceFileName() { - return null; - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrap() throws IOException { - String model = "test linewrap float val; int x; double y;"; - String expected = "test linewrap\nfloat val;\nint x;\ndouble y;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void keepComments() throws IOException { - // String model = "test linewrap float val; int x; double y;"; - String model = "// begincomment \ntest linewrap// comment1\n" + "float val;//comment2\n" + "int x;" + "double y; //yoyoyo!\n// endcomment."; - final String exp = "// begincomment \ntest linewrap// comment1\n" + "float val;//comment2\n" + "int x;\n" + "double y; //yoyoyo!\n// endcomment."; - assertFormattedPTC(exp, model); - assertFormattedNM(exp, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test verifies that {@link com.avaloq.tools.ddk.xtext.formatter.formatterTestLanguage.Line} elements are aligned at the specified column. - * - * @throws IOException - */ - @Test - public void column() throws IOException { - String model = "test column item int x;"; - String expected = "test\n column\n\titem int x;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test verifies that elements can be aligned at column 0 and also that - * a minimum padding is always inserted. - * - * @throws IOException - */ - @Test - public void columnMinimumPadding() throws IOException { - String model = " test column name item int x;"; - String expected = "test\n column name\n\n\titem int x;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model.trim()); - } - - /** - * This test verifies that {@link com.avaloq.tools.ddk.xtext.formatter.formatterTestLanguage.Line} elements are aligned with the specified offset. - * - * @throws IOException - */ - @Test - public void offset() throws IOException { - String model = "test offset value v pair p1 p2"; - String expected = "test\noffset\n\tvalue v\n\t\tpair p1 p2"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test verifies right padding does pad and that there is always minimum padding " ". - * - * @throws IOException - */ - @Test - public void rightPadding() throws IOException { - String model = "test padding long_name n2;"; - String expected = "test\npadding long_name n2 ;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void indentation() throws IOException { - String model = "test indentation { float val; double y; indentation { int x; } }"; - String expected = "test indentation {\n float val;\n double y;\n indentation {\n int x;\n }\n}"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void association() throws IOException { - String model = "test indentation { var = [0,1,2,3,4]; }"; - String expected = "test indentation {\n var=[ 0, 1, 2, 3, 4 ];\n}"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void indentationAndComments() throws IOException { - String model = "test /* xxx */ indentation { float val; // some float\n double /* oo */ y; indentation { // some block\n int x; // xxx\n } } // final comment"; - String expected = "test /* xxx */ indentation {\n float val; // some float\n double /* oo */ y;\n indentation { // some block\n int x; // xxx\n }\n} // final comment"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * It has been adapted to - * - * @throws IOException - */ - @Test - public void indentationAndLineWrap() throws IOException { - String model = "test indentation { void func(x:int,y:int,s:javalangString, foo:javasqlDate, blupp:mylongtype, msads:adshdjkhsakdasdkslajdlsask, x:x, a:b, c:d ); }"; - String expected = "test indentation {\n void func(x:int,y:int,\n\t\ts:javalangString,\n\t\tfoo:javasqlDate,\n\t\tblupp:mylongtype,\n\t\tmsads:adshdjkhsakdasdkslajdlsask,\n\t\tx:x,a:b,c:d);\n}"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void between1() throws IOException { - String model = "test indentation { indentation { x x; }; }"; - String expected = "test indentation {\n indentation {\n x x;\n };\n}"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void between2() throws IOException { - String model = "test indentation { indentation { x x; } }"; - String expected = "test indentation {\n indentation {\n x x;\n }\n}"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapDatatypeRule() throws IOException { - String model = "test linewrap fqn ab; fqn xx.yy.zz;"; - String expected = "test linewrap\nfqn\nab;\nfqn\nxx.yy.zz;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapDatatypeRulePartial1() throws IOException { - String model = "test linewrap fqn ab . xx .yy .zz;"; - String expected = "test linewrap fqn ab.xx.yy.zz;"; - assertFormattedNM(expected, model, 22, 2); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapDatatypeRulePartial2() throws IOException { - String model = "test linewrap fqn ab . xx .yy .zz;fqn xxx;"; - String expected = "test linewrap fqn\nab.xx.yy.zz;fqn xxx;"; - assertFormattedNM(expected, model, 15, 10); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapDatatypeRulePartial3() throws IOException { - String model = "test linewrap fqn ab . xx .yy .zz;fqn xxx;"; - String expected = "test linewrap fqn ab.xx.yy.zz;\nfqn xxx;"; - assertFormattedNM(expected, model, 25, 12); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void formatSegment1() throws IOException { - String model = "test\nindentation {\n indentation { x x ; } }"; - String expected = "test\nindentation {\n indentation {\n x x;\n } }"; - assertFormattedNM(expected, model, 30, 18); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void formatSegment2() throws IOException { - String model = "test indentation {\n indentation { x x ; } }"; - // String expected = - // "test\nindentation {\n indentation {\n x x;\n } }"; - assertFormattedNM(model, model, 7, 10); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void formatSegment3() throws IOException { - String model = " test indentation {\n indentation { x x ; } }"; - String expected = "test indentation {\n indentation {\n x x;\n }\n}"; - assertFormattedNM(expected, model, 0, model.length()); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapDatatypeRuleRef1() throws IOException { - String model = "test linewrap fqn ab .cd .ef; fqnref ab. cd. ef;"; - String expected = "test linewrap\nfqn\nab.cd.ef;\nfqnref\nab.cd.ef;"; - // assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapDatatypeRuleRef2() throws IOException { - String model = "test linewrap fqn ab.cd.ef; fqnref ab.cd.ef;"; - String expected = "test linewrap\nfqn\nab.cd.ef;\nfqnref\nab.cd.ef;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest but it is modified - * because I think the expected behavior in the original test is wrong. - * - * @throws IOException - */ - @Test - public void linewrapDatatypeRuleComments() throws IOException { - String model = "test linewrap/* 1 */fqn/* 2 */ab.cd.ef/* 3 */;/* 4 */fqnref/* 5 */ab.cd.ef/* 6 */;/* 7 */"; - // The expected model string differs from Xtext's - - // Xtext does not expect a line wrap after the keyword "linewrap" - // Xtext does not expect a line wrap prior to fqnref assignment - // etc... - String expected = "test linewrap/* 1 */ fqn/* 2 */\nab.cd.ef/* 3 */;/* 4 */ fqnref\n/* 5 */ ab.cd.ef/* 6 */;/* 7 */"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void enumeration() throws IOException { - String model = "test linewrap enum lit1,lit2,lit3,lit1;"; - String expected = "test linewrap\nenum lit1 ,\nlit2,\nlit3,\nlit1;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=312559 - @Test - public void suppressedWhitespace() throws IOException { - String model = "test linewrap `f%%a` post;"; - String expected = "test linewrap\n`f%< b >%a` post;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - // TODO: investigate whether to include test or not - currently this test - // would fail - @Disabled - public void suppressedLinewrap() throws IOException { - String model = "test linewrap\n`foo%abcd%foo%< b\n>%abcd%foo%abcd%foo%abcd%" + "foo%abcd%foo%abcd%foo%abcd%foo%abcd%foo%abcd%foo%xx%foo%abcd%foo%abcd%" - + "foo%abcd%foo%<\nb >%foo%abcd` post;"; - assertFormattedPTC(model, model); - assertFormattedNM(model, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapMin() throws IOException { - String model = "test wrapminmax foo bar;"; - String expected = "test wrapminmax\n\nfoo bar;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapMax() throws IOException { - String model = "test wrapminmax\n\n\n\n\n\n\n\n\n\n\n\n\nfoo bar;"; - String expected = "test wrapminmax\n\n\n\n\nfoo bar;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapKeep() throws IOException { - String model = "test wrapminmax\n\n\n\nfoo bar;"; - assertFormattedPTC(model, model); - assertFormattedNM(model, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void linewrapDefault() { - FormatterTestLanguageFactory f = FormatterTestLanguageFactory.eINSTANCE; - TestLinewrapMinMax m = f.createTestLinewrapMinMax(); - Decl d = f.createDecl(); - d.getType().add("xxx"); - d.getName().add("yyy"); - m.getItems().add(d); - String actual = getSerializer().serialize(m, SaveOptions.newBuilder().format().getOptions()); - String expected = "test wrapminmax\n\n\nxxx yyy;"; - Assertions.assertEquals(expected, actual, "Default Linewrap"); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void space() throws IOException { - String model = "test linewrap space foo;"; - String expected = "test linewrap\nspace foo;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } - - /** - * This test is copied from - * org.eclipse.xtext.nodemodel.impl.formatter.FormatterTest. - * - * @throws IOException - */ - @Test - public void datatypeRules() throws IOException { - String model = "test linewrap datatypes abc kw1 bcd def kw3;"; - String expected = "test linewrap\ndatatypes abc\nkw1\nbcd\ndef\nkw3;"; - assertFormattedPTC(expected, model); - assertFormattedNM(expected, model, 0, model.length()); - assertPreserved(model); - } -} diff --git a/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/util/FormatterTestUtil.java b/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/util/FormatterTestUtil.java deleted file mode 100644 index 02b6937a8..000000000 --- a/com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/util/FormatterTestUtil.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG 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: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.formatter.util; - -import com.avaloq.tools.ddk.xtext.formatter.FormatterTestLanguageStandaloneSetup; -import com.avaloq.tools.ddk.xtext.test.AbstractXtextTestUtil; -import com.google.inject.Injector; - - -public final class FormatterTestUtil extends AbstractXtextTestUtil { - private final Injector injector = new FormatterTestLanguageStandaloneSetup().createInjectorAndDoEMFRegistration(); - - private FormatterTestUtil() { - // private constructor - } - - /** - * The singleton instance. - */ - private static final class InstanceHolder { - // Initialize-on-demand holder pattern. - private static final FormatterTestUtil INSTANCE = new FormatterTestUtil(); - - static FormatterTestUtil get() { - return INSTANCE; - } - } - - public static FormatterTestUtil getInstance() { - return InstanceHolder.get(); - } - - @Override - protected Injector getInjector() { - return injector; - } - -}