forked from eclipse-platform/eclipse.platform
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate ConfigurationSessionTestSuite to JUnit 5 eclipse-platform#903
This introduces a JUnit 5 replacement for the JUnit 3 ConfigurationSessionTestSuite that allows to define session tests using a custom workbench configuration with an explicit config.ini defining a custom-defined bundle set. It is realized by the CustomSessionConfiguration class, which encapsulates all information and logic to set up a custom workbench configuration, and an additional parameterization of the JUnit 5 SessionTestExecution extension, which allows this extension to replace the default workbench configuration with the custom one. The new capability of defining a custom workbench configuration for a JUnit 5 session test is applied to the PlatformURLSessionTest, which was based on the JUnit 3 ConfigurationSessionTestSuite so far. Contributes to eclipse-platform#903
- Loading branch information
1 parent
416af86
commit a055e52
Showing
6 changed files
with
434 additions
and
61 deletions.
There are no files selected for viewing
69 changes: 69 additions & 0 deletions
69
....tests.harness/src/org/eclipse/core/tests/harness/session/CustomSessionConfiguration.java
This file contains 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,69 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2024 Vector Informatik GmbH and others. | ||
* | ||
* This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v2.0 | ||
* which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*******************************************************************************/ | ||
package org.eclipse.core.tests.harness.session; | ||
|
||
import java.nio.file.Path; | ||
import org.eclipse.core.tests.harness.session.customization.SessionCustomization; | ||
import org.osgi.framework.Bundle; | ||
|
||
/** | ||
* A session customization to use a custom session configuration, i.e., a custom | ||
* "config.ini" file with a defined set of bundles to be used. | ||
*/ | ||
public interface CustomSessionConfiguration extends SessionCustomization { | ||
|
||
/** | ||
* Sets the given configuration directory. If not called, a temporary folder is | ||
* used as the configuration directory. | ||
* | ||
* @param configurationDirectory the path of the directory to place the | ||
* configuration in, must not be {@code null} | ||
* | ||
* @return this | ||
*/ | ||
public CustomSessionConfiguration setConfigurationDirectory(Path configurationDirectory); | ||
|
||
/** | ||
* Adds the bundle containing the given class to the session configuration. | ||
* | ||
* @param classFromBundle a class from the bundle to add, must not be | ||
* {@code null} | ||
* | ||
* @return this | ||
*/ | ||
public CustomSessionConfiguration addBundle(Class<?> classFromBundle); | ||
|
||
/** | ||
* Adds the bundle to the session configuration. | ||
* | ||
* @param bundle the bundle to add, must not be {@code null} | ||
* | ||
* @return this | ||
*/ | ||
public CustomSessionConfiguration addBundle(Bundle bundle); | ||
|
||
/** | ||
* Activates the "osgi.configuration.cascaded" option for this configuration. | ||
* | ||
* @return this | ||
*/ | ||
public CustomSessionConfiguration setCascaded(); | ||
|
||
/** | ||
* Marks the configuration area as read only. This will be effective from the | ||
* second session on, since the the first session requires a writable | ||
* configuration area for initial setup. | ||
* | ||
* @return this | ||
*/ | ||
public CustomSessionConfiguration setReadOnly(); | ||
|
||
} |
This file contains 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
258 changes: 258 additions & 0 deletions
258
.../org/eclipse/core/tests/harness/session/customization/CustomSessionConfigurationImpl.java
This file contains 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,258 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2024 Vector Informatik GmbH and others. | ||
* | ||
* This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v2.0 | ||
* which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*******************************************************************************/ | ||
package org.eclipse.core.tests.harness.session.customization; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.Assert.assertTrue; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.Properties; | ||
import java.util.stream.Collectors; | ||
import org.eclipse.core.internal.runtime.InternalPlatform; | ||
import org.eclipse.core.runtime.FileLocator; | ||
import org.eclipse.core.runtime.Platform; | ||
import org.eclipse.core.tests.harness.session.CustomSessionConfiguration; | ||
import org.eclipse.core.tests.session.ConfigurationSessionTestSuite; | ||
import org.eclipse.core.tests.session.Setup; | ||
import org.junit.Assert; | ||
import org.osgi.framework.Bundle; | ||
import org.osgi.framework.Constants; | ||
import org.osgi.framework.FrameworkUtil; | ||
import org.osgi.framework.Version; | ||
|
||
@SuppressWarnings("restriction") | ||
public class CustomSessionConfigurationImpl implements CustomSessionConfiguration { | ||
private static final String PROP_BUNDLES = "osgi.bundles"; | ||
private static final String PROP_FRAMEWORK = "osgi.framework"; | ||
private static final String PROP_BUNDLES_DEFAULT_START_LEVEL = "osgi.bundles.defaultStartLevel"; | ||
private static final String PROP_INSTALL_AREA = "osgi.install.area"; | ||
private static final String PROP_CONFIG_AREA_READ_ONLY = InternalPlatform.PROP_CONFIG_AREA + ".readOnly"; | ||
private static final String PROP_CONFIG_CASCADED = "osgi.configuration.cascaded"; | ||
|
||
private final Collection<BundleReference> bundleReferences = new ArrayList<>(); | ||
private Path configurationDirectory; | ||
private boolean readOnly = false; | ||
private boolean cascaded = false; | ||
private boolean firstExecutedSession = true; | ||
|
||
public CustomSessionConfigurationImpl() { | ||
addMinimalBundleSet(); | ||
} | ||
|
||
@SuppressWarnings("deprecation") | ||
private void addMinimalBundleSet() { | ||
// Just use any class from the bundles we want to add as minimal bundle set | ||
|
||
addBundle(org.eclipse.core.runtime.FileLocator.class, "@2:start"); // org.eclipse.equinox.common | ||
addBundle(org.eclipse.core.runtime.Platform.class, "@:start"); // org.eclipse.core.runtime | ||
addBundle(org.eclipse.core.runtime.jobs.Job.class); // org.eclipse.core.jobs | ||
addBundle(org.eclipse.core.runtime.IExtension.class); // org.eclipse.equinox.registry | ||
addBundle(org.eclipse.core.runtime.preferences.IEclipsePreferences.class); // org.eclipse.equinox.preferences | ||
addBundle(org.osgi.service.prefs.Preferences.class); // org.osgi.service.prefs | ||
addBundle(org.eclipse.core.runtime.content.IContentType.class); // org.eclipse.core.contenttype | ||
addBundle(org.eclipse.equinox.app.IApplication.class); // org.eclipse.equinox.app | ||
|
||
addBundle(org.eclipse.core.tests.harness.TestHarnessPlugin.class); // org.eclipse.core.tests.harness | ||
addBundle(org.eclipse.test.performance.Performance.class); // org.eclipse.test.performance | ||
|
||
addBundle(org.eclipse.jdt.internal.junit.runner.ITestLoader.class); // org.eclipse.jdt.junit.runtime | ||
addBundle(org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.class); // org.eclipse.jdt.junit4.runtime | ||
addBundle(org.eclipse.jdt.internal.junit5.runner.JUnit5TestLoader.class); // org.eclipse.jdt.junit5.runtime | ||
addBundle(org.eclipse.pde.internal.junit.runtime.CoreTestApplication.class); // org.eclipse.pde.junit.runtime | ||
|
||
addBundle(net.bytebuddy.ByteBuddy.class); // net.bytebuddy for org.assertj.core.api | ||
addBundle(org.assertj.core.api.Assertions.class); // org.assertj.core.api | ||
addBundle(org.hamcrest.CoreMatchers.class); // org.hamcrest.core | ||
|
||
// The org.junit bundle requires an org.hamcrest.core bundle, but as of version | ||
// 2.x, the org.hamcrest bundle above provides the actual classes. So we need to | ||
// ensure that the actual org.hamcrest.core bundle required by org.junit is | ||
// added too. | ||
if ("org.hamcrest".equals(FrameworkUtil.getBundle(org.hamcrest.CoreMatchers.class).getSymbolicName())) { | ||
Bundle maxHamcrestCoreBundle = null; | ||
Version maxHamcrestCoreVersion = null; | ||
for (Bundle bundle : FrameworkUtil.getBundle(ConfigurationSessionTestSuite.class).getBundleContext() | ||
.getBundles()) { | ||
if ("org.hamcrest.core".equals(bundle.getSymbolicName())) { | ||
Version version = bundle.getVersion(); | ||
if (maxHamcrestCoreVersion == null || maxHamcrestCoreVersion.compareTo(version) < 0) { | ||
maxHamcrestCoreVersion = version; | ||
maxHamcrestCoreBundle = bundle; | ||
} | ||
} | ||
} | ||
if (maxHamcrestCoreBundle != null) { | ||
addBundle(maxHamcrestCoreBundle, null); | ||
} | ||
} | ||
|
||
addBundle(org.junit.Test.class); // org.junit | ||
addBundle(org.junit.jupiter.api.Test.class); // junit-jupiter-api | ||
addBundle(org.junit.jupiter.engine.JupiterTestEngine.class); // junit-jupiter-engine | ||
addBundle(org.junit.jupiter.migrationsupport.EnableJUnit4MigrationSupport.class); // junit-jupiter-migrationsupport | ||
addBundle(org.junit.jupiter.params.ParameterizedTest.class); // junit-jupiter-params | ||
addBundle(org.junit.vintage.engine.VintageTestEngine.class); // junit-vintage-engine | ||
addBundle(org.junit.platform.commons.JUnitException.class); // junit-platform-commons | ||
addBundle(org.junit.platform.engine.TestEngine.class); // junit-platform-engine | ||
addBundle(org.junit.platform.launcher.Launcher.class); // junit-platform-launcher | ||
addBundle(org.junit.platform.runner.JUnitPlatform.class); // junit-platform-runner | ||
addBundle(org.junit.platform.suite.api.Suite.class); // junit-platform-suite-api | ||
addBundle(org.junit.platform.suite.commons.SuiteLauncherDiscoveryRequestBuilder.class); // junit-platform-suite-commons | ||
addBundle(org.junit.platform.suite.engine.SuiteTestEngine.class); // junit-platform-suite-engine | ||
addBundle(org.apiguardian.api.API.class); // org.apiguardian.api | ||
addBundle(org.opentest4j.AssertionFailedError.class); // org.opentest4j | ||
} | ||
|
||
@Override | ||
public CustomSessionConfiguration setCascaded() { | ||
this.cascaded = true; | ||
return this; | ||
} | ||
|
||
@Override | ||
public CustomSessionConfiguration setReadOnly() { | ||
this.readOnly = true; | ||
return this; | ||
} | ||
|
||
@Override | ||
public CustomSessionConfiguration setConfigurationDirectory(Path configurationDirectory) { | ||
Objects.requireNonNull(configurationDirectory); | ||
this.configurationDirectory = configurationDirectory; | ||
return this; | ||
} | ||
|
||
private Path getConfigurationDirectory() throws IOException { | ||
if (configurationDirectory == null) { | ||
this.configurationDirectory = Files.createTempDirectory(null); | ||
this.configurationDirectory.toFile().deleteOnExit(); | ||
} | ||
return configurationDirectory; | ||
} | ||
|
||
@Override | ||
public void prepareSession(Setup setup) throws IOException { | ||
if (firstExecutedSession) { | ||
// configuration area needs to be written on first start | ||
overwriteConfigurationAreaWritability(setup); | ||
} | ||
setCustomConfigurationArea(setup); | ||
if (firstExecutedSession) { | ||
createOrRefreshConfigIni(); | ||
} | ||
} | ||
|
||
@Override | ||
public void cleanupSession(Setup setup) { | ||
if (firstExecutedSession) { | ||
// after first session, use configuration area's readability configuration | ||
removeConfigurationAreaWritabilityOverwrite(setup); | ||
} | ||
firstExecutedSession = false; | ||
} | ||
|
||
private void overwriteConfigurationAreaWritability(Setup setup) { | ||
setup.setSystemProperty(PROP_CONFIG_AREA_READ_ONLY, Boolean.FALSE.toString()); | ||
} | ||
|
||
private void removeConfigurationAreaWritabilityOverwrite(Setup setup) { | ||
setup.setSystemProperty(PROP_CONFIG_AREA_READ_ONLY, null); | ||
} | ||
|
||
private void setCustomConfigurationArea(Setup setup) throws IOException { | ||
// the base implementation will have set this to the host configuration | ||
setup.setEclipseArgument(Setup.CONFIGURATION, null); | ||
setup.setSystemProperty(InternalPlatform.PROP_CONFIG_AREA, getConfigurationDirectory().toString()); | ||
} | ||
|
||
private void createOrRefreshConfigIni() throws IOException { | ||
Properties contents = new Properties(); | ||
contents.put(PROP_BUNDLES, String.join(",", getBundleUrls())); | ||
contents.put(PROP_FRAMEWORK, getOsgiFrameworkBundleUrl()); | ||
contents.put(PROP_BUNDLES_DEFAULT_START_LEVEL, "4"); | ||
contents.put(PROP_INSTALL_AREA, Platform.getInstallLocation().getURL().toExternalForm()); | ||
contents.put(PROP_CONFIG_CASCADED, Boolean.valueOf(cascaded).toString()); | ||
contents.put(PROP_CONFIG_AREA_READ_ONLY, Boolean.valueOf(readOnly).toString()); | ||
// save the properties | ||
Path configINI = getConfigurationDirectory().resolve("config.ini"); | ||
try (OutputStream out = Files.newOutputStream(configINI)) { | ||
contents.store(out, null); | ||
} | ||
} | ||
|
||
@Override | ||
public CustomSessionConfiguration addBundle(Class<?> classFromBundle) { | ||
Objects.requireNonNull(classFromBundle); | ||
addBundle(classFromBundle, null); | ||
return this; | ||
} | ||
|
||
private void addBundle(Class<?> classFromBundle, String suffix) { | ||
Bundle bundle = FrameworkUtil.getBundle(classFromBundle); | ||
Assert.assertNotNull("Class is not from a bundle: " + classFromBundle, bundle); | ||
addBundle(bundle, suffix); | ||
} | ||
|
||
@Override | ||
public CustomSessionConfiguration addBundle(Bundle bundle) { | ||
Objects.requireNonNull(bundle); | ||
addBundle(bundle, ""); | ||
return this; | ||
} | ||
|
||
private void addBundle(Bundle bundle, String suffix) { | ||
bundleReferences.add(new BundleReference(bundle, suffix)); | ||
} | ||
|
||
private Collection<String> getBundleUrls() { | ||
assertThat(bundleReferences).as("check bundles are not empty").isNotEmpty(); | ||
return bundleReferences.stream().map(BundleReference::toURL).collect(Collectors.toList()); | ||
} | ||
|
||
private static String getOsgiFrameworkBundleUrl() { | ||
Bundle osgiFrameworkBundle = FrameworkUtil.getBundle(CustomSessionConfigurationImpl.class).getBundleContext() | ||
.getBundle(Constants.SYSTEM_BUNDLE_LOCATION); | ||
BundleReference osgiFrameworkBundleReference = new BundleReference(osgiFrameworkBundle); | ||
return osgiFrameworkBundleReference.toURL(); | ||
} | ||
|
||
private record BundleReference(Bundle bundle, String suffix) { | ||
BundleReference(Bundle bundle, String suffix) { | ||
this.bundle = bundle; | ||
this.suffix = suffix != null ? suffix : ""; | ||
} | ||
|
||
BundleReference(Bundle bundle) { | ||
this(bundle, null); | ||
} | ||
|
||
String toURL() { | ||
Optional<File> location = FileLocator.getBundleFileLocation(bundle); | ||
assertTrue("Unable to locate bundle with id: " + bundle.getSymbolicName(), location.isPresent()); | ||
String externalForm; | ||
try { | ||
externalForm = location.get().toURI().toURL().toExternalForm(); | ||
} catch (Exception e) { | ||
throw new IllegalArgumentException("Failed to convert file to URL string:" + location.get(), e); | ||
} | ||
// workaround for bug 88070 | ||
return "reference:" + externalForm + suffix; | ||
} | ||
} | ||
} |
Oops, something went wrong.