blob: 64f905cac822a6367209eaf915e34b3835928a40 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 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
* Alexander Kurtakov <akurtako@redhat.com> - bug 458490
*******************************************************************************/
package org.eclipse.equinox.common.tests;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@SuppressWarnings("deprecation")
public class SubProgressTest {
/**
* <p>Depth of the chain chain of progress monitors. In all of the tests, we create
* a nested chain of progress monitors rathar than a single monitor, to test its
* scalability under recursion. We pick a number representing a moderately deep
* recursion, but is still small enough that it could correspond to a real call stack
* without causing overflow.</p>
*
* <p>Note: changing this constant will invalidate comparisons with old performance data.</p>
*/
public static final int CHAIN_DEPTH = 100;
/**
* <p>Number of calls to worked() within each test. This was chosen to be significantly larger
* than 1000 to test how well the monitor can optimize unnecessary resolution
* in reported progress, but small enough that the test completes in a reasonable
* amount of time.</p>
*
* <p>Note: changing this constant will invalidate comparisons with old performance data.</p>
*/
public static final int PROGRESS_SIZE = 100000;
@Rule
public TestName name = new TestName();
/**
* Tests the style bits in SubProgressMonitor
* @deprecated to suppress deprecation warnings
*/
@Deprecated
@Test
public void testStyles() {
int[] styles = new int[] {0, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK | SubProgressMonitor.SUPPRESS_SUBTASK_LABEL};
HashMap<String, String[]> expected = new HashMap<>();
expected.put("style 0 below style 2", new String[] {"setTaskName0", "", "setTaskName1"});
expected.put("style 2 below style 0", new String[] {"setTaskName1", "beginTask1 ", "setTaskName1"});
expected.put("style 6 below style 0", new String[] {"setTaskName1", "beginTask1 ", "setTaskName1"});
expected.put("style 2 below style 4", new String[] {"setTaskName1", "beginTask0 beginTask1 ", "setTaskName1"});
expected.put("style 0 below style 0", new String[] {"setTaskName0", "subTask1", "setTaskName1"});
expected.put("style 6 as top-level monitor", new String[] {"", "", "setTaskName0"});
expected.put("style 6 below style 2", new String[] {"setTaskName1", "", "setTaskName1"});
expected.put("style 6 below style 6", new String[] {"setTaskName1", "", "setTaskName1"});
expected.put("style 0 below style 6", new String[] {"setTaskName0", "", "setTaskName1"});
expected.put("style 4 below style 2", new String[] {"setTaskName1", "", "setTaskName1"});
expected.put("style 0 as top-level monitor", new String[] {"", "subTask0", "setTaskName0"});
expected.put("style 0 below style 4", new String[] {"setTaskName0", "beginTask0 subTask1", "setTaskName1"});
expected.put("style 4 below style 0", new String[] {"setTaskName1", "beginTask1 subTask1", "setTaskName1"});
expected.put("style 4 as top-level monitor", new String[] {"", "beginTask0 subTask0", "setTaskName0"});
expected.put("style 2 below style 6", new String[] {"setTaskName1", "", "setTaskName1"});
expected.put("style 4 below style 6", new String[] {"setTaskName1", "", "setTaskName1"});
expected.put("style 2 below style 2", new String[] {"setTaskName1", "", "setTaskName1"});
expected.put("style 2 as top-level monitor", new String[] {"", "", "setTaskName0"});
expected.put("style 6 below style 4", new String[] {"setTaskName1", "beginTask0 beginTask1 ", "setTaskName1"});
expected.put("style 4 below style 4", new String[] {"setTaskName1", "beginTask0 beginTask1 subTask1", "setTaskName1"});
HashMap<String, String[]> results = new HashMap<>();
for (int style : styles) {
TestProgressMonitor top = new TestProgressMonitor();
top.beginTask("", 100);
SubProgressMonitor child = new SubProgressMonitor(top, 100, style);
String testName = "style " + style + " as top-level monitor";
results.put(testName, runChildTest(0, top, child, 100 * styles.length));
for (int innerStyle : styles) {
SubProgressMonitor innerChild = new SubProgressMonitor(child, 100, innerStyle);
testName = "style " + innerStyle + " below style " + style;
results.put(testName, runChildTest(1, top, innerChild, 100));
innerChild.done();
}
child.done();
}
// Output the code for the observed results, in case one of them has changed intentionally
for (Map.Entry<String, String[]> entry : results.entrySet()) {
String[] expectedResult = expected.get(entry.getKey());
String[] value = entry.getValue();
assertArrayEquals(value, expectedResult);
}
}
private String[] runChildTest(int depth, TestProgressMonitor root, IProgressMonitor child, int ticks) {
ArrayList<String> results = new ArrayList<>();
child.beginTask("beginTask" + depth, ticks);
results.add(root.getTaskName());
child.subTask("subTask" + depth);
results.add(root.getSubTaskName());
child.setTaskName("setTaskName" + depth);
results.add(root.getTaskName());
return results.toArray(new String[results.size()]);
}
/**
* Tests SubProgressMonitor nesting when using the default constructor. (Tests
* parents in floating point mode)
* @deprecated to suppress deprecation warnings
*/
@Deprecated
@Test
public void testConstructorNestingFP() {
TestProgressMonitor top = new TestProgressMonitor();
top.beginTask("", 2000);
// Create an SPM, put it in floating-point mode, and consume half its work
SubProgressMonitor fpMonitor = new SubProgressMonitor(top, 1000);
fpMonitor.beginTask("", 100);
fpMonitor.internalWorked(50.0);
fpMonitor.internalWorked(-10.0); // should have no effect
assertEquals(500.0, top.getTotalWork(), 0.01d);
// Create a child monitor, and ensure that it grabs the correct amount of work
// from the parent.
SubProgressMonitor childMonitor = new SubProgressMonitor(fpMonitor, 20);
childMonitor.beginTask("", 100);
childMonitor.worked(100);
childMonitor.done();
assertEquals(700.0, top.getTotalWork(), 0.01d);
// Create a child monitor, and ensure that it grabs the correct amount of work
// from the parent.
SubProgressMonitor childMonitor2 = new SubProgressMonitor(fpMonitor, 30);
childMonitor2.beginTask("", 100);
childMonitor2.worked(100);
childMonitor2.done();
assertEquals(1000.0, top.getTotalWork(), 0.01d);
// Ensure that creating another child will have no effect
SubProgressMonitor childMonitor3 = new SubProgressMonitor(fpMonitor, 10);
childMonitor3.beginTask("", 100);
childMonitor3.worked(100);
childMonitor3.done();
assertEquals(1000.0, top.getTotalWork(), 0.01d);
fpMonitor.worked(100);
assertEquals(1000.0, top.getTotalWork(), 0.01d);
fpMonitor.done();
assertEquals(1000.0, top.getTotalWork(), 0.01d);
}
/**
* Tests SubProgressMonitor nesting when using the default constructor. Tests constructors
* in int mode.
* @deprecated to suppress deprecation warnings
*/
@Deprecated
@Test
public void testConstructorNestingInt() {
TestProgressMonitor top = new TestProgressMonitor();
top.beginTask("", 2000);
// Create an SPM leave it in int mode, and consume half its work
SubProgressMonitor fpMonitor = new SubProgressMonitor(top, 1000);
fpMonitor.beginTask("", 100);
fpMonitor.worked(50);
assertEquals(500.0, top.getTotalWork(), 0.01d);
// Create a child monitor, and ensure that it grabs the correct amount of work
// from the parent.
SubProgressMonitor childMonitor = new SubProgressMonitor(fpMonitor, 20);
childMonitor.beginTask("", 100);
childMonitor.worked(100);
childMonitor.done();
assertEquals(700.0, top.getTotalWork(), 0.01d);
// Create a child monitor, and ensure that it grabs the correct amount of work
// from the parent.
SubProgressMonitor childMonitor2 = new SubProgressMonitor(fpMonitor, 30);
childMonitor2.beginTask("", 100);
childMonitor2.worked(100);
childMonitor2.done();
assertEquals(1000.0, top.getTotalWork(), 0.01d);
// Ensure that creating another child will have no effect
SubProgressMonitor childMonitor3 = new SubProgressMonitor(fpMonitor, 10);
childMonitor3.beginTask("", 100);
childMonitor3.worked(100);
childMonitor3.done();
assertEquals(1000.0, top.getTotalWork(), 0.01d);
fpMonitor.worked(100);
assertEquals(1000.0, top.getTotalWork(), 0.01d);
fpMonitor.done();
assertEquals(1000.0, top.getTotalWork(), 0.01d);
}
/**
* Tests the automatic cleanup when progress monitors are created via their constructor
* @deprecated to suppress deprecation warnings
*/
@Deprecated
@Test
public void testParallelChildren() {
TestProgressMonitor top = new TestProgressMonitor();
top.beginTask("", 1000);
SubProgressMonitor mon = new SubProgressMonitor(top, 1000);
mon.beginTask("", 1000);
SubProgressMonitor monitor1 = new SubProgressMonitor(mon, 200);
SubProgressMonitor monitor2 = new SubProgressMonitor(mon, 200);
assertEquals("Ensure no work has been reported yet", 0.0, top.getTotalWork(), 0.01d);
monitor1.beginTask("", 1000);
assertEquals("Ensure no work has been reported yet", 0.0, top.getTotalWork(), 0.01d);
monitor2.beginTask("", 1000);
assertEquals("Should not have cleaned up monitor 1", 0.0, top.getTotalWork(), 0.01d);
monitor1.done();
assertEquals("Should have cleaned up monitor 1", 200.0, top.getTotalWork(), 0.01d);
monitor1.worked(1000);
assertEquals("Monitor1 shouldn't report work once it's complete", 200.0, top.getTotalWork(), 0.01d);
monitor2.worked(500);
assertEquals(300.0, top.getTotalWork(), 0.01d);
// Create a monitor that will leak - monitors won't be auto-completed until their done methods are
// called
SubProgressMonitor monitor3 = new SubProgressMonitor(mon, 300);
assertEquals("Monitor2 should not have been cleaned up yet", 300.0, top.getTotalWork(), 0.01d);
SubProgressMonitor monitor4 = new SubProgressMonitor(mon, 300);
monitor4.beginTask("", 100);
mon.done();
Assert.assertNotNull(monitor3);
assertEquals("All leaked work should have been collected", 1000.0, top.getTotalWork(), 0.01d);
}
/**
* @deprecated to suppress deprecation warnings
*/
@Deprecated
@Test
public void testCancellation() {
TestProgressMonitor root = new TestProgressMonitor();
root.beginTask("", 1000);
SubProgressMonitor spm = new SubProgressMonitor(root, 1000);
// Test that changes at the root propogate to the child
root.setCanceled(true);
assertTrue(spm.isCanceled());
root.setCanceled(false);
assertFalse(spm.isCanceled());
// Test that changes to the child propogate to the root
spm.setCanceled(true);
assertTrue(root.isCanceled());
spm.setCanceled(false);
assertFalse(root.isCanceled());
// Test a chain of depth 2
spm.beginTask("", 1000);
SubProgressMonitor spm2 = new SubProgressMonitor(spm, 1000);
// Test that changes at the root propogate to the child
root.setCanceled(true);
assertTrue(spm2.isCanceled());
root.setCanceled(false);
assertFalse(spm2.isCanceled());
// Test that changes to the child propogate to the root
spm2.setCanceled(true);
assertTrue(root.isCanceled());
spm2.setCanceled(false);
assertFalse(root.isCanceled());
}
/**
* Tests creating progress monitors under a custom progress monitor parent. This
* is the same as the performance test as the same name, but it verifies
* correctness rather than performance.
*/
@Test
public void testCreateChildrenUnderCustomParent() {
TestProgressMonitor monitor = new TestProgressMonitor();
createChildrenUnderParent(monitor, SubProgressTest.PROGRESS_SIZE);
// We don't actually expect the progress to be optimal in this case since the progress monitor wouldn't
// know what it was rooted under and would have had to report more progress than necessary... but we
// should be able to check that there was no redundancy.
assertEquals(0, monitor.getRedundantWorkCalls());
assertTrue(monitor.getWorkCalls() >= 100);
}
/**
* Creates and destroys the given number of child progress monitors under the given parent.
*
* @param monitor monitor to create children under. The caller must call done on this monitor
* if necessary.
* @param progressSize total number of children to create.
*
* @deprecated to suppress deprecation warnings
*/
@Deprecated
private static void createChildrenUnderParent(IProgressMonitor monitor, int progressSize) {
monitor.beginTask("", progressSize);
for (int count = 0; count < progressSize; count++) {
SubProgressMonitor mon = new SubProgressMonitor(monitor, 1);
mon.beginTask("", 100);
mon.done();
}
}
/**
* Test SubProgressMonitor's created with negative a work value.
*/
@Test
public void testNegativeWorkValues() {
TestProgressMonitor top = new TestProgressMonitor();
top.beginTask("", 10);
SubProgressMonitor childMonitor = new SubProgressMonitor(top, IProgressMonitor.UNKNOWN); // -1
childMonitor.beginTask("", 10);
childMonitor.worked(5);
assertEquals(0.0, top.getTotalWork(), 0.01d);
childMonitor.done();
assertEquals(0.0, top.getTotalWork(), 0.01d);
top.done();
}
}