-
Notifications
You must be signed in to change notification settings - Fork 90
How to use junit.jupiter Extensions in OSGi and plain java #2510
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
.../bundles/org.eclipse.test/src/META-INF/services/org.junit.jupiter.api.extension.Extension
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| org.eclipse.test.services.LoggingTestExtension | ||
| org.eclipse.test.services.SwtLeakTestExtension |
1 change: 1 addition & 0 deletions
1
eclipse.platform.releng/bundles/org.eclipse.test/src/junit-platform.properties
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| junit.jupiter.extensions.autodetection.enabled=true |
56 changes: 56 additions & 0 deletions
56
...m.releng/bundles/org.eclipse.test/src/org/eclipse/test/services/LoggingTestExtension.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2024 Joerg Kubitz and others. | ||
| * | ||
| * This program and the accompanying materials | ||
| * are made available under the terms of the Eclipse Public License 2.0 | ||
| * which accompanies this distribution, and is available at | ||
| * https://www.eclipse.org/legal/epl-2.0/ | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| * | ||
| * Contributors: | ||
| * Joerg Kubitz - initial API and implementation | ||
| *******************************************************************************/ | ||
| package org.eclipse.test.services; | ||
jukzi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import org.junit.jupiter.api.extension.AfterTestExecutionCallback; | ||
| import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; | ||
| import org.junit.jupiter.api.extension.ExtensionContext; | ||
| import org.junit.jupiter.api.extension.ExtensionContext.Namespace; | ||
| import org.junit.jupiter.api.extension.ExtensionContext.Store; | ||
|
|
||
| /** logs start, stop, duration, error for all tests executed **/ | ||
| public class LoggingTestExtension implements AfterTestExecutionCallback, BeforeTestExecutionCallback { | ||
| public static String BEFORE_TEST_START = "Test Before: "; | ||
| public static String AFTER_TEST_PASSED = "Test Passed: "; | ||
| public static String AFTER_TEST_FAILED = "Test Failed: "; | ||
|
|
||
| public LoggingTestExtension() { | ||
| System.out.println("LoggingTestService"); | ||
| } | ||
| @Override | ||
| public void beforeTestExecution(ExtensionContext context) throws Exception { | ||
| long n0 = System.nanoTime(); | ||
| getStore(context).put("TIME", n0); | ||
| System.out.println(BEFORE_TEST_START + context.getDisplayName()); | ||
| } | ||
|
|
||
| @Override | ||
| public void afterTestExecution(ExtensionContext context) throws Exception { | ||
| long t1 = System.nanoTime(); | ||
| long t0 = getStore(context).remove("TIME", long.class); | ||
| String took = " after: " + (t1 - t0) / 1_000_000 + "ms"; | ||
| Throwable t = context.getExecutionException().orElse(null); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's the most simple way to judge the test outcome. alternatively you could also implement https://junit.org/junit5/docs/5.4.1/api/org/junit/jupiter/api/extension/TestWatcher.html, which gets notified on test results. Not sure what fits better for your case. |
||
| if (t == null) { | ||
| System.out.println(AFTER_TEST_PASSED + context.getDisplayName() + took); | ||
| } else { | ||
| System.out.println(AFTER_TEST_FAILED + context.getDisplayName() + took); | ||
| t.printStackTrace(System.out); | ||
| } | ||
| } | ||
|
|
||
| private Store getStore(ExtensionContext context) { | ||
| return context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod())); | ||
| } | ||
|
|
||
| } | ||
50 changes: 50 additions & 0 deletions
50
...leng/bundles/org.eclipse.test/src/org/eclipse/test/services/LoggingTestExtensionTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2024 Joerg Kubitz and others. | ||
| * | ||
| * This program and the accompanying materials | ||
| * are made available under the terms of the Eclipse Public License 2.0 | ||
| * which accompanies this distribution, and is available at | ||
| * https://www.eclipse.org/legal/epl-2.0/ | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| * | ||
| * Contributors: | ||
| * Joerg Kubitz - initial API and implementation | ||
| *******************************************************************************/ | ||
| package org.eclipse.test.services; | ||
|
|
||
| import static org.junit.Assert.assertTrue; | ||
|
|
||
| import java.io.ByteArrayOutputStream; | ||
| import java.io.PrintStream; | ||
|
|
||
| import org.junit.jupiter.api.AfterAll; | ||
| import org.junit.jupiter.api.BeforeAll; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| /** verifies the Extension is used **/ | ||
| public class LoggingTestExtensionTest { | ||
| private static final String SOMETHING = "something"; | ||
| private static final ByteArrayOutputStream OUT = new ByteArrayOutputStream(); | ||
| private static PrintStream oldStdOut; | ||
|
|
||
| @BeforeAll | ||
| public static void beforeAll() { | ||
| oldStdOut = System.out; | ||
| System.setOut(new PrintStream(OUT)); | ||
| } | ||
| @Test | ||
| public void testWritten() { | ||
| System.out.println(SOMETHING); | ||
| } | ||
|
|
||
| @AfterAll | ||
| public static void afterAll() { | ||
| String output = new String(OUT.toByteArray()); | ||
| System.setOut(oldStdOut); | ||
| assertTrue(output, output.contains("testWritten")); | ||
| assertTrue(output, output.contains(SOMETHING)); | ||
| assertTrue(output, output.contains(org.eclipse.test.services.LoggingTestExtension.BEFORE_TEST_START)); | ||
| assertTrue(output, output.contains(org.eclipse.test.services.LoggingTestExtension.AFTER_TEST_PASSED)); | ||
| } | ||
| } |
31 changes: 31 additions & 0 deletions
31
...eng/bundles/org.eclipse.test/src/org/eclipse/test/services/LoggingTestExtensionTest2.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2024 Joerg Kubitz and others. | ||
| * | ||
| * This program and the accompanying materials | ||
| * are made available under the terms of the Eclipse Public License 2.0 | ||
| * which accompanies this distribution, and is available at | ||
| * https://www.eclipse.org/legal/epl-2.0/ | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| * | ||
| * Contributors: | ||
| * Joerg Kubitz - initial API and implementation | ||
| *******************************************************************************/ | ||
| package org.eclipse.test.services; | ||
|
|
||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| // Manual demonstration: | ||
| public class LoggingTestExtensionTest2 { | ||
|
|
||
| @Test | ||
| public void testWritten() { | ||
| System.out.println("real"); | ||
| } | ||
|
|
||
| @Test | ||
| public void testWritten2() { | ||
| throw new RuntimeException("intended"); | ||
| } | ||
|
|
||
| } |
63 changes: 63 additions & 0 deletions
63
...m.releng/bundles/org.eclipse.test/src/org/eclipse/test/services/SwtLeakTestExtension.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2024 Joerg Kubitz and others. | ||
| * | ||
| * This program and the accompanying materials | ||
| * are made available under the terms of the Eclipse Public License 2.0 | ||
| * which accompanies this distribution, and is available at | ||
| * https://www.eclipse.org/legal/epl-2.0/ | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| * | ||
| * Contributors: | ||
| * Joerg Kubitz - initial API and implementation | ||
| *******************************************************************************/ | ||
| package org.eclipse.test.services; | ||
|
|
||
| import static org.junit.Assert.assertEquals; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| import org.eclipse.swt.widgets.Shell; | ||
| import org.eclipse.ui.IWorkbench; | ||
| import org.eclipse.ui.PlatformUI; | ||
| import org.junit.jupiter.api.extension.AfterTestExecutionCallback; | ||
| import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; | ||
| import org.junit.jupiter.api.extension.ExtensionContext; | ||
| import org.junit.jupiter.api.extension.ExtensionContext.Namespace; | ||
| import org.junit.jupiter.api.extension.ExtensionContext.Store; | ||
|
|
||
| /** logs start, stop, duration, error for all tests executed **/ | ||
| public class SwtLeakTestExtension implements AfterTestExecutionCallback, BeforeTestExecutionCallback { | ||
| @Override | ||
| public void beforeTestExecution(ExtensionContext context) throws Exception { | ||
| IWorkbench workbench = PlatformUI.getWorkbench(); | ||
| Set<Shell> preExistingShells = Set.of(workbench.getDisplay().getShells()); | ||
|
|
||
| getStore(context).put("workbench", workbench); | ||
| getStore(context).put("preExistingShells", preExistingShells); | ||
| } | ||
|
|
||
| @Override | ||
| public void afterTestExecution(ExtensionContext context) throws Exception { | ||
| IWorkbench workbench = getStore(context).remove("workbench", IWorkbench.class); | ||
| Set<Shell> preExistingShells = getStore(context).remove("preExistingShells", Set.class); | ||
| // Check for shell leak. | ||
| List<String> leakedModalShellTitles = new ArrayList<>(); | ||
| Shell[] shells = workbench.getDisplay().getShells(); | ||
| for (Shell shell : shells) { | ||
| if (!shell.isDisposed() && !preExistingShells.contains(shell)) { | ||
| leakedModalShellTitles.add(shell.getText()); | ||
| shell.close(); | ||
| } | ||
| } | ||
| assertEquals("Test leaked modal shell: [" + String.join(", ", leakedModalShellTitles) + "]", 0, | ||
| leakedModalShellTitles.size()); | ||
| } | ||
|
|
||
| private Store getStore(ExtensionContext context) { | ||
| return context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod())); | ||
| } | ||
|
|
||
| } |
48 changes: 48 additions & 0 deletions
48
...leng/bundles/org.eclipse.test/src/org/eclipse/test/services/SwtLeakTestExtensionTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2024 Joerg Kubitz and others. | ||
| * | ||
| * This program and the accompanying materials | ||
| * are made available under the terms of the Eclipse Public License 2.0 | ||
| * which accompanies this distribution, and is available at | ||
| * https://www.eclipse.org/legal/epl-2.0/ | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| * | ||
| * Contributors: | ||
| * Joerg Kubitz - initial API and implementation | ||
| *******************************************************************************/ | ||
| package org.eclipse.test.services; | ||
|
|
||
| import org.eclipse.swt.widgets.Display; | ||
| import org.eclipse.swt.widgets.Shell; | ||
| import org.eclipse.ui.IWorkbench; | ||
| import org.eclipse.ui.PlatformUI; | ||
| import org.junit.jupiter.api.AfterAll; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| // Manual demonstration: | ||
| public class SwtLeakTestExtensionTest { | ||
| static Shell leakedShell; | ||
| @Test | ||
| public void testNoLeak() { | ||
| System.out.println("noleak"); | ||
| IWorkbench workbench = PlatformUI.getWorkbench(); | ||
| Display display = workbench.getDisplay(); | ||
| Shell shell = new Shell(display); | ||
| shell.dispose(); | ||
| } | ||
|
|
||
| @Test | ||
| public void testLeak() { // fails | ||
| System.out.println("leak"); | ||
| IWorkbench workbench = PlatformUI.getWorkbench(); | ||
| Display display = workbench.getDisplay(); | ||
| leakedShell = new Shell(display); | ||
| } | ||
|
|
||
| @AfterAll | ||
| public static void afterAll() { | ||
| leakedShell.dispose(); | ||
| } | ||
|
|
||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should not the bundle
org.apache.aries.spifly.dynamic.bundleby it's name.If you want to make sure that this bundle properly registers it's provided service at the OSGi service loader mediator is present then you should instead register the extension as provided Java service:
Maybe this also helps with your registration issue, because without these changes the extension is basically invisible in an OSGi runtime. Of course this requires also that the JUnit bundles are configured correspondingly for OSGi runtimes as service consumer.
For reference see
https://aries.apache.org/documentation/modules/spi-fly.html#specconf
https://docs.osgi.org/specification/osgi.cmpn/8.0.0/service.loader.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which issue? this example seems to work fine. I would like to understand what is changed/improved if i would use the "Capability" thing instead. Especially how that would work in an non-osgi environment like SWT tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue you mentioned in
To summarize it in short: It makes the Java service loader work in OSGi runtimes, where it does not work out of the box due to classloader separation of bundles. Additionally you have to make sure that a provider is auto-started by the app or product.
In non-OSGi environments like SWT tests the java Service-Loader works out of the box because the app has one flat classpath.
In general I'm not aware of any situation where a bundle should require
org.apache.aries.spifly.dynamic.bundleor one of it's packages directly.If you point me to your exact use-case I can try to help making it work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The general idea is to surround all junit tests in the I-Build with the same logging and checks. However as far as i have tested it seems to work on junit5 tests only and still most tests are 3 or 4. Thats where i am stuck with this experiment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we more want a require capability then here this will require/pull in that automatically.