blob: 66aa84dee12482b6e1870237dad48b94adfe4126 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2015 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
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 474274
******************************************************************************/
package org.eclipse.e4.core.internal.tests.contexts;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.e4.core.contexts.ContextFunction;
import org.eclipse.e4.core.contexts.EclipseContextFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.contexts.RunAndTrack;
import org.eclipse.e4.core.internal.tests.CoreTestsActivator;
import org.junit.After;
import org.junit.Test;
/**
* Tests for {@link org.eclipse.e4.core.RunAndTrack.context.IRunAndTrack}.
*/
public class RunAndTrackTest {
private static final class TestRAT extends RunAndTrack {
private String varName;
private Object varValue;
private int calls = 0;
public TestRAT(String varName) {
this.varName = varName;
}
@Override
public boolean changed(IEclipseContext context) {
++calls;
varValue = context.get(varName);
return true;
}
public int getCalls() {
return calls;
}
public Object getVarValue() {
return varValue;
}
public void resetCalls() {
calls = 0;
}
}
private static class ActivePartLookupFunction extends ContextFunction {
@Override
public Object compute(IEclipseContext context, String contextKey) {
IEclipseContext childContext = (IEclipseContext) context.getLocal(ACTIVE_CHILD);
if (childContext != null) {
return childContext.get(ACTIVE_PART);
}
return context.get(INTERNAL_LOCAL_PART);
}
}
static final String ACTIVE_CHILD = "activeChild";
static final String ACTIVE_PART = "activePart";
static final String ACTIVE_PART_ID = "activePartId";
static final String INTERNAL_LOCAL_PART = "localPart";
private List<IEclipseContext> createdContexts = new ArrayList<>();
private IEclipseContext createContext(IEclipseContext parentContext, String level) {
IEclipseContext childContext = parentContext.createChild(level);
createdContexts.add(childContext);
return childContext;
}
private IEclipseContext getGlobalContext() {
IEclipseContext serviceContext = EclipseContextFactory
.getServiceContext(CoreTestsActivator.getDefault().getBundleContext());
// global initialization and setup, usually done by workbench
IEclipseContext appContext = createContext(serviceContext, "globalContext");
appContext.set("globalContext", appContext);
return appContext;
}
private IEclipseContext[] createNextLevel(IEclipseContext parent, String prefix, int num) {
assertTrue(num > 0);
IEclipseContext[] contexts = new IEclipseContext[num];
for (int i = 0; i < num; i++) {
contexts[i] = createContext(parent, prefix + i);
contexts[i].set(INTERNAL_LOCAL_PART, prefix + i);
}
parent.set(ACTIVE_CHILD, contexts[0]);
return contexts;
}
@After
public void tearDown() throws Exception {
for (IEclipseContext context : createdContexts) {
context.dispose();
}
createdContexts.clear();
}
@Test
public void testActiveChain() throws Exception {
final IEclipseContext workbenchContext = getGlobalContext();
workbenchContext.set("activePart", new ActivePartLookupFunction());
final IEclipseContext[] windows = createNextLevel(workbenchContext, "window", 1);
createNextLevel(windows[0], "part", 2);
assertEquals("part0", workbenchContext.get(ACTIVE_PART));
}
@Test
public void testActiveChange() throws Exception {
final IEclipseContext workbenchContext = getGlobalContext();
workbenchContext.set("activePart", new ActivePartLookupFunction());
final IEclipseContext[] windows = createNextLevel(workbenchContext, "window", 1);
final IEclipseContext[] parts = createNextLevel(windows[0], "part", 2);
assertEquals("part0", workbenchContext.get(ACTIVE_PART));
windows[0].set(ACTIVE_CHILD, parts[1]);
assertEquals("part1", workbenchContext.get(ACTIVE_PART));
}
/**
* There was a failing scenario in the legacy workbench support. This captures the hierarchy and
* function (without any workbench level references). It should be updated when we figure out
* the failing scenario :-)
*
* @throws Exception
*/
@Test
public void testRunAndTrackComplex() throws Exception {
final IEclipseContext workbenchContext = getGlobalContext();
workbenchContext.set("activePart", new ActivePartLookupFunction());
final IEclipseContext[] windows = createNextLevel(workbenchContext, "window", 1);
windows[0].runAndTrack(new RunAndTrack() {
@Override
public boolean changed(IEclipseContext context) {
final Object part = windows[0].get(ACTIVE_PART);
windows[0].set(ACTIVE_PART_ID, part);
return true;
}
@Override
public String toString() {
return ACTIVE_PART_ID;
}
});
final IEclipseContext[] mainSashes = createNextLevel(windows[0], "mainSash", 2);
createNextLevel(mainSashes[1], "editorArea", 1);
final IEclipseContext[] viewSashes = createNextLevel(mainSashes[0], "viewSashes", 2);
// create package explorer stack
final IEclipseContext[] packageStack = createNextLevel(viewSashes[0], "packageStack", 1);
final IEclipseContext[] packageViews = createNextLevel(packageStack[0], "packageViews", 3);
assertNotNull(packageViews);
assertEquals("packageViews0", windows[0].get(ACTIVE_PART));
assertEquals("packageViews0", windows[0].get(ACTIVE_PART_ID));
// create problems stack
final IEclipseContext[] problemsStack = createNextLevel(viewSashes[1], "problemsStack", 1);
final IEclipseContext[] problemsViews = createNextLevel(problemsStack[0], "problemViews", 5);
assertNotNull(problemsViews);
assertEquals("packageViews0", windows[0].get(ACTIVE_PART));
assertEquals("packageViews0", windows[0].get(ACTIVE_PART_ID));
assertEquals("problemViews0", problemsStack[0].get(ACTIVE_PART));
// this won't change since it is a "runAndTrack" at the window context
// level
assertEquals("packageViews0", problemsStack[0].get(ACTIVE_PART_ID));
// set the "problems view" active, propagating the information up
// the active chain.
problemsStack[0].set(ACTIVE_CHILD, problemsViews[0]);
viewSashes[1].set(ACTIVE_CHILD, problemsStack[0]);
mainSashes[0].set(ACTIVE_CHILD, viewSashes[1]);
windows[0].set(ACTIVE_CHILD, mainSashes[0]);
workbenchContext.set(ACTIVE_CHILD, windows[0]);
assertEquals("problemViews0", windows[0].get(ACTIVE_PART));
assertEquals("problemViews0", windows[0].get(ACTIVE_PART_ID));
assertEquals("packageViews0", packageStack[0].get(ACTIVE_PART));
assertEquals("problemViews0", packageStack[0].get(ACTIVE_PART_ID));
}
@Test
public void testRunAndTrackSimple() throws Exception {
final IEclipseContext workbenchContext = getGlobalContext();
workbenchContext.set("activePart", new ActivePartLookupFunction());
final IEclipseContext[] windows = createNextLevel(workbenchContext, "window", 1);
windows[0].runAndTrack(new RunAndTrack() {
@Override
public boolean changed(IEclipseContext context) {
final Object part = windows[0].get(ACTIVE_PART);
windows[0].set(ACTIVE_PART_ID, part);
return true;
}
@Override
public String toString() {
return ACTIVE_PART_ID;
}
});
final IEclipseContext[] parts = createNextLevel(windows[0], "part", 2);
assertEquals("part0", workbenchContext.get(ACTIVE_PART));
assertEquals("part0", windows[0].get(ACTIVE_PART_ID));
windows[0].set(ACTIVE_CHILD, parts[1]);
assertEquals("part1", windows[0].get(ACTIVE_PART));
assertEquals("part1", windows[0].get(ACTIVE_PART_ID));
}
/**
* Test how a RAT responds to a change hidden from it; changed value is == to child value
*/
@Test
public void testSetHiddenValueToChildObject() {
final String newRootValue = "child";
doHiddenValueChangeTest(newRootValue);
}
/**
* Test how a RAT responds to a change hidden from it; changed value is != to child value
*/
@Test
public void testSetHiddenValueToDifferentObject() {
final String newRootValue = "other";
doHiddenValueChangeTest(newRootValue);
}
/**
* Test how a RAT responds to a change hidden from it; changed value is != to child value (but is .equals())
*/
@Test
public void testSetHiddenValueToObjectEqualToChild() {
// avoid compiler's pushing all my strings into a single string pool
final String newRootValue = new String("child");
doHiddenValueChangeTest(newRootValue);
}
/**
* Test how a RAT responds to a change hidden from it; changed value is == to root value
*/
@Test
public void testSetHiddenValueToRootObject() {
final String newRootValue = "root";
doHiddenValueChangeTest(newRootValue);
}
/**
* Test how a RAT responds to a change hidden from it; changed value is != to root value (but is .equals())
*/
@Test
public void testSetHiddenValueToEqualRootObject() {
// avoid compiler's pushing all my strings into a single string pool
final String newRootValue = new String("root");
doHiddenValueChangeTest(newRootValue);
}
/**
* Test how a RAT responds to a change hidden from it; changed value is == to root value
*/
@Test
public void testSetHiddenValueToNull() {
final String newRootValue = null;
doHiddenValueChangeTest(newRootValue);
}
/**
* Perform a hidden value test that verifies that the test RAT does not run, and
* that has last seen the initial value in the child context (namely "child").
* @param newRootValue the new value for the variable 'v' in the root context.
* @see #doHiddenValueChangeTest(ITestAction, Object, int)
*/
void doHiddenValueChangeTest(final String newRootValue) {
doHiddenValueChangeTest((IEclipseContext root, String var) -> {
root.set(var, newRootValue);
}, "child", 0);
}
/**
* Interface defining function
*
*/
private interface ITestAction {
void execute(IEclipseContext root, String var);
}
/**
* Create a two level hierarchy of contexts, each defining a variable 'v' with values 'root' and 'child', respectively.
* Create and install a RAT on the child context that is dependent on 'v'.
* Run <code>testAction</code>.
* Tests whether the RAT ran the expected number of times,
* and tests last value of 'v' that the RAT saw.
* @param testAction the context action to perform as part of the test
* @param expectedValue the expected last value of variable 'v' that the RAT saw.
* @param expectedRATCalls the expected number of times the RAT was run in response to testAction
*/
void doHiddenValueChangeTest(ITestAction testAction, Object expectedValue, int expectedRATCalls) {
final IEclipseContext root = getGlobalContext();
final IEclipseContext child = root.createChild("child");
root.set("v", "root");
child.set("v", "child");
final TestRAT testRAT = new TestRAT("v");
// install the RAT
child.runAndTrack(testRAT);
assertEquals("child", testRAT.getVarValue());
assertEquals(1, testRAT.getCalls());
testRAT.resetCalls();
// set the new root value
testAction.execute(root, "v");
assertEquals(expectedValue, testRAT.getVarValue());
assertEquals(expectedRATCalls, testRAT.getCalls());
}
/**
* Test that a variable change in a context hidden from a RAT in
* a child context does not re-run the RAT.
*/
@Test
public void testRemoveHiddenVariable() {
doHiddenValueChangeTest((IEclipseContext root, String var) -> {
root.remove(var);
}, "child", 0);
}
/**
* Test that setting a context variable to it's existing
* value does not re-run dependent RATs
*/
@Test
public void testSetContextVarToSameObject() {
doSingleContextChangeTest((IEclipseContext root, String var) -> {
root.set(var, "root");
}, "root", 0);
}
/**
* Test that setting a context variable to a value that {@link Object#equals(Object) equals}
* the current value, but is same object DOES re-run dependent RATs.
*/
@Test
public void testSetContextVarToEqualObject() {
doSingleContextChangeTest((IEclipseContext root, String var) -> {
root.set(var, new String("root"));
}, "root", 1);
}
/**
* Test that setting a context variable to a different object, not equal to the
* current value re-runs dependent RATs.
*/
@Test
public void testSetContextVarToOtherObject() {
doSingleContextChangeTest((IEclipseContext root, String var) -> {
root.set(var, "other");
}, "other", 1);
}
/**
* Test that removing a context variable re-runs dependent RATs.
*/
@Test
public void testRemoveContextVar() {
doSingleContextChangeTest((root, var) -> root.remove(var), null, 1);
}
/**
* Creates a context, sets a variable 'v' to "root", creates a RAT dependent on 'v' in the context,
* then executes <code>testAction</code> and tests whether the RAT ran the expected number of times,
* and tests last value of 'v' that the RAT saw.
* @param testAction the context action to perform as part of the test
* @param expectedValue the expected last value of variable 'v' that the RAT saw.
* @param expectedRATCalls the expected number of times the RAT was run in response to testAction
*/
private void doSingleContextChangeTest(ITestAction testAction, Object expectedValue, int expectedRATCalls) {
final IEclipseContext root = getGlobalContext();
root.set("v", "root");
final TestRAT testRAT = new TestRAT("v");
// install the RAT
root.runAndTrack(testRAT);
assertEquals("root", testRAT.getVarValue());
assertEquals(1, testRAT.getCalls());
testRAT.resetCalls();
testAction.execute(root, "v");
assertEquals(expectedRATCalls, testRAT.getCalls());
assertEquals(expectedValue, testRAT.getVarValue());
}
}