/******************************************************************************* * Copyright (c) 2008, 2011 VMware Inc. 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: * VMware Inc. - initial contribution * EclipseSource - Bug 358442 Change InstallArtifact graph from a tree to a DAG *******************************************************************************/ package org.eclipse.virgo.kernel.install.artifact.internal; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.List; import java.util.Set; import org.eclipse.virgo.kernel.artifact.ArtifactSpecification; import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS; import org.eclipse.virgo.kernel.artifact.plan.PlanDescriptor.Provisioning; import org.eclipse.virgo.nano.core.AbortableSignal; import org.eclipse.virgo.nano.deployer.api.core.DeploymentException; import org.eclipse.virgo.kernel.install.artifact.InstallArtifact; import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact; import org.eclipse.virgo.util.common.DirectedAcyclicGraph; import org.eclipse.virgo.util.common.GraphNode; import org.eclipse.virgo.util.common.ThreadSafeDirectedAcyclicGraph; import org.junit.Test; import org.osgi.framework.Version; public class AtomicInstallArtifactLifecycleListenerTests { private static enum Methods { ASYNC_START, // START, // STOP, // UNINSTALL } private final AtomicInstallArtifactLifecycleListener listener = new AtomicInstallArtifactLifecycleListener(); private final StubInstallArtifact oneAtomicNoTree = new StubInstallArtifact(true); private final StubInstallArtifact oneNonAtomicNoTree = new StubInstallArtifact(false); private final StubInstallArtifact oneAtomic = makeChain(new StubInstallArtifact(true)); private final StubInstallArtifact oneNonAtomic = makeChain(new StubInstallArtifact(false)); private final StubInstallArtifact twoNonAtomic = makeChain(new StubInstallArtifact(false), new StubInstallArtifact(false)); private final StubInstallArtifact oneNonAtomicOneAtomic = makeChain(new StubInstallArtifact(true), new StubInstallArtifact(false)); private final StubInstallArtifact twoNonAtomicOneAtomic = makeChain(new StubInstallArtifact(true), new StubInstallArtifact(false), new StubInstallArtifact(false)); private final StubInstallArtifact oneNonAtomicOneAtomicOneNonAtomic = makeChain(new StubInstallArtifact(false), new StubInstallArtifact(true), new StubInstallArtifact(false)); @Test public void onStarting() throws DeploymentException { listener.onStarting(oneAtomicNoTree); assertMethodCalls(oneAtomicNoTree); listener.onStarting(oneNonAtomicNoTree); assertMethodCalls(oneNonAtomicNoTree); listener.onStarting(oneAtomic); assertMethodCalls(oneAtomic); listener.onStarting(oneNonAtomic); assertMethodCalls(oneNonAtomic); listener.onStarting(twoNonAtomic); assertMethodCalls(twoNonAtomic); assertMethodCalls(getParent(twoNonAtomic)); listener.onStarting(oneNonAtomicOneAtomic); assertMethodCalls(oneNonAtomicOneAtomic); assertMethodCalls(getParent(oneNonAtomicOneAtomic), Methods.START); listener.onStarting(twoNonAtomicOneAtomic); assertMethodCalls(twoNonAtomicOneAtomic); assertMethodCalls(getParent(twoNonAtomicOneAtomic)); assertMethodCalls(getParent(getParent(twoNonAtomicOneAtomic))); listener.onStarting(oneNonAtomicOneAtomicOneNonAtomic); assertMethodCalls(oneNonAtomicOneAtomicOneNonAtomic); assertMethodCalls(getParent(oneNonAtomicOneAtomicOneNonAtomic), Methods.START); assertMethodCalls(getParent(getParent(oneNonAtomicOneAtomicOneNonAtomic))); } @Test public void onStartFailed() throws DeploymentException { listener.onStartFailed(oneAtomicNoTree, null); assertMethodCalls(oneAtomicNoTree); listener.onStartFailed(oneNonAtomicNoTree, null); assertMethodCalls(oneNonAtomicNoTree); listener.onStartFailed(oneAtomic, null); assertMethodCalls(oneAtomic); listener.onStartFailed(oneNonAtomic, null); assertMethodCalls(oneNonAtomic); listener.onStartFailed(twoNonAtomic, null); assertMethodCalls(twoNonAtomic); assertMethodCalls(getParent(twoNonAtomic)); listener.onStartFailed(oneNonAtomicOneAtomic, null); assertMethodCalls(oneNonAtomicOneAtomic); assertMethodCalls(getParent(oneNonAtomicOneAtomic), Methods.STOP); listener.onStartFailed(twoNonAtomicOneAtomic, null); assertMethodCalls(twoNonAtomicOneAtomic); assertMethodCalls(getParent(twoNonAtomicOneAtomic)); assertMethodCalls(getParent(getParent(twoNonAtomicOneAtomic))); listener.onStartFailed(oneNonAtomicOneAtomicOneNonAtomic, null); assertMethodCalls(oneNonAtomicOneAtomicOneNonAtomic); assertMethodCalls(getParent(oneNonAtomicOneAtomicOneNonAtomic), Methods.STOP); assertMethodCalls(getParent(getParent(oneNonAtomicOneAtomicOneNonAtomic))); } @Test public void onStopped() { listener.onStopped(oneAtomicNoTree); assertMethodCalls(oneAtomicNoTree); listener.onStopped(oneNonAtomicNoTree); assertMethodCalls(oneNonAtomicNoTree); listener.onStopped(oneAtomic); assertMethodCalls(oneAtomic); listener.onStopped(oneNonAtomic); assertMethodCalls(oneNonAtomic); listener.onStopped(twoNonAtomic); assertMethodCalls(twoNonAtomic); assertMethodCalls(getParent(twoNonAtomic)); listener.onStopped(oneNonAtomicOneAtomic); assertMethodCalls(oneNonAtomicOneAtomic); assertMethodCalls(getParent(oneNonAtomicOneAtomic), Methods.STOP); listener.onStopped(twoNonAtomicOneAtomic); assertMethodCalls(twoNonAtomicOneAtomic); assertMethodCalls(getParent(twoNonAtomicOneAtomic)); assertMethodCalls(getParent(getParent(twoNonAtomicOneAtomic))); listener.onStopped(oneNonAtomicOneAtomicOneNonAtomic); assertMethodCalls(oneNonAtomicOneAtomicOneNonAtomic); assertMethodCalls(getParent(oneNonAtomicOneAtomicOneNonAtomic), Methods.STOP); assertMethodCalls(getParent(getParent(oneNonAtomicOneAtomicOneNonAtomic))); } @Test public void onUninstalled() throws DeploymentException { listener.onUninstalled(oneAtomicNoTree); assertMethodCalls(oneAtomicNoTree); listener.onUninstalled(oneNonAtomicNoTree); assertMethodCalls(oneNonAtomicNoTree); listener.onUninstalled(oneAtomic); assertMethodCalls(oneAtomic); listener.onUninstalled(oneNonAtomic); assertMethodCalls(oneNonAtomic); listener.onUninstalled(twoNonAtomic); assertMethodCalls(twoNonAtomic); assertMethodCalls(getParent(twoNonAtomic)); listener.onUninstalled(oneNonAtomicOneAtomic); assertMethodCalls(oneNonAtomicOneAtomic); assertMethodCalls(getParent(oneNonAtomicOneAtomic), Methods.UNINSTALL); listener.onUninstalled(twoNonAtomicOneAtomic); assertMethodCalls(twoNonAtomicOneAtomic); assertMethodCalls(getParent(twoNonAtomicOneAtomic)); assertMethodCalls(getParent(getParent(twoNonAtomicOneAtomic))); listener.onUninstalled(oneNonAtomicOneAtomicOneNonAtomic); assertMethodCalls(oneNonAtomicOneAtomicOneNonAtomic); assertMethodCalls(getParent(oneNonAtomicOneAtomicOneNonAtomic), Methods.UNINSTALL); assertMethodCalls(getParent(getParent(oneNonAtomicOneAtomicOneNonAtomic))); } private StubInstallArtifact getParent(StubInstallArtifact artifact) { GraphNode<InstallArtifact> tree = artifact.getGraph(); if (tree != null) { List<GraphNode<InstallArtifact>> parents = tree.getParents(); // TODO DAG: when the Tree is generalised to a DAG, this testcase can assume the DAG is still a tree in structure because the testcase constructs the tree. // TODO DAG Test case uses tree. -> get first parent. if (!parents.isEmpty()) { GraphNode<InstallArtifact> parent = tree.getParents().get(0); return (StubInstallArtifact) parent.getValue(); } } return null; } private void assertMethodCalls(StubInstallArtifact artifact, Methods... methods) { List<Methods> calledMethods = Arrays.asList(methods); if (calledMethods.contains(Methods.START)) { assertTrue(artifact.getStartCalled()); } else { assertFalse(artifact.getStartCalled()); } if (calledMethods.contains(Methods.STOP)) { assertTrue(artifact.getStopCalled()); } else { assertFalse(artifact.getStopCalled()); } if (calledMethods.contains(Methods.UNINSTALL)) { assertTrue(artifact.getUninstallCalled()); } else { assertFalse(artifact.getUninstallCalled()); } } /** * Create a chain (linear tree) behind a list of values: * * <pre> * makeChain([a,b,c]) * </pre> * * produces * * <pre> * (a)->(b)->(c) * </pre> * * and returns <code><em>c</em></code>, the value of the last node (with tree attached). * * @param installArtifactArray array of values to be in nodes of chain tree * @return value (with tree attached) at last leaf of chain tree */ private final static StubInstallArtifact makeChain(StubInstallArtifact... installArtifactArray) { DirectedAcyclicGraph<InstallArtifact> dag = new ThreadSafeDirectedAcyclicGraph<InstallArtifact>(); GraphNode<InstallArtifact> graph = null; for (StubInstallArtifact installArtifact : installArtifactArray) { GraphNode<InstallArtifact> leaf = dag.createRootNode(installArtifact); if (graph == null) { graph = leaf; } else { graph.addChild(leaf); graph = leaf; } installArtifact.setGraph(graph); } if(graph == null){ return null; } return (StubInstallArtifact) graph.getValue(); } private static class StubInstallArtifact implements PlanInstallArtifact { private final boolean atomic; private volatile GraphNode<InstallArtifact> graph; private volatile boolean startCalled = false; private volatile boolean stopCalled = false; private volatile boolean uninstallCalled = false; public StubInstallArtifact(boolean atomic) { this.atomic = atomic; } public boolean getStartCalled() { return startCalled; } public boolean getStopCalled() { return stopCalled; } public boolean getUninstallCalled() { return uninstallCalled; } public GraphNode<InstallArtifact> getGraph() { return this.graph; } public void setGraph(GraphNode<InstallArtifact> graph) { this.graph = graph; } public void stop() throws DeploymentException { this.stopCalled = true; } public void start() throws DeploymentException { start(null); } public void start(AbortableSignal signal) throws DeploymentException { this.startCalled = true; } public void uninstall() throws DeploymentException { this.uninstallCalled = true; } public boolean isAtomic() { return this.atomic; } public ArtifactFS getArtifactFS() { throw new UnsupportedOperationException(); } public String getName() { throw new UnsupportedOperationException(); } public String getRepositoryName() { throw new UnsupportedOperationException(); } public State getState() { throw new UnsupportedOperationException(); } public String getType() { throw new UnsupportedOperationException(); } public Version getVersion() { throw new UnsupportedOperationException(); } public boolean refresh() { throw new UnsupportedOperationException(); } public List<ArtifactSpecification> getArtifactSpecifications() { throw new UnsupportedOperationException(); } public boolean isScoped() { throw new UnsupportedOperationException(); } public boolean refresh(String symbolicName) throws DeploymentException { throw new UnsupportedOperationException(); } public boolean refreshScope() { throw new UnsupportedOperationException(); } public String getProperty(String name) { throw new UnsupportedOperationException(); } public Set<String> getPropertyNames() { throw new UnsupportedOperationException(); } public String setProperty(String name, String value) { throw new UnsupportedOperationException(); } public String getScopeName() { return null; } public Provisioning getProvisioning() { throw new UnsupportedOperationException(); } } }