From 8c2cbb7ba42312b3897b5b1bed29f5862ba83d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Wed, 6 May 2026 00:44:27 +0200 Subject: [PATCH 1/2] test: restore 5 silently-dropped tests The Oct 2025 JUnit 5 migration silently dropped 5 test classes from their suites (50 @Test methods total): CheckCoreTestSuite was missing: CheckValidationTest (30 @Test) CheckJavaValidatorUtilTest (13 @Test) CheckApiAccessValidationsTest (3 @Test) ExportTestSuite was missing: ExportValidationOkTest (1 @Test) GeneratorTestSuite was missing: XbaseGeneratorFragmentTest (3 @Test) The CheckCoreTestSuite drops trace to a single mistake in d5328e1b3: the migration also changed `public class CheckJavaValidatorUtilTest extends TestCase` -> `class CheckJavaValidatorUtilTest`, making the class package-private. That broke the cross-package import in the suite, and rather than restore visibility the import was deleted -- silently dropping the other two CheckCoreTestSuite tests as collateral. ExportValidationOkTest was added to xtext.export.test in d08ea194a but never included in any suite at any point. XbaseGeneratorFragmentTest was orphaned much earlier (Feb 2022, by 45aed05d7 + e59c902be) during a legacy-generator cleanup. Restoring it now requires updating its mocks to track Xtext API drift: XbaseUsageDetector now navigates `rule.eResource().getResourceSet().getEObject(URI, true)` and compares via EcoreUtil2.isAssignableFrom (was a package-name comparison). The mock setup is updated so the resource-set lookup yields a shared mock EClass that matching rules also return -- reference equality preserves the original test intent. Discovered by computing @SelectClasses transitive closure from AllTests and diffing against all classes containing @Test methods. Verified locally: 307 -> 357 tests run (+50), 0 new errors or failures. The 5 restored classes appear in surefire reports for the first time since the migration. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../check/test/core/CheckCoreTestSuite.java | 6 ++++ .../CheckJavaValidatorUtilTest.java | 2 +- .../xtext/test/export/ExportTestSuite.java | 3 +- .../test/generator/GeneratorTestSuite.java | 4 ++- .../test/XbaseGeneratorFragmentTest.xtend | 29 +++++++++++++++++++ 5 files changed, 41 insertions(+), 3 deletions(-) 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.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) } /** From ecbd49a2c80653468399fe41fe4da69b5715970d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Wed, 6 May 2026 00:44:28 +0200 Subject: [PATCH 2/2] chore: delete dead JUnit 4 test infrastructure (49 files / ~9.9k lines) The Oct 2025 JUnit 5 migration left two parallel test-infrastructure trees in place: the old JUnit 4 base classes / runners / filters in the original packages, and the new Jupiter-based equivalents in .../jupiter/ subpackages. Test classes have been migrated to the jupiter versions for some time (every external import resolves to a jupiter package), so the old tree is unreachable from any live test. Delete the dead tree in one cohesive cut. One file (ValidationHelper) is migrated rather than deleted because jupiter/AbstractXtextTestUtil still uses it -- its dependency on AbstractValidationTest.assertNoErrorsOnResource is inlined and switched to org.junit.jupiter.api.Assertions.fail. Removed - JUnit 4 runner/filter subsystem (test.core/junit/runners/): ClassRunner, DiscerningSuite, DynamicSuite, FilterRegistry, NegatedFilter, SorterUtil + 14 *MethodFilter / *ClassFilter / *Filter classes: DebugClassFilter, DebugMethodFilter, FixedIssueFilter, FlakyMethodFilter, IntegrationTestMethodFilter, ModuleTestMethodFilter, NameMethodFilter, PerformanceTestMethodFilter, SwtBotClassFilter, SystemTestMethodFilter, TestLabelFilter (junit/runners/ duplicate), UnitTestMethodFilter, UnresolvedBugMethodFilter, UnresolvedIssueFilter Removed - recording-runner support (test.ui, xtext.test.core): SwtBotRecordingTestRunner, TestRunRecording, SwtBotRecordingXtextTestRunner, XtextClassRunner Removed - non-jupiter rules / helpers in test.core (rule logic now lives in test.core.jupiter equivalents): AbstractSystemTest, BugTestAwareRule, IssueAwareRule, LoggingRule, StatementFactory, TestLabelFilter, TestPlan Removed - non-jupiter base test classes in xtext.test.core (replaced by .../xtext/test/jupiter/ siblings): AbstractTest, AbstractXtextTest, AbstractXtextTestUtil, AbstractXtextMarkerBasedTest, AbstractFormattingTest, AbstractGeneratorTest, AbstractAcfContentAssistTest, AbstractScopingTest, AbstractValidationTest, AbstractLinkingTest, AbstractValueConverterServiceTest, AbstractModelInferrerTest, AbstractJvmModelInferrerTest, AbstractResourceDescriptionManagerTest Removed - duplicated/dead test classes in xtext.test: AbstractFormatterTest, FormatterTest (orphaned non-jupiter duplicate; live twin xtext.jupiter.formatter.FormatterTest is in XtextTestSuite), FormatterTestUtil Migrated - ValidationHelper: imports org.junit.jupiter.api.Assertions.fail; assertNoSyntaxOrLinkingErrors inlines the resource-error check that previously delegated to the deleted AbstractValidationTest.assertNoErrorsOnResource. Verified locally: 357 tests run (unchanged from the restore commit), 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ddk/test/core/AbstractSystemTest.java | 342 ----- .../tools/ddk/test/core/BugTestAwareRule.java | 89 -- .../tools/ddk/test/core/IssueAwareRule.java | 88 -- .../tools/ddk/test/core/LoggingRule.java | 91 -- .../tools/ddk/test/core/StatementFactory.java | 61 - .../tools/ddk/test/core/TestLabelFilter.java | 39 - .../avaloq/tools/ddk/test/core/TestPlan.java | 408 ------ .../test/core/junit/runners/ClassRunner.java | 351 ----- .../core/junit/runners/DebugClassFilter.java | 34 - .../core/junit/runners/DebugMethodFilter.java | 34 - .../core/junit/runners/DiscerningSuite.java | 142 -- .../test/core/junit/runners/DynamicSuite.java | 93 -- .../core/junit/runners/FilterRegistry.java | 265 ---- .../core/junit/runners/FixedIssueFilter.java | 58 - .../core/junit/runners/FlakyMethodFilter.java | 34 - .../runners/IntegrationTestMethodFilter.java | 34 - .../junit/runners/ModuleTestMethodFilter.java | 34 - .../core/junit/runners/NameMethodFilter.java | 54 - .../core/junit/runners/NegatedFilter.java | 44 - .../runners/PerformanceTestMethodFilter.java | 34 - .../test/core/junit/runners/SorterUtil.java | 74 - .../core/junit/runners/SwtBotClassFilter.java | 40 - .../junit/runners/SystemTestMethodFilter.java | 34 - .../core/junit/runners/TestLabelFilter.java | 41 - .../junit/runners/UnitTestMethodFilter.java | 34 - .../runners/UnresolvedBugMethodFilter.java | 35 - .../junit/runners/UnresolvedIssueFilter.java | 58 - .../runners/SwtBotRecordingTestRunner.java | 128 -- .../ui/junit/runners/TestRunRecording.java | 726 ---------- .../tools/ddk/xtext/test/AbstractTest.java | 462 ------- .../test/AbstractXtextMarkerBasedTest.java | 656 --------- .../ddk/xtext/test/AbstractXtextTest.java | 111 -- .../ddk/xtext/test/AbstractXtextTestUtil.java | 367 ----- .../AbstractAcfContentAssistTest.java | 357 ----- .../AbstractValueConverterServiceTest.java | 281 ---- .../formatting/AbstractFormattingTest.java | 173 --- .../test/generator/AbstractGeneratorTest.java | 423 ------ .../SwtBotRecordingXtextTestRunner.java | 128 -- .../test/junit/runners/XtextClassRunner.java | 353 ----- .../AbstractJvmModelInferrerTest.java | 45 - .../test/linking/AbstractLinkingTest.java | 39 - .../AbstractModelInferrerTest.java | 139 -- ...stractResourceDescriptionManagerTest.xtend | 198 --- .../test/scoping/AbstractScopingTest.java | 877 ------------ .../validation/AbstractValidationTest.java | 1221 ----------------- .../test/validation/ValidationHelper.java | 13 +- .../formatter/AbstractFormatterTest.java | 76 - .../ddk/xtext/formatter/FormatterTest.java | 507 ------- .../formatter/util/FormatterTestUtil.java | 46 - 49 files changed, 12 insertions(+), 9959 deletions(-) delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/AbstractSystemTest.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/BugTestAwareRule.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/IssueAwareRule.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/LoggingRule.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/StatementFactory.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/TestLabelFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/TestPlan.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/ClassRunner.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DebugClassFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DebugMethodFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DiscerningSuite.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/DynamicSuite.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FilterRegistry.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FixedIssueFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/FlakyMethodFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/IntegrationTestMethodFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/ModuleTestMethodFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/NameMethodFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/NegatedFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/PerformanceTestMethodFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SorterUtil.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SwtBotClassFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/SystemTestMethodFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/TestLabelFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnitTestMethodFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnresolvedBugMethodFilter.java delete mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/junit/runners/UnresolvedIssueFilter.java delete mode 100644 com.avaloq.tools.ddk.test.ui/src/com/avaloq/tools/ddk/test/ui/junit/runners/SwtBotRecordingTestRunner.java delete mode 100644 com.avaloq.tools.ddk.test.ui/src/com/avaloq/tools/ddk/test/ui/junit/runners/TestRunRecording.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextMarkerBasedTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/AbstractXtextTestUtil.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/contentassist/AbstractAcfContentAssistTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/conversion/AbstractValueConverterServiceTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/formatting/AbstractFormattingTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/generator/AbstractGeneratorTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/junit/runners/SwtBotRecordingXtextTestRunner.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/junit/runners/XtextClassRunner.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jvmmodel/AbstractJvmModelInferrerTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/linking/AbstractLinkingTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/modelinference/AbstractModelInferrerTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/resource/AbstractResourceDescriptionManagerTest.xtend delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/scoping/AbstractScopingTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/validation/AbstractValidationTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/AbstractFormatterTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/FormatterTest.java delete mode 100644 com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/formatter/util/FormatterTestUtil.java 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.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; - } - -}