From c0dd0ad4fb1817d3e0048cdfee9d5f8f69393a8d Mon Sep 17 00:00:00 2001
From: U004458
Date: Thu, 6 Nov 2025 16:09:02 +0800
Subject: [PATCH] feat: Migrate com.avaloq.tools.ddk.xtext.generator.test to
Junit5
- completely remove junit4 dependencies, hence removing the need for
dependency in junit-vintage-engine
- create AbstractTest and AbstractXtextTest that does not need junit4
runners
- create junit5 extensions for LoggingRule, BugTestAwareRule, and
IssueAwareRule to move away from the junit4 @Rule annotation and use
@RegisterExtension instead
---
.../META-INF/MANIFEST.MF | 4 +-
.../test/core/jupiter/BugTestAwareRule.java | 100 ++++
.../ddk/test/core/jupiter/IssueAwareRule.java | 99 ++++
.../ddk/test/core/jupiter/LoggingRule.java | 93 ++++
.../META-INF/MANIFEST.MF | 5 +-
.../expression/CodeGenerationXTest.java | 40 +-
.../expression/CompilationContextTest.java | 24 +-
.../expression/ExpressionsExtentionsTest.java | 11 +-
.../test/generator/GeneratorTestSuite.java | 19 +-
.../test/util/EClassComparatorTest.java | 10 +-
.../xtext/generator/test/util/GraphTest.java | 10 +-
.../test/XbaseGeneratorFragmentTest.xtend | 27 +-
.../META-INF/MANIFEST.MF | 4 +-
.../ddk/xtext/test/AbstractTestUtil.java | 6 +-
.../xtext/test/IllegalJUnitAnnotation.java | 4 +-
.../ddk/xtext/test/jupiter/AbstractTest.java | 440 ++++++++++++++++++
.../xtext/test/jupiter/AbstractXtextTest.java | 114 +++++
17 files changed, 934 insertions(+), 76 deletions(-)
create mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/BugTestAwareRule.java
create mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/IssueAwareRule.java
create mode 100644 com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/LoggingRule.java
create mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jupiter/AbstractTest.java
create mode 100644 com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jupiter/AbstractXtextTest.java
diff --git a/com.avaloq.tools.ddk.test.core/META-INF/MANIFEST.MF b/com.avaloq.tools.ddk.test.core/META-INF/MANIFEST.MF
index ea792c11de..e3fbd6fe75 100644
--- a/com.avaloq.tools.ddk.test.core/META-INF/MANIFEST.MF
+++ b/com.avaloq.tools.ddk.test.core/META-INF/MANIFEST.MF
@@ -14,10 +14,12 @@ Require-Bundle: org.eclipse.core.runtime,
com.google.guava,
org.apache.commons.lang,
org.eclipse.emf.common,
- com.avaloq.tools.ddk
+ com.avaloq.tools.ddk,
+ junit-jupiter-api
Export-Package: com.avaloq.tools.ddk.test.core,
com.avaloq.tools.ddk.test.core.data,
com.avaloq.tools.ddk.test.core.junit.runners,
+ com.avaloq.tools.ddk.test.core.jupiter,
com.avaloq.tools.ddk.test.core.mock,
com.avaloq.tools.ddk.test.core.util
Import-Package: org.apache.logging.log4j
diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/BugTestAwareRule.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/BugTestAwareRule.java
new file mode 100644
index 0000000000..cacdde4aa2
--- /dev/null
+++ b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/BugTestAwareRule.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2025 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.jupiter;
+
+import java.lang.reflect.Method;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.InvocationInterceptor;
+import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
+
+import com.avaloq.tools.ddk.test.core.BugTest;
+
+
+/**
+ * This {@link InvocationInterceptor} 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 InvocationInterceptor {
+
+ 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;
+ }
+ }
+
+ @SuppressWarnings("nls")
+ @Override
+ public void interceptTestMethod(final Invocation invocation, final ReflectiveInvocationContext invocationContext, final ExtensionContext extensionContext) throws Throwable {
+ BugTest bugTestAnnotation = extensionContext.getRequiredTestMethod().getAnnotation(BugTest.class);
+ if (bugTestAnnotation == null && extensionContext.getRequiredTestClass() != null) {
+ bugTestAnnotation = extensionContext.getRequiredTestClass().getAnnotation(BugTest.class);
+ }
+ if (bugTestAnnotation != null && bugTestAnnotation.unresolved()) {
+ try {
+ invocation.proceed();
+ } catch (AssertionError exception) {
+ return;
+ }
+ String testCase = extensionContext.getRequiredTestClass().getSimpleName() + "." + extensionContext.getRequiredTestMethod().getName();
+ throw new AssertionError(ERROR_TEST_MUST_FAIL + " " + testCase);
+ } else {
+ invocation.proceed();
+ }
+ }
+
+}
diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/IssueAwareRule.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/IssueAwareRule.java
new file mode 100644
index 0000000000..2dfe5c3155
--- /dev/null
+++ b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/IssueAwareRule.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2025 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.jupiter;
+
+import java.lang.reflect.Method;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.InvocationInterceptor;
+import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
+
+import com.avaloq.tools.ddk.test.core.Issue;
+
+
+/**
+ * This {@link InvocationInterceptor} 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 InvocationInterceptor {
+
+ 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;
+ }
+ }
+
+ @SuppressWarnings("nls")
+ @Override
+ public void interceptTestMethod(final Invocation invocation, final ReflectiveInvocationContext invocationContext, final ExtensionContext extensionContext) throws Throwable {
+ Issue issueAnnotation = extensionContext.getRequiredTestMethod().getAnnotation(Issue.class);
+ if (issueAnnotation == null && extensionContext.getRequiredTestClass() != null) {
+ issueAnnotation = extensionContext.getRequiredTestClass().getAnnotation(Issue.class);
+ }
+ if (issueAnnotation != null && issueAnnotation.fixed()) {
+ try {
+ invocation.proceed();
+ } catch (AssertionError exception) {
+ return;
+ }
+ String testCase = extensionContext.getRequiredTestClass().getSimpleName() + "." + extensionContext.getRequiredTestMethod().getName();
+ throw new AssertionError(ERROR_TEST_MUST_FAIL + " " + testCase);
+ } else {
+ invocation.proceed();
+ }
+ }
+}
diff --git a/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/LoggingRule.java b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/LoggingRule.java
new file mode 100644
index 0000000000..9152b473d6
--- /dev/null
+++ b/com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/jupiter/LoggingRule.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2025 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.jupiter;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.TestWatcher;
+
+
+/**
+ * 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 implements TestWatcher, BeforeEachCallback, AfterEachCallback {
+
+ 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;
+ }
+ }
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("STARTING: " + getDescriptionName(context));
+ }
+ }
+
+ /**
+ * 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 ExtensionContext context) {
+ return context.getRequiredTestClass().getSimpleName() + '.' + context.getRequiredTestMethod().getName();
+ }
+
+ @Override
+ public void testSuccessful(final ExtensionContext context) {
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("SUCCEEDED: " + getDescriptionName(context));
+ }
+ }
+
+ @Override
+ public void testFailed(final ExtensionContext context, final Throwable cause) {
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("FAILED: " + getDescriptionName(context));
+ }
+ }
+
+ @Override
+ public void afterEach(final ExtensionContext context) throws Exception {
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("FINISHED: " + getDescriptionName(context));
+ }
+ }
+}
diff --git a/com.avaloq.tools.ddk.xtext.generator.test/META-INF/MANIFEST.MF b/com.avaloq.tools.ddk.xtext.generator.test/META-INF/MANIFEST.MF
index f0a8cb3887..c4247a2e9f 100644
--- a/com.avaloq.tools.ddk.xtext.generator.test/META-INF/MANIFEST.MF
+++ b/com.avaloq.tools.ddk.xtext.generator.test/META-INF/MANIFEST.MF
@@ -7,19 +7,18 @@ Bundle-Vendor: Avaloq Group AG
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Fragment-Host: com.avaloq.tools.ddk.xtext.generator
-Require-Bundle: com.avaloq.tools.ddk.test.core,
+Require-Bundle: com.avaloq.tools.ddk.test.core,
com.avaloq.tools.ddk.xtext.expression,
com.avaloq.tools.ddk.xtext.test.core,
org.eclipse.xtend,
org.eclipse.xtend.typesystem.emf,
org.eclipse.xtext,
- org.junit,
org.mockito.mockito-core,
com.avaloq.tools.ddk.xtext.ui,
org.eclipse.xtext.xtext.generator,
junit-jupiter-api,
junit-jupiter-engine,
- junit-vintage-engine
+ junit-platform-suite-api
Export-Package: com.avaloq.tools.ddk.xtext.generator.test.generator
Import-Package: org.eclipse.xtext.ui.resource
Automatic-Module-Name: com.avaloq.tools.ddk.xtext.generator.test
diff --git a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/CodeGenerationXTest.java b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/CodeGenerationXTest.java
index 00fa408b08..772a8220f5 100644
--- a/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/CodeGenerationXTest.java
+++ b/com.avaloq.tools.ddk.xtext.generator.test/src/com/avaloq/tools/ddk/xtext/generator/expression/CodeGenerationXTest.java
@@ -10,27 +10,27 @@
*******************************************************************************/
package com.avaloq.tools.ddk.xtext.generator.expression;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import org.eclipse.xtend.expression.ExecutionContextImpl;
import org.eclipse.xtend.type.impl.java.JavaBeansMetaModel;
import org.eclipse.xtend.typesystem.emf.EmfRegistryMetaModel;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import com.avaloq.tools.ddk.xtext.expression.expression.Expression;
import com.avaloq.tools.ddk.xtext.expression.generator.CompilationContext;
import com.avaloq.tools.ddk.xtext.expression.generator.CompilerX;
import com.avaloq.tools.ddk.xtext.expression.generator.GenModelUtilX;
import com.avaloq.tools.ddk.xtext.generator.test.util.GeneratorTestUtil;
-import com.avaloq.tools.ddk.xtext.test.AbstractXtextTest;
+import com.avaloq.tools.ddk.xtext.test.jupiter.AbstractXtextTest;
/**
* Tests the code generation as implemented by CodeGenerationX wrapped by {@link CompilerX}.
*/
-@SuppressWarnings("nls")
+@SuppressWarnings({"nls", "PMD.SignatureDeclareThrowsException"})
public class CodeGenerationXTest extends AbstractXtextTest {
private CompilerX getCompiler() {
@@ -61,7 +61,7 @@ protected String getTestSourceFileName() {
}
@Test
- public void testliterals() throws Exception {
+ void testliterals() throws Exception {
// CHECKSTYLE:CONSTANTS-OFF
assertEquals("42", compile("42")); // NOPMD
assertEquals("4.2", compile("4.2")); // NOPMD
@@ -73,19 +73,19 @@ public void testliterals() throws Exception {
}
@Test
- public void testListLiterals() throws Exception {
+ void testListLiterals() throws Exception {
assertEquals("java.util.Collections. emptyList()", compile("{}")); // NOPMD
assertEquals("java.util.Collections.singletonList(1)", compile("{1}")); // NOPMD
assertEquals("com.google.common.collect.Lists.newArrayList(1, 2, 3)", compile("{1,2,3}")); // NOPMD
}
@Test
- public void testIdentifiers() throws Exception {
+ void testIdentifiers() throws Exception {
assertEquals("obj.getTrue()", compile("^true")); // NOPMD
}
@Test
- public void testBracketing() throws Exception {
+ void testBracketing() throws Exception {
// CHECKSTYLE:CONSTANTS-OFF
assertEquals("(4 + 2) * 3", compile("(4 + 2) * 3")); // NOPMD
assertEquals("(4 + 2) * 3 * 4", compile("(4 + 2) * 3 * 4")); // NOPMD
@@ -97,7 +97,7 @@ public void testBracketing() throws Exception {
}
@Test
- public void testBooleanLogic() throws Exception {
+ void testBooleanLogic() throws Exception {
// CHECKSTYLE:CONSTANTS-OFF
assertEquals("true", compile("true")); // NOPMD
assertEquals("false", compile("false")); // NOPMD
@@ -116,7 +116,7 @@ public void testBooleanLogic() throws Exception {
}
@Test
- public void testArithmetics() throws Exception {
+ void testArithmetics() throws Exception {
// CHECKSTYLE:CONSTANTS-OFF
assertEquals("4 + 2", compile("4 + 2")); // NOPMD
assertEquals("4 - 2", compile("4 - 2")); // NOPMD
@@ -127,7 +127,7 @@ public void testArithmetics() throws Exception {
}
@Test
- public void testPrefixExpressions() throws Exception {
+ void testPrefixExpressions() throws Exception {
// CHECKSTYLE:CONSTANTS-OFF
assertEquals("-(4 * 2)", compile("-(4 * 2)")); // NOPMD
assertEquals("-(-42)", compile("-(-42)")); // NOPMD
@@ -141,7 +141,7 @@ public void testPrefixExpressions() throws Exception {
}
@Test
- public void testInfixExpressions() throws Exception {
+ void testInfixExpressions() throws Exception {
// CHECKSTYLE:CONSTANTS-OFF
assertEquals("(true ? 1 : 2) + 3", compile("(true ? 1 : 2) + 3")); // NOPMD
assertEquals("!(true ? true : false)", compile("!(true ? true : false)")); // NOPMD
@@ -150,28 +150,28 @@ public void testInfixExpressions() throws Exception {
}
@Test
- public void testImplicitVariable() throws Exception {
+ void testImplicitVariable() throws Exception {
assertEquals("obj", compile("this")); // NOPMD
}
@Test
- public void testCasting() throws Exception {
+ void testCasting() throws Exception {
assertEquals("((org.eclipse.emf.ecore.EObject) obj)", compile("(ecore::EObject) this")); // NOPMD
}
@Test
- public void testTypes() throws Exception {
+ void testTypes() throws Exception {
assertEquals("org.eclipse.emf.ecore.EObject", compile("ecore::EObject")); // NOPMD
assertEquals("String", compile("java::lang::String")); // NOPMD
}
@Test
- public void testIsInstance() throws Exception {
+ void testIsInstance() throws Exception {
assertEquals("obj instanceof org.eclipse.emf.ecore.EObject", compile("ecore::EObject.isInstance(this)")); // NOPMD
}
@Test
- public void testEContainerNavigation() throws Exception {
+ void testEContainerNavigation() throws Exception {
// CHECKSTYLE:CONSTANTS-OFF
assertEquals("obj.eContainer()", compile("this.eContainer")); // NOPMD
assertEquals("obj.eContainer()", compile("this.eContainer()")); // NOPMD
@@ -179,7 +179,7 @@ public void testEContainerNavigation() throws Exception {
}
@Test
- public void testTypeSelect() throws Exception {
+ void testTypeSelect() throws Exception {
assertEquals(// NOPMD
"com.google.common.collect.Iterables.filter(obj.getFoos(), org.eclipse.emf.ecore.EObject.class)", compile("this.foos.typeSelect(ecore::EObject)"));
assertEquals(// NOPMD
@@ -187,13 +187,13 @@ public void testTypeSelect() throws Exception {
}
@Test
- public void testCollectionExpression() throws Exception {
+ void testCollectionExpression() throws Exception {
assertEquals(// NOPMD
"com.google.common.collect.Iterables.filter(java.util.Collections.singletonList(obj), new com.google.common.base.Predicate