| /******************************************************************************* |
| * Copyright (c) 2006, 2015 IBM Corporation 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: |
| * Stefan Xenos - initial API and implementation |
| * Stefan Xenos - bug 174539 - add a 1-argument convert(...) method |
| * Stefan Xenos - bug 174040 - SubMonitor#convert doesn't always set task name |
| * Stefan Xenos - bug 206942 - Regression test for infinite progress reporting rate |
| * IBM Corporation - bug 252446 - SubMonitor.newChild passes zero ticks to child |
| * Alexander Kurtakov <akurtako@redhat.com> - bug 458490 |
| *******************************************************************************/ |
| package org.eclipse.core.tests.runtime; |
| |
| import java.util.*; |
| import junit.framework.TestCase; |
| import org.eclipse.core.runtime.*; |
| import org.junit.Assert; |
| |
| /** |
| * |
| */ |
| public class SubMonitorTest extends TestCase { |
| |
| private long startTime; |
| /** |
| * <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 = SubProgressTest.PROGRESS_SIZE; |
| /** |
| * <p>Depth of the chain chain of progress monitors. In all of the tests, we create |
| * a nested chain of progress monitors rather 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 = SubProgressTest.CHAIN_DEPTH; |
| |
| public SubMonitorTest() { |
| super(); |
| } |
| |
| public SubMonitorTest(String name) { |
| super(name); |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| startTime = System.currentTimeMillis(); |
| super.setUp(); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| long endTime = System.currentTimeMillis(); |
| reportPerformance(getClass().getName(), getName(), startTime, endTime); |
| super.tearDown(); |
| } |
| |
| /** |
| * Reports progress by iterating over a loop of the given size, reporting 1 progress |
| * at each iteration. Simulates the progress of worked(int) in loops. |
| * |
| * @param monitor progress monitor (callers are responsible for calling done() if necessary) |
| * @param loopSize size of the loop |
| */ |
| private static void reportWorkInLoop(IProgressMonitor monitor, int loopSize) { |
| monitor.beginTask("", loopSize); |
| for (int i = 0; i < loopSize; i++) { |
| monitor.worked(1); |
| } |
| } |
| |
| /** |
| * Reports progress by iterating over a loop of the given size, reporting 1 progress |
| * at each iteration. Simulates the progress of internalWorked(double) in loops. |
| * |
| * @param monitor progress monitor (callers are responsible for calling done() if necessary) |
| * @param loopSize size of the loop |
| */ |
| private static void reportFloatingPointWorkInLoop(IProgressMonitor monitor, int loopSize) { |
| monitor.beginTask("", loopSize); |
| for (int i = 0; i < loopSize; i++) { |
| monitor.internalWorked(1.0d); |
| } |
| } |
| |
| /** |
| * Runs an "infinite progress" loop. Each iteration will consume 1/ratio |
| * of the remaining work, and will run for the given number of iterations. |
| * Retuns the number of ticks reported (out of 1000). |
| * |
| * @param ratio |
| * @return the number of ticks reported |
| */ |
| private double runInfiniteProgress(int ratio, int iterations) { |
| TestProgressMonitor monitor = new TestProgressMonitor(); |
| SubMonitor mon = SubMonitor.convert(monitor); |
| |
| for (int i = 0; i < iterations; i++) { |
| mon.setWorkRemaining(ratio); |
| mon.worked(1); |
| } |
| |
| return monitor.getTotalWork(); |
| } |
| |
| public void testInfiniteProgress() { |
| // In theory when reporting "infinite" progress, the actual progress reported after |
| // n iterations should be f(n) = T(1-(1-R)^n) |
| // |
| // where T is the total ticks allocated on the root (T=1000) and R is the ratio |
| // (R=1/the_argument_to_setWorkRemaining). |
| |
| // Reporting 1% per iteration, we should be at 993.4 ticks after 500 iterations |
| double test1 = runInfiniteProgress(100, 500); |
| assertEquals(993.4, test1, 1.0); |
| |
| // Reporting 0.1% per iteration, we should be at 950.2 ticks after 3000 iterations |
| double test2 = runInfiniteProgress(1000, 3000); |
| assertEquals(950.2, test2, 1.0); |
| |
| // Reporting 0.01% per iteration, we should be at 393.5 ticks after 5000 iterations |
| double test3 = runInfiniteProgress(10000, 5000); |
| assertEquals(393.5, test3, 1.0); |
| |
| // Reporting 0.01% per iteration, we should be at 864.7 ticks after 20000 iterations |
| double test4 = runInfiniteProgress(10000, 20000); |
| assertEquals(864.7, test4, 1.0); |
| |
| // Reporting 0.01% per iteration, we should be at 9.9 ticks after 100 iterations |
| double test5 = runInfiniteProgress(10000, 100); |
| assertEquals(9.9, test5, 1.0); |
| } |
| |
| /** |
| * Ensures that we don't lose any progress when calling setWorkRemaining |
| */ |
| public void testSetWorkRemaining() { |
| TestProgressMonitor monitor = new TestProgressMonitor(); |
| SubMonitor mon = SubMonitor.convert(monitor, 0); |
| |
| for (int i = 1000; i >= 0; i--) { |
| mon.setWorkRemaining(i); |
| mon.internalWorked(0.5); |
| |
| mon.setWorkRemaining(i); |
| mon.internalWorked(0.5); |
| |
| mon.internalWorked(-0.5); // should not affect progress |
| } |
| |
| monitor.done(); |
| monitor.assertOptimal(); |
| } |
| |
| /** |
| * Tests that SubMonitor.done() will clean up after an unconsumed child |
| * that was created with the explicit constructor |
| */ |
| public void testCleanupConstructedChildren() { |
| TestProgressMonitor top = new TestProgressMonitor(); |
| |
| SubMonitor monitor = SubMonitor.convert(top, 1000); |
| monitor.beginTask("", 1000); |
| |
| monitor.newChild(500); |
| SubMonitor child2 = monitor.newChild(100); |
| |
| child2.done(); |
| |
| Assert.assertEquals("Ensure that done() reports unconsumed progress, even if beginTask wasn't called", 600.0, top.getTotalWork(), 0.01d); |
| |
| SubMonitor child3 = monitor.newChild(100); |
| |
| SubMonitor child3andAHalf = monitor.newChild(-10); // should not affect progress |
| child3andAHalf.done(); |
| |
| monitor.done(); |
| |
| Assert.assertEquals("Ensure that done() cleans up after unconsumed children that were created by their constructor", 1000.0, top.getTotalWork(), 0.01d); |
| |
| child3.worked(100); |
| |
| Assert.assertEquals("Ensure that children can't report any progress if their parent has completed", 1000.0, top.getTotalWork(), 0.01d); |
| } |
| |
| /** |
| * Tests SubMonitor under typical usage. This is the same |
| * as the performance test as the same name, but it verifies correctness |
| * rather than performance. |
| */ |
| public void testTypicalUsage() { |
| TestProgressMonitor monitor = new TestProgressMonitor(); |
| SubMonitorTest.runTestTypicalUsage(monitor); |
| monitor.assertOptimal(); |
| } |
| |
| /** |
| * Tests creating a tree of SubMonitors. This is the same |
| * as the performance test as the same name, but it verifies correctness |
| * rather than performance. |
| */ |
| public void testCreateTree() { |
| TestProgressMonitor monitor = new TestProgressMonitor(); |
| SubMonitorTest.runTestCreateTree(monitor); |
| monitor.assertOptimal(); |
| } |
| |
| /** |
| * Tests claimed problem reported in bug 2100394. |
| */ |
| public void testBug210394() { |
| TestProgressMonitor testMonitor = new TestProgressMonitor(); |
| SubMonitor monitor = SubMonitor.convert(testMonitor); |
| monitor.beginTask("", 2); |
| |
| SubMonitor step1 = monitor.newChild(1); |
| step1.done(); |
| |
| assertEquals(500.0, testMonitor.getTotalWork(), 1.0); |
| |
| SubMonitor step2 = monitor.newChild(2); |
| // Here we find out that we had really 5 additional steps to accomplish |
| SubMonitor subStep2 = SubMonitor.convert(step2, 5); |
| subStep2.worked(1); |
| assertEquals(600.0, testMonitor.getTotalWork(), 1.0); |
| subStep2.worked(1); |
| assertEquals(700.0, testMonitor.getTotalWork(), 1.0); |
| subStep2.worked(1); |
| assertEquals(800.0, testMonitor.getTotalWork(), 1.0); |
| subStep2.worked(1); |
| assertEquals(900.0, testMonitor.getTotalWork(), 1.0); |
| subStep2.worked(1); |
| assertEquals(1000.0, testMonitor.getTotalWork(), 1.0); |
| } |
| |
| /** |
| * Ensures that SubMonitor won't report more than 100% progress |
| * when a child is created with more than the amount of available progress. |
| */ |
| public void testChildOverflow() { |
| TestProgressMonitor top = new TestProgressMonitor(); |
| |
| SubMonitor mon1 = SubMonitor.convert(top, 1000); |
| Assert.assertEquals(0.0, top.getTotalWork(), 0.1d); |
| |
| SubMonitor child2 = mon1.newChild(700); |
| child2.done(); |
| |
| Assert.assertEquals(700.0, top.getTotalWork(), 0.1d); |
| |
| SubMonitor child3 = mon1.newChild(700); |
| child3.done(); |
| |
| Assert.assertEquals("The reported work should not exceed 1000", 1000.0, top.getTotalWork(), 0.1d); |
| |
| mon1.done(); |
| |
| top.done(); |
| } |
| |
| /** |
| * Tests the 1-argument convert(...) method |
| */ |
| public void testConvert() { |
| TestProgressMonitor top = new TestProgressMonitor(); |
| SubMonitor mon1 = SubMonitor.convert(top); |
| Assert.assertEquals(0.0, top.getTotalWork(), 0.1d); |
| mon1.worked(10); |
| Assert.assertEquals(0.0, top.getTotalWork(), 0.1d); |
| mon1.setWorkRemaining(100); |
| mon1.worked(50); |
| Assert.assertEquals(500.0, top.getTotalWork(), 0.1d); |
| mon1.done(); |
| Assert.assertEquals(1000.0, top.getTotalWork(), 0.1d); |
| top.done(); |
| } |
| |
| /** |
| * Tests the function of the SUPPRESS_* flags |
| */ |
| public void testFlags() { |
| TestProgressMonitor top = new TestProgressMonitor(); |
| |
| SubMonitor mon1 = SubMonitor.convert(top, "initial", 100); |
| |
| // Ensure that we've called begintask on the root with the correct argument |
| Assert.assertEquals(top.getBeginTaskCalls(), 1); |
| Assert.assertEquals(top.getBeginTaskName(), "initial"); |
| |
| mon1.beginTask("beginTask", 1000); |
| |
| // Ensure that beginTask on the child does NOT result in more than 1 call to beginTask on the root |
| Assert.assertEquals(top.getBeginTaskCalls(), 1); |
| |
| // Ensure that the task name was propogated correctly |
| Assert.assertEquals(top.getTaskName(), "beginTask"); |
| |
| mon1.setTaskName("setTaskName"); |
| Assert.assertEquals(top.getTaskName(), "setTaskName"); |
| |
| mon1.subTask("subTask"); |
| Assert.assertEquals(top.getSubTaskName(), "subTask"); |
| |
| // Create a child monitor that permits calls to beginTask |
| { |
| SubMonitor mon2 = mon1.newChild(10, SubMonitor.SUPPRESS_NONE); |
| |
| // Ensure that everything is propogated |
| mon2.beginTask("mon2.beginTask", 100); |
| Assert.assertEquals(top.getTaskName(), "mon2.beginTask"); |
| |
| mon2.setTaskName("mon2.setTaskName"); |
| Assert.assertEquals(top.getTaskName(), "mon2.setTaskName"); |
| |
| mon2.subTask("mon2.subTask"); |
| Assert.assertEquals(top.getSubTaskName(), "mon2.subTask"); |
| } |
| } |
| |
| 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()]); |
| } |
| |
| public void testSplitPerformsAutoCancel() { |
| NullProgressMonitor npm = new NullProgressMonitor(); |
| npm.setCanceled(true); |
| SubMonitor subMonitor = SubMonitor.convert(npm, 1000); |
| try { |
| subMonitor.split(500); |
| fail("split should have thrown an exception"); |
| } catch (OperationCanceledException exception) { |
| } |
| } |
| |
| public void testNewChildDoesNotAutoCancel() { |
| NullProgressMonitor npm = new NullProgressMonitor(); |
| npm.setCanceled(true); |
| SubMonitor subMonitor = SubMonitor.convert(npm, 1000); |
| subMonitor.newChild(500); |
| } |
| |
| public void testSplitDoesNotThrowExceptionIfParentNotCanceled() { |
| NullProgressMonitor npm = new NullProgressMonitor(); |
| SubMonitor subMonitor = SubMonitor.convert(npm, 1000); |
| subMonitor.split(500); |
| } |
| |
| public void testAutoCancelDoesNothingForTrivialConversions() { |
| NullProgressMonitor npm = new NullProgressMonitor(); |
| npm.setCanceled(true); |
| SubMonitor subMonitor = SubMonitor.convert(npm, 1000); |
| subMonitor.split(1000); |
| } |
| |
| public void testAutoCancelDoesNothingForSingleTrivialOperation() { |
| NullProgressMonitor npm = new NullProgressMonitor(); |
| npm.setCanceled(true); |
| SubMonitor subMonitor = SubMonitor.convert(npm, 1000); |
| subMonitor.split(0); |
| } |
| |
| public void testAutoCancelThrowsExceptionEventuallyForManyTrivialOperations() { |
| NullProgressMonitor npm = new NullProgressMonitor(); |
| npm.setCanceled(true); |
| SubMonitor subMonitor = SubMonitor.convert(npm, 1000); |
| try { |
| for (int counter = 0; counter < 1000; counter++) { |
| subMonitor.split(0); |
| } |
| fail("split should have thrown an exception"); |
| } catch (OperationCanceledException exception) { |
| } |
| } |
| |
| public void testConsumingEndOfMonitorNotTreatedAsTrivial() { |
| NullProgressMonitor npm = new NullProgressMonitor(); |
| SubMonitor subMonitor = SubMonitor.convert(npm, 1000); |
| subMonitor.newChild(500); |
| try { |
| npm.setCanceled(true); |
| subMonitor.split(500); |
| fail("split should have thrown an exception"); |
| } catch (OperationCanceledException exception) { |
| } |
| } |
| |
| /** |
| * Tests the style bits in SubProgressMonitor |
| */ |
| public void testStyles() { |
| |
| int[] styles = new int[] {SubMonitor.SUPPRESS_NONE, SubMonitor.SUPPRESS_BEGINTASK, SubMonitor.SUPPRESS_SETTASKNAME, SubMonitor.SUPPRESS_SUBTASK, SubMonitor.SUPPRESS_BEGINTASK | SubMonitor.SUPPRESS_SETTASKNAME, SubMonitor.SUPPRESS_BEGINTASK | SubMonitor.SUPPRESS_SUBTASK, SubMonitor.SUPPRESS_SETTASKNAME | SubMonitor.SUPPRESS_SUBTASK, SubMonitor.SUPPRESS_ALL_LABELS}; |
| |
| HashMap<String, String[]> expected = new HashMap<>(); |
| expected.put("style 5 below style 7", new String[] {"", "", ""}); |
| expected.put("style 7 below style 5", new String[] {"beginTask0", "", "beginTask0"}); |
| expected.put("style 7 below style 4", new String[] {"beginTask0", "subTask0", "beginTask0"}); |
| expected.put("style 5 below style 6", new String[] {"", "subTask0", ""}); |
| expected.put("style 3 below style 7", new String[] {"", "", ""}); |
| expected.put("style 5 below style 5", new String[] {"beginTask0", "", "beginTask0"}); |
| expected.put("style 7 below style 3", new String[] {"setTaskName0", "", "setTaskName0"}); |
| expected.put("style 7 below style 2", new String[] {"setTaskName0", "subTask0", "setTaskName0"}); |
| expected.put("style 5 below style 4", new String[] {"beginTask0", "subTask0", "beginTask0"}); |
| expected.put("style 3 below style 6", new String[] {"", "subTask0", ""}); |
| expected.put("style 1 below style 7", new String[] {"", "", ""}); |
| expected.put("style 3 below style 5", new String[] {"beginTask0", "", "beginTask0"}); |
| expected.put("style 5 below style 3", new String[] {"beginTask1", "", "beginTask1"}); |
| expected.put("style 7 below style 1", new String[] {"setTaskName0", "", "setTaskName0"}); |
| expected.put("style 3 below style 4", new String[] {"beginTask0", "subTask0", "beginTask0"}); |
| expected.put("style 5 below style 2", new String[] {"beginTask1", "subTask0", "beginTask1"}); |
| expected.put("style 7 below style 0", new String[] {"setTaskName0", "subTask0", "setTaskName0"}); |
| expected.put("style 1 below style 6", new String[] {"", "subTask0", ""}); |
| expected.put("style 1 below style 5", new String[] {"beginTask0", "", "beginTask0"}); |
| expected.put("style 3 below style 3", new String[] {"setTaskName0", "", "setTaskName1"}); |
| expected.put("style 5 below style 1", new String[] {"beginTask1", "", "beginTask1"}); |
| expected.put("style 1 below style 4", new String[] {"beginTask0", "subTask0", "beginTask0"}); |
| expected.put("style 3 below style 2", new String[] {"setTaskName0", "subTask0", "setTaskName1"}); |
| expected.put("style 5 below style 0", new String[] {"beginTask1", "subTask0", "beginTask1"}); |
| expected.put("style 1 below style 3", new String[] {"beginTask1", "", "setTaskName1"}); |
| expected.put("style 3 below style 1", new String[] {"setTaskName0", "", "setTaskName1"}); |
| expected.put("style 1 below style 2", new String[] {"beginTask1", "subTask0", "setTaskName1"}); |
| expected.put("style 3 below style 0", new String[] {"setTaskName0", "subTask0", "setTaskName1"}); |
| expected.put("style 1 below style 1", new String[] {"beginTask1", "", "setTaskName1"}); |
| expected.put("style 1 below style 0", new String[] {"beginTask1", "subTask0", "setTaskName1"}); |
| expected.put("style 3 as top-level monitor", new String[] {"", "", "setTaskName0"}); |
| expected.put("style 7 as top-level monitor", new String[] {"", "", ""}); |
| expected.put("style 2 as top-level monitor", new String[] {"", "subTask0", "setTaskName0"}); |
| expected.put("style 6 as top-level monitor", new String[] {"", "subTask0", ""}); |
| expected.put("style 6 below style 7", new String[] {"", "", ""}); |
| expected.put("style 6 below style 6", new String[] {"", "subTask1", ""}); |
| expected.put("style 4 below style 7", new String[] {"", "", ""}); |
| expected.put("style 6 below style 5", new String[] {"beginTask0", "", "beginTask0"}); |
| expected.put("style 6 below style 4", new String[] {"beginTask0", "subTask1", "beginTask0"}); |
| expected.put("style 4 below style 6", new String[] {"", "subTask1", ""}); |
| expected.put("style 2 below style 7", new String[] {"", "", ""}); |
| expected.put("style 4 below style 5", new String[] {"beginTask0", "", "beginTask0"}); |
| expected.put("style 6 below style 3", new String[] {"setTaskName0", "", "setTaskName0"}); |
| expected.put("style 4 below style 4", new String[] {"beginTask0", "subTask1", "beginTask0"}); |
| expected.put("style 6 below style 2", new String[] {"setTaskName0", "subTask1", "setTaskName0"}); |
| expected.put("style 2 below style 6", new String[] {"", "subTask1", ""}); |
| expected.put("style 0 below style 7", new String[] {"", "", ""}); |
| expected.put("style 2 below style 5", new String[] {"beginTask0", "", "beginTask0"}); |
| expected.put("style 6 below style 1", new String[] {"setTaskName0", "", "setTaskName0"}); |
| expected.put("style 4 below style 3", new String[] {"beginTask1", "", "beginTask1"}); |
| expected.put("style 2 below style 4", new String[] {"beginTask0", "subTask1", "beginTask0"}); |
| expected.put("style 6 below style 0", new String[] {"setTaskName0", "subTask1", "setTaskName0"}); |
| expected.put("style 4 below style 2", new String[] {"beginTask1", "subTask1", "beginTask1"}); |
| expected.put("style 0 below style 6", new String[] {"", "subTask1", ""}); |
| expected.put("style 0 below style 5", new String[] {"beginTask0", "", "beginTask0"}); |
| expected.put("style 4 below style 1", new String[] {"beginTask1", "", "beginTask1"}); |
| expected.put("style 2 below style 3", new String[] {"setTaskName0", "", "setTaskName1"}); |
| expected.put("style 0 below style 4", new String[] {"beginTask0", "subTask1", "beginTask0"}); |
| expected.put("style 4 below style 0", new String[] {"beginTask1", "subTask1", "beginTask1"}); |
| expected.put("style 2 below style 2", new String[] {"setTaskName0", "subTask1", "setTaskName1"}); |
| expected.put("style 1 as top-level monitor", new String[] {"beginTask0", "", "setTaskName0"}); |
| expected.put("style 2 below style 1", new String[] {"setTaskName0", "", "setTaskName1"}); |
| expected.put("style 0 below style 3", new String[] {"beginTask1", "", "setTaskName1"}); |
| expected.put("style 2 below style 0", new String[] {"setTaskName0", "subTask1", "setTaskName1"}); |
| expected.put("style 0 below style 2", new String[] {"beginTask1", "subTask1", "setTaskName1"}); |
| expected.put("style 0 below style 1", new String[] {"beginTask1", "", "setTaskName1"}); |
| expected.put("style 0 below style 0", new String[] {"beginTask1", "subTask1", "setTaskName1"}); |
| expected.put("style 5 as top-level monitor", new String[] {"beginTask0", "", "beginTask0"}); |
| expected.put("style 0 as top-level monitor", new String[] {"beginTask0", "subTask0", "setTaskName0"}); |
| expected.put("style 4 as top-level monitor", new String[] {"beginTask0", "subTask0", "beginTask0"}); |
| expected.put("style 7 below style 7", new String[] {"", "", ""}); |
| expected.put("style 7 below style 6", new String[] {"", "subTask0", ""}); |
| HashMap<String, String[]> results = new HashMap<>(); |
| |
| for (int i = 0; i < styles.length; i++) { |
| int style = styles[i]; |
| { |
| TestProgressMonitor top = new TestProgressMonitor(); |
| top.beginTask("", 100); |
| SubMonitor converted = SubMonitor.convert(top, 100); |
| |
| SubMonitor styled = converted.newChild(100, style); |
| styled.setWorkRemaining(100); |
| |
| String testName = "style " + style + " as top-level monitor"; |
| results.put(testName, runChildTest(0, top, styled, 100 * styles.length)); |
| } |
| |
| for (int j = 0; j < styles.length; j++) { |
| int innerStyle = styles[j]; |
| |
| TestProgressMonitor newTop = new TestProgressMonitor(); |
| newTop.beginTask("", 100); |
| SubMonitor newConverted = SubMonitor.convert(newTop, 100); |
| |
| SubMonitor innerStyled = newConverted.newChild(100, style); |
| |
| runChildTest(0, newTop, innerStyled, 100); |
| |
| SubMonitor innerChild = innerStyled.newChild(100, innerStyle); |
| String testName = "style " + innerStyle + " below style " + style; |
| results.put(testName, runChildTest(1, newTop, innerChild, 100)); |
| innerChild.done(); |
| } |
| } |
| |
| String failure = null; |
| // 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()); |
| if (expectedResult == null) { |
| expectedResult = new String[0]; |
| } |
| |
| String[] value = entry.getValue(); |
| if (compareArray(value, expectedResult)) { |
| continue; |
| } |
| |
| System.out.print("expected.put(\"" + entry.getKey() + "\", new String[] {"); |
| failure = entry.getKey(); |
| String list = concatArray(value); |
| System.out.println(list + "});"); |
| } |
| |
| if (failure != null) { |
| Assert.assertEquals(failure, concatArray(expected.get(failure)), concatArray(results.get(failure))); |
| } |
| } |
| |
| private boolean compareArray(String[] value, String[] expectedResult) { |
| if (value == null || expectedResult == null) { |
| return value == null && expectedResult == null; |
| } |
| |
| if (value.length != expectedResult.length) { |
| return false; |
| } |
| for (int i = 0; i < expectedResult.length; i++) { |
| String next = expectedResult[i]; |
| if (!next.equals(value[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private String concatArray(String[] value) { |
| if (value == null) { |
| return ""; |
| } |
| |
| StringBuffer buf = new StringBuffer(); |
| boolean isFirst = true; |
| for (int i = 0; i < value.length; i++) { |
| String nextValue = value[i]; |
| if (!isFirst) { |
| buf.append(", "); |
| } |
| isFirst = false; |
| buf.append("\"" + nextValue + "\""); |
| } |
| String list = buf.toString(); |
| return list; |
| } |
| |
| /** |
| * Ensures that SubMonitor doesn't propogate redundant progress to its parent. |
| */ |
| public void testRedundantWork() { |
| TestProgressMonitor top = new TestProgressMonitor(); |
| |
| SubMonitor monitor = SubMonitor.convert(top, 10000); |
| for (int i = 0; i < 10000; i++) { |
| monitor.setTaskName("Task name"); |
| monitor.subTask("Subtask"); |
| monitor.worked(0); |
| monitor.internalWorked(0.0); |
| |
| // Report some real work |
| monitor.worked(1); |
| } |
| |
| top.done(); |
| top.assertOptimal(); |
| } |
| |
| public void testCancellation() { |
| TestProgressMonitor root = new TestProgressMonitor(); |
| |
| SubMonitor spm = SubMonitor.convert(root, 1000); |
| |
| // Test that changes at the root propogate to the child |
| root.setCanceled(true); |
| Assert.assertTrue(spm.isCanceled()); |
| root.setCanceled(false); |
| Assert.assertFalse(spm.isCanceled()); |
| |
| // Test that changes to the child propogate to the root |
| spm.setCanceled(true); |
| Assert.assertTrue(root.isCanceled()); |
| spm.setCanceled(false); |
| Assert.assertFalse(root.isCanceled()); |
| |
| // Test a chain of depth 2 |
| |
| SubMonitor spm2 = spm.newChild(1000); |
| |
| // Test that changes at the root propogate to the child |
| root.setCanceled(true); |
| Assert.assertTrue(spm2.isCanceled()); |
| root.setCanceled(false); |
| Assert.assertFalse(spm2.isCanceled()); |
| |
| // Test that changes to the child propogate to the root |
| spm2.setCanceled(true); |
| Assert.assertTrue(root.isCanceled()); |
| spm2.setCanceled(false); |
| Assert.assertFalse(root.isCanceled()); |
| } |
| |
| public void testNullParent() { |
| // Touch everything in the public API to ensure we don't throw an NPE |
| SubMonitor mon = SubMonitor.convert(null, 1000); |
| mon.setWorkRemaining(500); |
| mon.worked(250); |
| mon.newChild(200); |
| |
| mon.internalWorked(50.0); |
| Assert.assertFalse(mon.isCanceled()); |
| mon.setCanceled(true); |
| Assert.assertTrue(mon.isCanceled()); |
| mon.subTask("subtask"); |
| mon.setTaskName("taskname"); |
| mon.done(); |
| } |
| |
| /** |
| * Tests the automatic cleanup when progress monitors are created via their constructor |
| */ |
| public void testNewChild() { |
| TestProgressMonitor top = new TestProgressMonitor(); |
| SubMonitor mon = SubMonitor.convert(top, 1000); |
| |
| Assert.assertEquals("Ensure no work has been reported yet", 0.0, top.getTotalWork(), 0.01d); |
| |
| mon.newChild(100); |
| |
| Assert.assertEquals("Ensure no work has been reported yet", 0.0, top.getTotalWork(), 0.01d); |
| |
| mon.newChild(200); |
| |
| Assert.assertEquals("Ensure monitor1 was collected", 100.0, top.getTotalWork(), 0.01d); |
| |
| // The following behavior is necessary to make it possible to pass multiple progress monitors as |
| // arguments to the same method. |
| Assert.assertEquals("Monitor2 should not have been collected yet (when the public constructor is used, collection should happen when beginTask() or setWorkRemaining() is called.", 100.0, top.getTotalWork(), 0.01d); |
| |
| SubMonitor monitor4 = mon.newChild(300); |
| |
| Assert.assertEquals("Now monitor2 should be collected", 300.0, top.getTotalWork(), 0.01d); |
| |
| monitor4.done(); |
| |
| Assert.assertEquals("Now monitor4 should be collected", 600.0, top.getTotalWork(), 0.01d); |
| |
| mon.newChild(10); |
| |
| Assert.assertEquals("Creating a child when there are no active children should not report any work", 600.0, top.getTotalWork(), 0.01d); |
| |
| mon.worked(20); |
| |
| // Test for bug 210394 |
| Assert.assertEquals("Reporting work should cause the active child to be destroyed", 630.0, top.getTotalWork(), 0.01d); |
| |
| mon.newChild(10); |
| |
| Assert.assertEquals("monitor5 should have been cleaned up", 630.0, top.getTotalWork(), 0.01d); |
| |
| mon.internalWorked(60); |
| |
| Assert.assertEquals("Calling internalWorked should clean up active children", 700.0, top.getTotalWork(), 0.01d); |
| |
| // Now create a chain of undisposed children |
| SubMonitor monitor7 = mon.newChild(100); |
| |
| SubMonitor monitor8 = monitor7.newChild(40); |
| |
| monitor8.newChild(10); |
| |
| mon.done(); |
| |
| Assert.assertEquals("Calling done should clean up unused work", 1000.0, top.getTotalWork(), 0.01d); |
| } |
| |
| /** |
| * 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. |
| */ |
| public void testCreateChildrenUnderCustomParent() { |
| TestProgressMonitor monitor = new TestProgressMonitor(); |
| createChildrenUnderParent(monitor, SubMonitorTest.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. |
| |
| Assert.assertTrue(monitor.getRedundantWorkCalls() == 0); |
| Assert.assertTrue(monitor.getWorkCalls() >= 100); |
| } |
| |
| /** |
| * Creates a chain of n nested progress monitors. Calls beginTask on all monitors |
| * except for the innermost one. |
| * |
| * @param parent |
| * @param depth |
| * @return the innermost SubMonitor |
| */ |
| public static SubMonitor createSubProgressChain(SubMonitor parent, int depth) { |
| depth--; |
| parent.beginTask("", 100); |
| SubMonitor current = parent; |
| while (depth > 0) { |
| current.setWorkRemaining(100); |
| current = current.newChild(100); |
| depth--; |
| } |
| return current; |
| } |
| |
| /** |
| * Creates a balanced binary tree of progress monitors, without calling worked. Tests |
| * progress monitor creation and cleanup time, and ensures that excess progress is |
| * being collected when IProgressMonitor.done() is called. |
| * |
| * @param monitor progress monitor (callers are responsible for calling done() if necessary) |
| * @param loopSize total size of the recursion tree |
| */ |
| public static void createBalancedTree(IProgressMonitor parent, int loopSize) { |
| SubMonitor monitor = SubMonitor.convert(parent, 100); |
| int leftBranch = loopSize / 2; |
| int rightBranch = loopSize - leftBranch; |
| |
| if (leftBranch > 1) { |
| createBalancedTree(monitor.newChild(50), leftBranch); |
| } |
| |
| if (rightBranch > 1) { |
| createBalancedTree(monitor.newChild(50), rightBranch); |
| } |
| } |
| |
| /** |
| * <p>The innermost loop for the create tree test. We make this a static method so |
| * that it can be used both in this performance test and in the correctness test.</p> |
| * |
| * <p>The performance test ensures that it is fast to create a lot of progress monitors.</p> |
| * |
| * <p>The correctness test ensures that creating and destroying SubMonitors |
| * is enough to report progress, even if worked(int) and worked(double) are never called</p> |
| */ |
| public static void runTestCreateTree(IProgressMonitor monitor) { |
| SubMonitor progress = SubMonitor.convert(monitor, 100); |
| SubMonitor nestedMonitor = SubMonitorTest.createSubProgressChain(progress, SubMonitorTest.CHAIN_DEPTH); |
| |
| SubMonitorTest.createBalancedTree(nestedMonitor, SubMonitorTest.PROGRESS_SIZE); |
| |
| progress.done(); |
| monitor.done(); |
| } |
| |
| /** |
| * Reports progress by creating a balanced binary tree of progress monitors. Simulates |
| * mixed usage of IProgressMonitor in a typical usage. Calls isCanceled once each time work |
| * is reported. Half of the work is reported using internalWorked and half is reported using worked, |
| * to simulate mixed usage of the progress monitor. |
| * |
| * @param monitor progress monitor (callers are responsible for calling done() if necessary) |
| * @param loopSize total size of the recursion tree |
| */ |
| public static void reportWorkInBalancedTree(IProgressMonitor parent, int loopSize) { |
| SubMonitor monitor = SubMonitor.convert(parent, 100); |
| int leftBranch = loopSize / 2; |
| int rightBranch = loopSize - leftBranch; |
| |
| if (leftBranch > 1) { |
| reportWorkInBalancedTree(monitor.newChild(50), leftBranch); |
| } else { |
| monitor.worked(25); |
| monitor.internalWorked(25.0); |
| monitor.isCanceled(); |
| } |
| |
| if (rightBranch > 1) { |
| reportWorkInBalancedTree(monitor.newChild(50), rightBranch); |
| } else { |
| monitor.worked(25); |
| monitor.internalWorked(25.0); |
| monitor.isCanceled(); |
| } |
| } |
| |
| /** |
| * The innermost loop for the recursion test. We make this a static method so |
| * that it can be used both in this performance test and in the correctness test. |
| */ |
| public static void runTestTypicalUsage(IProgressMonitor monitor) { |
| SubMonitor progress = SubMonitor.convert(monitor, 100); |
| SubMonitor nestedMonitor = SubMonitorTest.createSubProgressChain(progress, SubMonitorTest.CHAIN_DEPTH); |
| |
| SubMonitorTest.reportWorkInBalancedTree(nestedMonitor, SubMonitorTest.PROGRESS_SIZE); |
| |
| progress.done(); |
| monitor.done(); |
| } |
| |
| /** |
| * Tests SubMonitor.worked. This is the same |
| * as the performance test as the same name, but it verifies correctness |
| * rather than performance. |
| */ |
| public void testWorked() { |
| TestProgressMonitor monitor = new TestProgressMonitor(); |
| SubMonitor progress = SubMonitor.convert(monitor, 100); |
| SubMonitor nestedMonitor = createSubProgressChain(progress, SubProgressTest.CHAIN_DEPTH); |
| |
| reportWorkInLoop(nestedMonitor, SubProgressTest.PROGRESS_SIZE); |
| |
| progress.done(); |
| monitor.done(); |
| |
| monitor.assertOptimal(); |
| } |
| |
| /** |
| * Tests SubMonitor.worked. This is the same |
| * as the performance test as the same name, but it verifies correctness |
| * rather than performance. |
| */ |
| public void testInternalWorked() { |
| TestProgressMonitor monitor = new TestProgressMonitor(); |
| SubMonitor progress = SubMonitor.convert(monitor, 100); |
| SubMonitor nestedMonitor = createSubProgressChain(progress, SubProgressTest.CHAIN_DEPTH); |
| |
| reportFloatingPointWorkInLoop(nestedMonitor, SubProgressTest.PROGRESS_SIZE); |
| |
| progress.done(); |
| monitor.done(); |
| |
| monitor.assertOptimal(); |
| } |
| |
| /** |
| * Tests reporting of progress by sub-monitors created via newChild() |
| */ |
| public void testBug252446() { |
| int children = 12; |
| int cyclesPerChild = 17; |
| |
| TestProgressMonitor monitor = new TestProgressMonitor(); |
| SubMonitor progress = SubMonitor.convert(monitor, children * cyclesPerChild); |
| |
| // At this time monitor.getExpectedWork() == SubMonitor.MINIMUM_RESOLUTION == 1000 |
| double expectedTicksPerIteration = (double) monitor.getExpectedWork() / children / cyclesPerChild; |
| |
| for (int i = 0; i < children; i++) { |
| IProgressMonitor mon = progress.newChild(cyclesPerChild); |
| for (int j = 1; j <= cyclesPerChild; j++) { |
| mon.worked(1); |
| double expectedTopMonitorWork = expectedTicksPerIteration * (i * cyclesPerChild + j); |
| // Progress is passed to the parent monitor as integer leading to rounding |
| // errors. The parent's progress has to follow child's progress "close enough" |
| // and then it will catch up when next child is created. Hence, a relatively large delta |
| // value in this check: |
| assertEquals(expectedTopMonitorWork, monitor.getTotalWork(), 2.0d); |
| } |
| } |
| monitor.done(); |
| monitor.assertOptimal(); |
| } |
| |
| /** |
| * Creates and destroys the given number of child progress monitors under the given parent. |
| * |
| * @param parent monitor to create children under. The caller must call done on this monitor |
| * if necessary. |
| * @param progressSize total number of children to create. |
| */ |
| private static void createChildrenUnderParent(IProgressMonitor parent, int progressSize) { |
| SubMonitor monitor = SubMonitor.convert(parent, progressSize); |
| |
| for (int count = 0; count < progressSize; count++) { |
| SubMonitor mon = monitor.newChild(1); |
| mon.beginTask("", 100); |
| } |
| } |
| |
| static public void reportPerformance(String className, String methodName, long startTime, long endTime) { |
| // enable to see performance results for the progress monitors |
| // System.out.println(className + "#" + methodName + " elapsed time: " + (endTime - startTime) / 1000.0d + "s"); |
| } |
| |
| } |