| /******************************************************************************* |
| * Copyright (c) 2006, 2021 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.tests.bundles; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.jar.Attributes; |
| import java.util.jar.JarOutputStream; |
| import java.util.jar.Manifest; |
| import org.eclipse.core.tests.harness.CoreTest; |
| import org.eclipse.osgi.framework.util.ThreadInfoReport; |
| import org.eclipse.osgi.launch.Equinox; |
| import org.eclipse.osgi.tests.OSGiTestsActivator; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleEvent; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.FrameworkEvent; |
| import org.osgi.framework.launch.Framework; |
| |
| public class AbstractBundleTests extends CoreTest { |
| public static int BUNDLE_LISTENER = 0x01; |
| public static int SYNC_BUNDLE_LISTENER = 0x02; |
| public static int SIMPLE_RESULTS = 0x04; |
| public static final String BUNDLES_ROOT = "bundle_tests"; |
| public static TestResults simpleResults; |
| public static EventListenerTestResults listenerResults; |
| public static SyncEventListenerTestResults syncListenerResults; |
| public static EventListenerTestResults frameworkListenerResults; |
| public static BundleInstaller installer; |
| |
| static class BundleBuilder { |
| static class BundleManifestBuilder { |
| private final Manifest manifest = new Manifest(); |
| |
| public Manifest build() { |
| manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); |
| return manifest; |
| } |
| |
| public BundleManifestBuilder symbolicName(String value) { |
| manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, value); |
| return this; |
| } |
| } |
| |
| private final BundleManifestBuilder manifestBuilder = new BundleManifestBuilder(); |
| |
| public InputStream build() throws IOException { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| JarOutputStream jos = new JarOutputStream(baos, manifestBuilder.build()); |
| jos.close(); |
| return new ByteArrayInputStream(baos.toByteArray()); |
| } |
| |
| public BundleBuilder symbolicName(String value) { |
| manifestBuilder.symbolicName(value); |
| return this; |
| } |
| } |
| |
| protected void setUp() throws Exception { |
| installer = new BundleInstaller(BUNDLES_ROOT, OSGiTestsActivator.getContext()); |
| installer.refreshPackages(null); |
| listenerResults = new EventListenerTestResults(); |
| OSGiTestsActivator.getContext().addBundleListener(listenerResults); |
| syncListenerResults = new SyncEventListenerTestResults(); |
| OSGiTestsActivator.getContext().addBundleListener(syncListenerResults); |
| simpleResults = new TestResults(); |
| frameworkListenerResults = new EventListenerTestResults(); |
| OSGiTestsActivator.getContext().addFrameworkListener(frameworkListenerResults); |
| } |
| |
| protected void tearDown() throws Exception { |
| installer.shutdown(); |
| installer = null; |
| OSGiTestsActivator.getContext().removeBundleListener(listenerResults); |
| OSGiTestsActivator.getContext().removeBundleListener(syncListenerResults); |
| OSGiTestsActivator.getContext().removeFrameworkListener(frameworkListenerResults); |
| listenerResults = null; |
| syncListenerResults = null; |
| simpleResults = null; |
| frameworkListenerResults = null; |
| } |
| |
| public BundleContext getContext() { |
| return OSGiTestsActivator.getContext(); |
| } |
| |
| static public void compareResults(Object[] expectedEvents, Object[] actualEvents) { |
| String expectedActual = " -- EXPECTED:" + toStringEventArray(expectedEvents) + " ACTUAL:" + toStringEventArray(actualEvents); |
| assertEquals("compareResults length" + expectedActual, expectedEvents.length, actualEvents.length); |
| for (int i = 0; i < expectedEvents.length; i++) { |
| String assertMsg = "compareResults: " + i + expectedActual; |
| assertEquals(assertMsg, expectedEvents[i], actualEvents[i]); |
| } |
| } |
| |
| static public String toStringEventArray(Object[] events) { |
| StringBuilder sb = new StringBuilder(); |
| boolean first = true; |
| sb.append('['); |
| for (Object event : events) { |
| if (first) { |
| first = false; |
| } else { |
| sb.append(", "); |
| } |
| sb.append(toString(event)); |
| } |
| sb.append(']'); |
| return sb.toString(); |
| } |
| |
| static public void assertEquals(String message, Object expected, Object actual) { |
| if (expected == null && actual == null) |
| return; |
| if ((expected == null || actual == null) && expected != actual) |
| failNotEquals(message, toString(expected), toString(actual)); |
| if (isEqual(expected, actual)) |
| return; |
| failNotEquals(message, toString(expected), toString(actual)); |
| } |
| |
| static public void assertEquals(String message, int[] expected, int[] actual) { |
| if (expected == null && actual == null) |
| return; |
| if ((expected == null || actual == null) && expected != actual) |
| failNotEquals(message, toString(expected), toString(actual)); |
| if (expected.length != actual.length) |
| failNotEquals(message, toString(expected), toString(actual)); |
| for (int i = 0; i < expected.length; i++) |
| if (expected[i] != actual[i]) |
| failNotEquals(message, toString(expected), toString(actual)); |
| } |
| |
| private static boolean isEqual(Object expected, Object actual) { |
| if (!expected.getClass().isAssignableFrom(actual.getClass())) |
| return false; |
| if (expected instanceof BundleEvent) |
| return isEqual((BundleEvent) expected, (BundleEvent) actual); |
| else if (expected instanceof FrameworkEvent) |
| return isEqual((FrameworkEvent) expected, (FrameworkEvent) actual); |
| return expected.equals(actual); |
| } |
| |
| private static boolean isEqual(BundleEvent expected, BundleEvent actual) { |
| return expected.getSource() == actual.getSource() && expected.getType() == actual.getType(); |
| } |
| |
| private static boolean isEqual(FrameworkEvent expected, FrameworkEvent actual) { |
| return expected.getSource() == actual.getSource() && expected.getType() == actual.getType(); |
| } |
| |
| private static String toString(int[] array) { |
| if (array == null) |
| return "null"; //$NON-NLS-1$ |
| String result = "["; //$NON-NLS-1$ |
| for (int i = 0; i < array.length; i++) { |
| if (i != 0) |
| result += ','; |
| result += array[i]; |
| } |
| result += "]"; //$NON-NLS-1$ |
| return result; |
| } |
| |
| private static Object toString(Object object) { |
| if (object instanceof BundleEvent) |
| return toString((BundleEvent) object); |
| else if (object instanceof FrameworkEvent) |
| return toString((FrameworkEvent) object); |
| return object.toString(); |
| } |
| |
| private static Object toString(FrameworkEvent event) { |
| StringBuilder result = new StringBuilder("FrameworkEvent ["); |
| switch (event.getType()) { |
| case FrameworkEvent.ERROR : |
| result.append("ERROR"); |
| break; |
| case FrameworkEvent.INFO : |
| result.append("INFO"); |
| break; |
| case FrameworkEvent.PACKAGES_REFRESHED : |
| result.append("PACKAGES_REFRESHED"); |
| break; |
| case FrameworkEvent.STARTED : |
| result.append("STARTED"); |
| break; |
| case FrameworkEvent.STARTLEVEL_CHANGED : |
| result.append("STARTLEVEL_CHANGED"); |
| break; |
| case FrameworkEvent.WARNING : |
| result.append("WARNING"); |
| break; |
| default : |
| break; |
| } |
| result.append("] ").append(event.getSource()); |
| return result.toString(); |
| } |
| |
| private static Object toString(BundleEvent event) { |
| StringBuilder result = new StringBuilder("BundleEvent ["); |
| switch (event.getType()) { |
| case BundleEvent.INSTALLED : |
| result.append("INSTALLED"); |
| break; |
| case BundleEvent.LAZY_ACTIVATION : |
| result.append("LAZY_ACTIVATION"); |
| break; |
| case BundleEvent.RESOLVED : |
| result.append("RESOLVED"); |
| break; |
| case BundleEvent.STARTED : |
| result.append("STARTED"); |
| break; |
| case BundleEvent.STARTING : |
| result.append("STARTING"); |
| break; |
| case BundleEvent.STOPPED : |
| result.append("STOPPED"); |
| break; |
| case BundleEvent.STOPPING : |
| result.append("STOPPING"); |
| break; |
| case BundleEvent.UNINSTALLED : |
| result.append("UNINSTALLED"); |
| break; |
| case BundleEvent.UNRESOLVED : |
| result.append("UNRESOLVED"); |
| break; |
| case BundleEvent.UPDATED : |
| result.append("UPDATED"); |
| break; |
| default : |
| break; |
| } |
| result.append("] ").append(event.getSource()); |
| return result.toString(); |
| } |
| |
| protected Map<String, Object> createConfiguration() { |
| File file = OSGiTestsActivator.getContext().getDataFile(getName()); |
| Map<String, Object> result = new HashMap<>(); |
| result.put(Constants.FRAMEWORK_STORAGE, file.getAbsolutePath()); |
| return result; |
| } |
| |
| protected void initAndStart(Equinox equinox) throws BundleException { |
| equinox.init(); |
| equinox.start(); |
| } |
| |
| static public void stop(Framework equinox, int expected) { |
| FrameworkEvent actual = stop(equinox); |
| if (expected > 0) { |
| assertNotNull("No FrameworkEvent returned.", actual); |
| assertEquals("Wrong FrameworkEvent.", getFrameworkEventType(expected), |
| getFrameworkEventType(actual.getType())); |
| } |
| } |
| |
| static private String getFrameworkEventType(int type) { |
| switch (type) { |
| case FrameworkEvent.ERROR: |
| return "ERROR"; |
| case FrameworkEvent.INFO: |
| return "INFO"; |
| case FrameworkEvent.STARTED: |
| return "STARTED"; |
| case FrameworkEvent.STOPPED: |
| return "STOPPED"; |
| case FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED: |
| return "STOPPED_BOOTCLASSPATH_MODIFIED"; |
| case FrameworkEvent.STOPPED_SYSTEM_REFRESHED: |
| return "STOPPED_SYSTEM_REFRESHED"; |
| case FrameworkEvent.STOPPED_UPDATE: |
| return "STOPPED_UPDATE"; |
| default: |
| return "UNKNOWN:" + type; |
| } |
| } |
| |
| static public FrameworkEvent stop(Framework equinox) { |
| return stop(equinox, false, 10000); |
| } |
| |
| static public FrameworkEvent stopQuietly(Framework equinox) { |
| return stop(equinox, true, 10000); |
| } |
| |
| static public FrameworkEvent update(final Framework equinox) { |
| final Exception[] failureException = new BundleException[1]; |
| final FrameworkEvent[] success = new FrameworkEvent[] { null }; |
| final String uuid = getUUID(equinox); |
| Thread waitForUpdate = new Thread((Runnable) () -> success[0] = waitForStop(equinox, uuid, false, 10000), "test waitForStop thread"); //$NON-NLS-1$ |
| waitForUpdate.start(); |
| |
| try { |
| // delay hack to allow waitForUpdate thread to block on waitForStop before we |
| // update. |
| Thread.sleep(200); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| fail("unexpected interuption", e); |
| } |
| |
| try { |
| equinox.update(); |
| } catch (BundleException e) { |
| fail("Failed to update the framework", e); //$NON-NLS-1$ |
| } |
| try { |
| waitForUpdate.join(); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| fail("unexpected interuption", e); //$NON-NLS-1$ |
| } |
| if (failureException[0] != null) { |
| fail("Error occurred while waiting", failureException[0]); //$NON-NLS-1$ |
| } |
| return success[0]; |
| } |
| |
| static public FrameworkEvent stop(Framework equinox, boolean quietly, long timeout) { |
| if (equinox == null) |
| return null; |
| final String uuid = getUUID(equinox); |
| try { |
| equinox.stop(); |
| } catch (BundleException e) { |
| if (!quietly) { |
| fail("Unexpected error stopping framework", e); //$NON-NLS-1$ |
| } |
| } |
| return waitForStop(equinox, uuid, quietly, timeout); |
| } |
| |
| protected static boolean delete(File file) { |
| if (file.exists()) { |
| if (file.isDirectory()) { |
| String list[] = file.list(); |
| if (list != null) { |
| int len = list.length; |
| for (int i = 0; i < len; i++) { |
| delete(new File(file, list[i])); |
| } |
| } |
| } |
| |
| return file.delete(); |
| } |
| return (true); |
| } |
| |
| static public FrameworkEvent waitForStop(Framework equinox, String uuid, boolean quietly, long timeout) { |
| try { |
| FrameworkEvent stopEvent = equinox.waitForStop(timeout); |
| if (stopEvent.getType() == FrameworkEvent.WAIT_TIMEDOUT) { |
| StringBuilder sb = new StringBuilder("Framework state is: "); |
| sb.append(getState(equinox)).append(" - ").append(uuid).append('\n'); |
| sb.append(ThreadInfoReport.getThreadDump(null)).append('\n'); |
| if (!quietly) { |
| fail(sb.toString()); |
| } else { |
| System.out.println(sb.toString()); |
| } |
| } |
| return stopEvent; |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| if (!quietly) { |
| fail("Unexpected interrupted exception", e); //$NON-NLS-1$ |
| } |
| } |
| return null; |
| } |
| |
| public static String getUUID(Framework equinox) { |
| BundleContext bc = equinox.getBundleContext(); |
| return bc == null ? null : bc.getProperty(Constants.FRAMEWORK_UUID); |
| } |
| |
| static private String getState(Framework framework) { |
| int state = framework.getState(); |
| switch (state) { |
| case Bundle.UNINSTALLED: |
| return "UNINSTALLED"; |
| case Bundle.INSTALLED: |
| return "INSTALLED"; |
| case Bundle.RESOLVED: |
| return "RESOLVED"; |
| case Bundle.STARTING: |
| return "STARTING"; |
| case Bundle.ACTIVE: |
| return "ACTIVE"; |
| case Bundle.STOPPING: |
| return "STOPPING"; |
| default: |
| return "UNKNOWN:" + state; |
| } |
| } |
| } |