diff options
author | Julian Honnen | 2019-02-07 08:35:35 +0000 |
---|---|---|
committer | Julian Honnen | 2019-02-08 14:54:14 +0000 |
commit | f7e971a9181eb47b0d0d1fddd5dcd32c5fecb32a (patch) | |
tree | 22c6905bbc76c8e343959d62b130781d7d1b930e /bundles/org.eclipse.equinox.common.tests/src/org/eclipse | |
parent | 970375004be10a7ad99207b61bbc65ce01a294f4 (diff) | |
download | rt.equinox.bundles-f7e971a9181eb47b0d0d1fddd5dcd32c5fecb32a.tar.gz rt.equinox.bundles-f7e971a9181eb47b0d0d1fddd5dcd32c5fecb32a.tar.xz rt.equinox.bundles-f7e971a9181eb47b0d0d1fddd5dcd32c5fecb32a.zip |
Bug 263880 - moved equinox tests from core.tests.runtime
Moved tests from org.eclipse.core.tests.runtime that only required
minimal adaptations.
Change-Id: I6f742cd939269ba3cbc91d8a410a32be2b544c3c
Signed-off-by: Julian Honnen <julian.honnen@vector.com>
Diffstat (limited to 'bundles/org.eclipse.equinox.common.tests/src/org/eclipse')
15 files changed, 4053 insertions, 4 deletions
diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/AllTests.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/AllTests.java index 868cc12fc..3f9c9d143 100644 --- a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/AllTests.java +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/AllTests.java @@ -1,6 +1,5 @@ /******************************************************************************* - * Copyright (c) 1997-2009 by ProSyst Software GmbH - * http://www.prosyst.com + * Copyright (c) 2018 Julian Honnen * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,7 +9,7 @@ * SPDX-License-Identifier: EPL-2.0 * * Contributors: - * ProSyst Software GmbH - initial API and implementation + * Julian Honnen - initial API and implementation *******************************************************************************/ package org.eclipse.equinox.common.tests; @@ -19,7 +18,7 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses(StatusTest.class) +@SuiteClasses(RuntimeTests.class) public class AllTests { // intentionally left blank } diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/CoreExceptionTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/CoreExceptionTest.java new file mode 100644 index 000000000..9ea83d158 --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/CoreExceptionTest.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 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.equinox.common.tests; + +import org.eclipse.core.runtime.*; +import org.eclipse.core.tests.harness.CoreTest; + +/** + * Test cases for the Path class. + */ +public class CoreExceptionTest extends CoreTest { + /** + * Need a zero argument constructor to satisfy the test harness. + * This constructor should not do any real work nor should it be + * called by user code. + */ + public CoreExceptionTest() { + super(null); + } + + public CoreExceptionTest(String name) { + super(name); + } + + public void testCoreException() { + final String MESSAGE_STRING = "An exception has occurred"; + IStatus status = new Status(IStatus.ERROR, "org.eclipse.core.tests.runtime", 31415, MESSAGE_STRING, new NumberFormatException()); + + CoreException e = new CoreException(status); + + assertEquals("1.0", status, e.getStatus()); + assertEquals("1.1", MESSAGE_STRING, e.getMessage()); + } +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/OperationCanceledExceptionTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/OperationCanceledExceptionTest.java new file mode 100644 index 000000000..bcd677cd2 --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/OperationCanceledExceptionTest.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 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.equinox.common.tests; + +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.tests.harness.CoreTest; + +/** + * Test cases for the Path class. + */ +public class OperationCanceledExceptionTest extends CoreTest { + /** + * Need a zero argument constructor to satisfy the test harness. + * This constructor should not do any real work nor should it be + * called by user code. + */ + public OperationCanceledExceptionTest() { + super(null); + } + + public OperationCanceledExceptionTest(String name) { + super(name); + } + + public void testCoreException() { + final String MESSAGE_STRING = "An exception has occurred"; + OperationCanceledException e = new OperationCanceledException(MESSAGE_STRING); + + assertEquals("1.0", MESSAGE_STRING, e.getMessage()); + } +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/PathTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/PathTest.java new file mode 100644 index 000000000..f20625867 --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/PathTest.java @@ -0,0 +1,979 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 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 + * Patrick Tasse - Add extra constructor to Path class (bug 454959) + *******************************************************************************/ +package org.eclipse.equinox.common.tests; + +import java.util.ArrayList; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.tests.harness.CoreTest; + +/** + * Test cases for the Path class. + */ +public class PathTest extends CoreTest { + /** + * Need a zero argument constructor to satisfy the test harness. + * This constructor should not do any real work nor should it be + * called by user code. + */ + public PathTest() { + super(null); + } + + public PathTest(String name) { + super(name); + } + + public void testAddTrailingSeparator() { + + IPath with = new Path("/first/second/third/"); + IPath without = new Path("/first/second/third"); + + assertSame("1.0", with, with.addTrailingSeparator()); + assertEquals("1.1", with, without.addTrailingSeparator()); + assertTrue("1.2", without.equals(without.addTrailingSeparator())); + + assertSame("2.0", Path.ROOT, Path.ROOT.addTrailingSeparator()); + assertEquals("2.1", Path.ROOT, Path.EMPTY.addTrailingSeparator()); + + with = new Path("//first/second/third/"); + without = new Path("//first/second/third"); + + assertSame("3.0", with, with.addTrailingSeparator()); + assertEquals("3.1", with, without.addTrailingSeparator()); + assertTrue("3.2", without.equals(without.addTrailingSeparator())); + + assertSame("4.0", Path.ROOT, Path.ROOT.addTrailingSeparator()); + assertEquals("4.1", Path.ROOT, Path.EMPTY.addTrailingSeparator()); + + with = new Path("c:/first/second/third/"); + without = new Path("c:/first/second/third"); + + assertSame("5.0", with, with.addTrailingSeparator()); + assertEquals("5.1", with, without.addTrailingSeparator()); + assertTrue("5.2", without.equals(without.addTrailingSeparator())); + + assertSame("6.0", Path.ROOT, Path.ROOT.addTrailingSeparator()); + assertEquals("6.1", Path.ROOT, Path.EMPTY.addTrailingSeparator()); + } + + public void testAppend() { + + IPath fore = new Path("/first/second/third/"); + IPath win = Path.forWindows("/first/second/third/"); + IPath posix = Path.forPosix("/first/second/third/"); + String aftString = "/fourth/fifth"; + IPath aft = new Path(aftString); + IPath combo = new Path("/first/second/third/fourth/fifth"); + + assertEquals("1.0", combo, fore.append(aft)); + assertEquals("1.1", combo, fore.removeTrailingSeparator().append(aft)); + assertEquals("1.2", combo, Path.ROOT.append(fore).append(aft)); + assertTrue("1.3", !fore.append(aft).hasTrailingSeparator()); + assertTrue("1.4", !Path.ROOT.append(fore).append(aft).hasTrailingSeparator()); + assertTrue("1.5", !fore.removeTrailingSeparator().append(aft).hasTrailingSeparator()); + // append empty and root path together + assertEquals("1.6", Path.EMPTY, Path.EMPTY.append(Path.EMPTY)); + assertEquals("1.7", Path.EMPTY, Path.EMPTY.append(Path.ROOT)); + assertEquals("1.8", Path.ROOT, Path.ROOT.append(Path.EMPTY)); + assertEquals("1.9", Path.ROOT, Path.ROOT.append(Path.ROOT)); + + assertEquals("2.0", combo, fore.append(aftString)); + assertEquals("2.1", combo, fore.removeTrailingSeparator().append(aftString)); + assertEquals("2.2", combo, Path.ROOT.append(fore).append(aftString)); + assertTrue("2.3", !fore.append(aftString).hasTrailingSeparator()); + assertTrue("2.4", !Path.ROOT.append(fore).append(aftString).hasTrailingSeparator()); + assertTrue("2.5", !fore.removeTrailingSeparator().append(aftString).hasTrailingSeparator()); + + //ensure append preserves correct trailing separator + assertTrue("3.0", !fore.append("aft").hasTrailingSeparator()); + assertTrue("3.1", fore.append("aft/").hasTrailingSeparator()); + assertTrue("3.2", !fore.append("/aft").hasTrailingSeparator()); + assertTrue("3.3", fore.append("/aft/").hasTrailingSeparator()); + assertTrue("3.4", !fore.append("\\aft").hasTrailingSeparator()); + //backslash is a trailing separator on windows only + assertTrue("3.5.win", win.append("aft\\").hasTrailingSeparator()); + assertFalse("3.6.posix", posix.append("aft\\").hasTrailingSeparator()); + assertTrue("3.7", !fore.append("fourth/fifth").hasTrailingSeparator()); + assertTrue("3.8", fore.append("fourth/fifth/").hasTrailingSeparator()); + assertTrue("3.9", !fore.append(new Path("aft")).hasTrailingSeparator()); + assertTrue("3.10", fore.append(new Path("aft/")).hasTrailingSeparator()); + assertTrue("3.11", !fore.append(new Path("fourth/fifth")).hasTrailingSeparator()); + assertTrue("3.12", fore.append(new Path("fourth/fifth/")).hasTrailingSeparator()); + + //make sure append converts backslashes appropriately + aftString = "fourth\\fifth"; + assertEquals("4.0.win", combo, win.append(aftString)); + assertEquals("4.1.win", combo, win.removeTrailingSeparator().append(aftString)); + // append path to root path uses optimized code + assertEquals("4.2.win", combo, Path.forWindows("/").append(win).append(aftString)); + assertEquals("4.3.win", combo, Path.forWindows("/").append(posix).append(aftString)); + // append path to empty path uses optimized code + assertEquals("4.4.win", combo, Path.forWindows("").append(win).append(aftString).makeAbsolute()); + assertEquals("4.5.win", combo, Path.forWindows("").append(posix).append(aftString).makeAbsolute()); + + assertEquals("5.0", new Path("/foo"), Path.ROOT.append("../foo")); + assertEquals("5.1", new Path("/foo"), Path.ROOT.append("./foo")); + assertEquals("5.2", new Path("c:/foo/xyz"), new Path("c:/foo/bar").append("../xyz")); + assertEquals("5.3", new Path("c:/foo/bar/xyz"), new Path("c:/foo/bar").append("./xyz")); + + //append preserves device and leading separator of receiver + assertEquals("6.1.win", Path.forWindows("c:foo/bar"), Path.forWindows("c:").append("/foo/bar")); + assertEquals("6.2.win", Path.forWindows("c:foo/bar"), Path.forWindows("c:").append("foo/bar")); + assertEquals("6.3.win", Path.forWindows("c:/foo/bar"), Path.forWindows("c:/").append("/foo/bar")); + assertEquals("6.4.win", Path.forWindows("c:/foo/bar"), Path.forWindows("c:/").append("foo/bar")); + assertEquals("6.5.win", Path.forWindows("c:foo/bar"), Path.forWindows("c:").append("z:/foo/bar")); + assertEquals("6.6.win", Path.forWindows("c:foo/bar"), Path.forWindows("c:").append("z:foo/bar")); + assertEquals("6.7.win", Path.forWindows("c:/foo/bar"), Path.forWindows("c:/").append("z:/foo/bar")); + assertEquals("6.8.win", Path.forWindows("c:/foo/bar"), Path.forWindows("c:/").append("z:foo/bar")); + assertEquals("6.9.win", Path.forWindows("c:/foo"), Path.forWindows("c:/").append("z:foo")); + assertEquals("6.10.posix", Path.forPosix("c:/foo/bar"), Path.forPosix("c:").append("/foo/bar")); + assertEquals("6.11.posix", Path.forPosix("c:/foo/bar/"), Path.forPosix("c:").append("foo/bar/")); + assertEquals("6.12.posix", Path.forPosix("/c:/foo/bar"), Path.forPosix("/c:").append("/foo/bar")); + assertEquals("6.13.posix", Path.forPosix("/c:/foo/bar"), Path.forPosix("/c:").append("foo/bar")); + + assertEquals("6.14", new Path("foo/bar"), new Path("foo").append(new Path("/bar"))); + assertEquals("6.15", new Path("foo/bar"), new Path("foo").append(new Path("bar"))); + assertEquals("6.16", new Path("/foo/bar"), new Path("/foo/").append(new Path("/bar"))); + assertEquals("6.17", new Path("/foo/bar"), new Path("/foo/").append(new Path("bar"))); + + assertEquals("6.18", new Path("foo/bar/"), new Path("foo").append(new Path("/bar/"))); + assertEquals("6.19", new Path("foo/bar/"), new Path("foo").append(new Path("bar/"))); + assertEquals("6.20", new Path("/foo/bar/"), new Path("/foo/").append(new Path("/bar/"))); + assertEquals("6.21", new Path("/foo/bar/"), new Path("/foo/").append(new Path("bar/"))); + + //append preserves isUNC of receiver + assertEquals("7.0", new Path("/foo/bar"), new Path("/foo").append("//bar")); + assertEquals("7.1", new Path("/foo/bar/test"), new Path("/foo").append("bar//test")); + assertEquals("7.2", new Path("//foo/bar"), new Path("//foo").append("bar")); + assertEquals("7.3", new Path("/bar"), Path.ROOT.append("//bar")); + + //append empty path does nothing + assertEquals("8.0", fore, fore.append(Path.ROOT)); + assertEquals("8.1", fore, fore.append(Path.EMPTY)); + assertEquals("8.2", fore, fore.append(new Path("//"))); + assertEquals("8.3", fore, fore.append(new Path("/"))); + assertEquals("8.4", fore, fore.append(new Path(""))); + assertEquals("8.5", fore, fore.append("//")); + assertEquals("8.6", fore, fore.append("/")); + assertEquals("8.7", fore, fore.append("")); + assertEquals("8.8.win", win, win.append("c://")); + assertEquals("8.9.win", win, win.append("c:/")); + assertEquals("8.10.win", win, win.append("c:")); + + // append string respects and preserves the initial path's file system + IPath win1 = Path.forWindows("a/b"); + IPath win2 = win1.append("c:d\\e"); + assertEquals("9.1.win", "a/b/d/e", win2.toString()); + assertEquals("9.2.win", null, win2.getDevice()); + assertEquals("9.3.win", 4, win2.segmentCount()); + assertEquals("9.4.win", "d", win2.segment(2)); + assertFalse("9.5.win", win2.isValidSegment(":")); + IPath posix1 = Path.forPosix("a/b"); + IPath posix2 = posix1.append("c:d\\e"); + assertEquals("9.6.posix", "a/b/c:d\\e", posix2.toString()); + assertEquals("9.7.posix", null, posix2.getDevice()); + assertEquals("9.8.posix", 3, posix2.segmentCount()); + assertEquals("9.9.posix", "c:d\\e", posix2.segment(2)); + assertTrue("9.10.posix", posix2.isValidSegment(":")); + assertTrue("9.11", win1.equals(posix1)); + assertFalse("9.12", win2.equals(posix2)); + + // append path respects and preserves the initial path's file system + IPath win3 = win1.append(Path.forPosix("c/d/e")); + assertEquals("10.1.win", "a/b/c/d/e", win3.toString()); + assertEquals("10.2.win", null, win3.getDevice()); + assertEquals("10.3.win", 5, win3.segmentCount()); + assertEquals("10.4.win", "c", win3.segment(2)); + assertFalse("10.5.win", win3.isValidSegment(":")); + IPath posix3 = posix1.append(Path.forWindows("c\\d\\e")); + assertEquals("10.6.posix", "a/b/c/d/e", posix3.toString()); + assertEquals("10.7.posix", null, posix3.getDevice()); + assertEquals("10.8.posix", 5, posix3.segmentCount()); + assertEquals("10.9.posix", "c", posix3.segment(2)); + assertTrue("10.10.posix", posix3.isValidSegment(":")); + assertTrue("10.11", win3.equals(posix3)); + + // append POSIX path to Windows path may produce invalid segments + IPath win4 = win1.append(Path.forPosix("c:d\\e")); + assertEquals("11.1.win", "a/b/c:d\\e", win4.toString()); + assertEquals("11.2.win", null, win4.getDevice()); + assertEquals("11.3.win", 3, win4.segmentCount()); + assertEquals("11.4.win", "c:d\\e", win4.segment(2)); + // isValidPath() considers it as device 'a/b/c:' with segments {'d','e'} + assertTrue("11.5.win", win4.isValidPath(win4.toString())); + assertFalse("11.6.win", win4.isValidSegment(win4.segment(2))); + } + + public void testSegmentCount() { + + assertEquals("1.0", 0, Path.ROOT.segmentCount()); + assertEquals("1.1", 0, Path.EMPTY.segmentCount()); + + assertEquals("2.0", 1, new Path("/first").segmentCount()); + assertEquals("2.1", 1, new Path("/first/").segmentCount()); + assertEquals("2.2", 3, new Path("/first/second/third/").segmentCount()); + assertEquals("2.3", 3, new Path("/first/second/third").segmentCount()); + assertEquals("2.4", 5, new Path("/first/second/third/fourth/fifth").segmentCount()); + + assertEquals("3.0", 0, new Path("//").segmentCount()); + assertEquals("3.1", 1, new Path("//first").segmentCount()); + assertEquals("3.2", 1, new Path("//first/").segmentCount()); + assertEquals("3.3", 2, new Path("//first/second").segmentCount()); + assertEquals("3.4", 2, new Path("//first/second/").segmentCount()); + } + + public void testCanonicalize() { + // Test collapsing multiple separators + // double slashes at the beginning of a path + // are left and assumed to be a UNC path + assertEquals("//", new Path("///////").toString()); + assertEquals("/a/b/c", new Path("/a/b//c").toString()); + assertEquals("//a/b/c", new Path("//a/b//c").toString()); + assertEquals("a/b/c/", new Path("a/b//c//").toString()); + + // Test collapsing single dots + assertEquals("2.0", "/", new Path("/./././.").toString()); + assertEquals("2.1", "/a/b/c", new Path("/a/./././b/c").toString()); + assertEquals("2.2", "/a/b/c", new Path("/a/./b/c/.").toString()); + assertEquals("2.3", "a/b/c", new Path("a/./b/./c").toString()); + + // Test collapsing double dots + assertEquals("3.0", "/a/b", new Path("/a/b/c/..").toString()); + assertEquals("3.1", "/", new Path("/a/./b/../..").toString()); + assertEquals("3.2", "../", new Path("../").toString()); + // test bug 46043 - IPath collapseParentReferences + assertEquals("3.3", "../", new Path("./../").toString()); + assertEquals("3.4", "../", new Path(".././").toString()); + assertEquals("3.5", "..", new Path("./..").toString()); + assertEquals("3.6", ".", new Path(".").toString()); + } + + public void testClone() { + + IPath anyPath = new Path("/a/b/c"); + assertEquals("1.0", anyPath, anyPath.clone()); + anyPath = new Path("//a/b/c"); + assertEquals("1.1", anyPath, anyPath.clone()); + anyPath = new Path("c:/a/b/c"); + assertEquals("1.2", anyPath, anyPath.clone()); + + assertEquals("1.3", Path.ROOT, Path.ROOT.clone()); + } + + public void testConstructors() { + + assertEquals("1.0", "", new Path("").toString()); + assertEquals("1.1", "/", new Path("/").toString()); + assertEquals("1.2", "a", new Path("a").toString()); + assertEquals("1.3", "/a", new Path("/a").toString()); + assertEquals("1.4", "//", new Path("//").toString()); + assertEquals("1.5", "/a/", new Path("/a/").toString()); + assertEquals("1.6", "/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z", new Path("/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z").toString()); + assertEquals("1.7", "...", new Path("...").toString()); + assertEquals("1.8", "/a/b/.../c", new Path("/a/b/.../c").toString()); + + IPath anyPath = new Path("/first/second/third"); + + assertEquals("2.0", Path.EMPTY, new Path("")); + assertEquals("2.1", Path.ROOT, new Path("/")); + assertEquals("2.2", anyPath, anyPath); + + //should handle slash before the device (see bug 84697) + // fullPath = new java.io.File("D:\\foo\\abc.txt").toURL().getPath() + assertEquals("3.0.win", "D:/foo/abc.txt", Path.forWindows("/D:/foo/abc.txt").toString()); + // fullPath = new java.io.File("D:/").toURL().getPath() + assertEquals("3.1.win", "D:/", Path.forWindows("/D:/").toString()); + } + + public void testFactoryMethods() { + + IPath win = Path.forWindows("a:b\\c/d"); + assertEquals("1.1.win", "a:b/c/d", win.toString()); + assertEquals("1.2.win", "a:", win.getDevice()); + assertEquals("1.3.win", 3, win.segmentCount()); + assertEquals("1.4.win", "b", win.segment(0)); + + IPath posix = Path.forPosix("a:b\\c/d"); + assertEquals("2.5.posix", "a:b\\c/d", posix.toString()); + assertEquals("2.6.posix", null, posix.getDevice()); + assertEquals("2.7.posix", 2, posix.segmentCount()); + assertEquals("2.8.posix", "a:b\\c", posix.segment(0)); + + assertFalse("3.1", win.equals(posix)); + } + + public void testFirstSegment() { + + assertNull("1.0", Path.ROOT.segment(0)); + assertNull("1.1", Path.EMPTY.segment(0)); + + assertEquals("2.0", "a", new Path("/a/b/c").segment(0)); + assertEquals("2.1", "a", new Path("a").segment(0)); + assertEquals("2.2", "a", new Path("/a").segment(0)); + assertEquals("2.3", "a", new Path("a/b").segment(0)); + assertEquals("2.4", "a", new Path("//a/b").segment(0)); + assertEquals("2.5.win", "a", Path.forWindows("c:a/b").segment(0)); + assertEquals("2.6.win", "a", Path.forWindows("c:/a/b").segment(0)); + assertEquals("2.7.posix", "c:", Path.forPosix("c:/a/b").segment(0)); + assertEquals("2.8.posix", "c:", Path.forPosix("c:/a\\b").segment(0)); + assertEquals("2.9.posix", "a", Path.forPosix("a/c:/b").segment(0)); + assertEquals("2.10.posix", "a\\b", Path.forPosix("a\\b/b").segment(0)); + + } + + public void testFromPortableString() { + assertEquals("1.0", "", Path.fromPortableString("").toString()); + assertEquals("1.1", "/", Path.fromPortableString("/").toString()); + assertEquals("1.2", "a", Path.fromPortableString("a").toString()); + assertEquals("1.3", "/a", Path.fromPortableString("/a").toString()); + assertEquals("1.4", "//", Path.fromPortableString("//").toString()); + assertEquals("1.5", "/a/", Path.fromPortableString("/a/").toString()); + + assertEquals("2.1", "a:", Path.fromPortableString("a:").toString()); + assertEquals("2.2", "a:", Path.fromPortableString("a::").toString()); + assertEquals("2.3", "a:b:", Path.fromPortableString("a:b::").toString()); + assertEquals("2.4", "a/b:c", Path.fromPortableString("a/b::c").toString()); + assertEquals("2.5", "a/b:c", Path.fromPortableString("a/b:c").toString()); + assertEquals("2.6", "a:b", Path.fromPortableString("a::b").toString()); + + boolean isLocalPosix = java.io.File.separatorChar == '/'; + IPath win1 = Path.forWindows("a:b\\c/d"); + IPath win2 = Path.fromPortableString(win1.toPortableString()); + assertEquals("3.1.win", "a:b/c/d", win2.toString()); + assertEquals("3.2.win", "a:", win2.getDevice()); + assertEquals("3.3.win", 3, win2.segmentCount()); + assertEquals("3.4.win", "b", win2.segment(0)); + assertTrue("3.5.win", win1.equals(win2)); + assertEquals("3.6.win", isLocalPosix, win2.isValidSegment(":")); + IPath posix1 = Path.forPosix("a:b\\c/d"); + IPath posix2 = Path.fromPortableString(posix1.toPortableString()); + assertEquals("3.7.posix", "a:b\\c/d", posix2.toString()); + assertEquals("3.8.posix", null, posix2.getDevice()); + assertEquals("3.9.posix", 2, posix2.segmentCount()); + assertEquals("3.10.posix", "a:b\\c", posix2.segment(0)); + assertTrue("3.11.posix", posix1.equals(posix2)); + assertEquals("3.12.posix", isLocalPosix, posix2.isValidSegment(":")); + } + + public void testGetFileExtension() { + + IPath anyPath = new Path("index.html"); + assertEquals("1.0", anyPath.getFileExtension(), "html"); + + assertNull("2.0", Path.ROOT.getFileExtension()); + assertNull("2.1", Path.EMPTY.getFileExtension()); + assertNull("2.2", new Path("index").getFileExtension()); + assertNull("2.3", new Path("/a/b/c.txt/").getFileExtension()); + + assertEquals("3.0", "txt", new Path("/a/b/c.txt").getFileExtension()); + assertEquals("3.1", "txt", new Path("/a/b/c.foo.txt").getFileExtension()); + assertEquals("3.2", "txt", new Path("//a/b/c.foo.txt").getFileExtension()); + assertEquals("3.3", "txt", new Path("c:/a/b/c.foo.txt").getFileExtension()); + assertEquals("3.4", "txt", new Path("c:a/b/c.foo.txt").getFileExtension()); + + } + + public void testHasTrailingSeparator() { + + // positive + assertTrue("1.0", new Path("/first/second/third/").hasTrailingSeparator()); + assertTrue("1.1", new Path("//first/second/third/").hasTrailingSeparator()); + assertTrue("1.2", new Path("c:/first/second/third/").hasTrailingSeparator()); + assertTrue("1.3", new Path("c:first/second/third/").hasTrailingSeparator()); + + // negative + assertTrue("2.0", !new Path("first/second/third").hasTrailingSeparator()); + assertTrue("2.1", !Path.ROOT.hasTrailingSeparator()); + assertTrue("2.2", !Path.EMPTY.hasTrailingSeparator()); + assertTrue("2.3", !new Path("//first/second/third").hasTrailingSeparator()); + assertTrue("2.4", !new Path("c:/first/second/third").hasTrailingSeparator()); + assertTrue("2.5", !new Path("c:first/second/third").hasTrailingSeparator()); + + //paths of length 0 never have a trailing separator + assertTrue("3.0", !new Path("/first/").removeLastSegments(1).hasTrailingSeparator()); + assertTrue("3.1", !new Path("/first/").removeFirstSegments(1).hasTrailingSeparator()); + assertTrue("3.2", !new Path("/").hasTrailingSeparator()); + assertTrue("3.3", !new Path("/first/").append("..").hasTrailingSeparator()); + assertTrue("3.4", !new Path("/first/").append(new Path("..")).hasTrailingSeparator()); + assertTrue("3.5", !new Path("/first/../").hasTrailingSeparator()); + assertTrue("3.6", !Path.ROOT.addTrailingSeparator().hasTrailingSeparator()); + assertTrue("3.7", !Path.EMPTY.addTrailingSeparator().hasTrailingSeparator()); + + } + + public void testIsAbsolute() { + + // positive + assertTrue("1.0", new Path("/first/second/third").isAbsolute()); + assertTrue("1.1", Path.ROOT.isAbsolute()); + assertTrue("1.2", new Path("//first/second/third").isAbsolute()); + assertTrue("1.3.win", Path.forWindows("c:/first/second/third").isAbsolute()); + assertTrue("1.4.posix", Path.forPosix("/c:first/second/third").isAbsolute()); + + // negative + assertTrue("2.0", !new Path("first/second/third").isAbsolute()); + assertTrue("2.1", !Path.EMPTY.isAbsolute()); + assertTrue("2.2", !new Path("c:first/second/third").isAbsolute()); + + // unc + assertTrue("3.0.win", Path.forWindows("c://").isAbsolute()); + assertTrue("3.1.posix", Path.forPosix("//c:/").isAbsolute()); + assertTrue("3.2", new Path("//").isAbsolute()); + assertTrue("3.3", new Path("//a").isAbsolute()); + assertTrue("3.4", new Path("//a/b/").isAbsolute()); + + } + + public void testIsEmpty() { + + // positive + assertTrue("1.0", Path.EMPTY.isEmpty()); + assertTrue("1.1", new Path("//").isEmpty()); + assertTrue("1.2", new Path("").isEmpty()); + assertTrue("1.3.win", Path.forWindows("c:").isEmpty()); + assertFalse("1.4.posix", Path.forPosix("c:").isEmpty()); + assertTrue("1.5", new Path("///").isEmpty()); + + // negative + assertTrue("2.0", !new Path("first/second/third").isEmpty()); + assertTrue("2.1", !Path.ROOT.isEmpty()); + assertTrue("2.2", !new Path("//a").isEmpty()); + assertTrue("2.3", !new Path("c:/").isEmpty()); + } + + public void testIsPrefixOf() { + + IPath prefix = new Path("/first/second"); + IPath path = new Path("/first/second/third/fourth"); + + assertTrue("1.0", prefix.isPrefixOf(path)); + // test the case where the arg is longer than the receiver. + assertTrue("1.1", !path.isPrefixOf(prefix)); + assertTrue("1.2", !new Path("fifth/sixth").isPrefixOf(path)); + + assertTrue("2.0", prefix.addTrailingSeparator().isPrefixOf(path)); + + assertTrue("3.0", Path.ROOT.isPrefixOf(path)); + assertTrue("3.1", Path.EMPTY.isPrefixOf(path)); + assertTrue("3.2", !path.isPrefixOf(Path.ROOT)); + assertTrue("3.3", !path.isPrefixOf(Path.EMPTY)); + } + + public void testIsRoot() { + + // negative + assertTrue("1.0", !new Path("/first/second").isRoot()); + assertTrue("1.1", !Path.EMPTY.isRoot()); + assertTrue("1.2", !new Path("//").isRoot()); + assertTrue("1.3", !new Path("///").isRoot()); + + // positive + assertTrue("2.0", Path.ROOT.isRoot()); + assertTrue("2.1", new Path("/").isRoot()); + assertTrue("2.2.win", Path.forWindows("/").isRoot()); + assertTrue("2.3.posix", Path.forPosix("/").isRoot()); + } + + public void testIsUNC() { + + // negative + assertTrue("1.0", !Path.ROOT.isUNC()); + assertTrue("1.1", !Path.EMPTY.isUNC()); + + assertTrue("2.0", !new Path("a").isUNC()); + assertTrue("2.1", !new Path("a/b").isUNC()); + assertTrue("2.2", !new Path("/a").isUNC()); + assertTrue("2.3", !new Path("/a/b").isUNC()); + + assertTrue("3.0", !new Path("c:/a/b").isUNC()); + assertTrue("3.1", !new Path("c:a/b").isUNC()); + assertTrue("3.2", !new Path("/F/../").isUNC()); + + assertTrue("4.0", !new Path("c://a/").isUNC()); + assertTrue("4.1", !new Path("c:\\/a/b").isUNC()); + assertTrue("4.2", !new Path("c:\\\\").isUNC()); + + // positive + assertTrue("5.0", new Path("//").isUNC()); + assertTrue("5.1", new Path("//a").isUNC()); + assertTrue("5.2", new Path("//a/b").isUNC()); + assertTrue("5.3.win", Path.forWindows("\\\\ThisMachine\\HOME\\foo.jar").isUNC()); + + assertTrue("6.0.win", Path.forWindows("c://a/").setDevice(null).isUNC()); + assertTrue("6.1.win", Path.forWindows("c:\\/a/b").setDevice(null).isUNC()); + assertTrue("6.2.win", Path.forWindows("c:\\\\").setDevice(null).isUNC()); + } + + public void testIsValidPath() { + IPath test = Path.ROOT; + // positive + assertTrue("1.0", test.isValidPath("/first/second/third")); + assertTrue("1.1", test.isValidPath("")); + assertTrue("1.2", test.isValidPath("a")); + assertTrue("1.3", test.isValidPath("c:")); + assertTrue("1.4", test.isValidPath("//")); + assertTrue("1.5", test.isValidPath("//a")); + assertTrue("1.6", test.isValidPath("c:/a")); + assertTrue("1.7", test.isValidPath("c://a//b//c//d//e//f")); + assertTrue("1.8", test.isValidPath("//a//b//c//d//e//f")); + + // platform-dependent + assertFalse("2.1.win", Path.forWindows("").isValidPath("c:b:")); + assertFalse("2.2.win", Path.forWindows("").isValidPath("c:a/b:")); + assertTrue("2.3.posix", Path.forPosix("").isValidPath("c:b:")); + assertTrue("2.4.posix", Path.forPosix("").isValidPath("c:a/b:")); + + // static methods + assertFalse("3.1.win", Path.isValidWindowsPath("c:b:")); + assertFalse("3.2.win", Path.isValidWindowsPath("c:a/b:")); + assertTrue("3.3.posix", Path.isValidPosixPath("c:b:")); + assertTrue("3.4.posix", Path.isValidPosixPath("c:a/b:")); + } + + public void testIsValidSegment() { + IPath test = Path.ROOT; + // positive + assertTrue("1.0", test.isValidSegment("a")); + + // negative + assertFalse("2.1", test.isValidSegment("")); + assertFalse("2.2", test.isValidSegment("/")); + + // platform-dependent + assertFalse("3.1.win", Path.forWindows("").isValidSegment("\\")); + assertFalse("3.2.win", Path.forWindows("").isValidSegment(":")); + assertTrue("3.3.posix", Path.forPosix("").isValidSegment("\\")); + assertTrue("3.4.posix", Path.forPosix("").isValidSegment(":")); + + // static methods + assertFalse("4.1.win", Path.isValidWindowsSegment("\\")); + assertFalse("4.2.win", Path.isValidWindowsSegment(":")); + assertTrue("4.3.posix", Path.isValidPosixSegment("\\")); + assertTrue("4.4.posix", Path.isValidPosixSegment(":")); + + // path constants and Path(String) always on local platform + boolean isLocalPosix = java.io.File.separatorChar == '/'; + assertEquals("5.1", isLocalPosix, Path.EMPTY.isValidSegment(":")); + assertEquals("5.2", isLocalPosix, Path.ROOT.isValidSegment(":")); + assertEquals("5.3", isLocalPosix, new Path("").isValidSegment(":")); + } + + public void testLastSegment() { + + assertEquals("1.0", "second", new Path("/first/second").lastSegment()); + + assertEquals("2.0", "first", new Path("first").lastSegment()); + assertEquals("2.1", "first", new Path("/first/").lastSegment()); + assertEquals("2.2", "second", new Path("first/second").lastSegment()); + assertEquals("2.3", "second", new Path("first/second/").lastSegment()); + + assertNull("3.0", Path.EMPTY.lastSegment()); + assertNull("3.1", Path.ROOT.lastSegment()); + assertNull("3.2", new Path("//").lastSegment()); + + assertEquals("4.0", "second", new Path("//first/second/").lastSegment()); + assertEquals("4.1", "second", new Path("//first/second").lastSegment()); + assertEquals("4.2", "second", new Path("c:/first/second/").lastSegment()); + assertEquals("4.3", "second", new Path("c:first/second/").lastSegment()); + + assertEquals("5.0", "first", new Path("//first").lastSegment()); + assertEquals("5.1", "first", new Path("//first/").lastSegment()); + } + + public void testMakeAbsolute() { + IPath anyPath = new Path("first/second/third").makeAbsolute(); + assertTrue("1.0", anyPath.isAbsolute()); + assertEquals("1.1", new Path("/first/second/third"), anyPath); + + anyPath = new Path("").makeAbsolute(); + assertTrue("2.0", anyPath.isAbsolute()); + assertEquals("2.1", Path.ROOT, anyPath); + } + + public void testMakeRelative() { + IPath anyPath = new Path("/first/second/third").makeRelative(); + assertTrue("1.0", !anyPath.isAbsolute()); + assertEquals("1.1", new Path("first/second/third"), anyPath); + + anyPath = Path.ROOT.makeRelative(); + assertTrue("2.0", !anyPath.isAbsolute()); + assertEquals("2.1", new Path(""), anyPath); + } + + /** + * Tests for {@link Path#makeRelativeTo(IPath)}. + */ + public void testMakeRelativeTo() { + //valid cases + IPath[] bases = new IPath[] {new Path("/a/"), new Path("/a/b")}; + IPath[] children = new IPath[] {new Path("/a/"), new Path("/a/b"), new Path("/a/b/c")}; + for (int i = 0; i < bases.length; i++) { + for (int j = 0; j < children.length; j++) { + final IPath base = bases[i]; + final IPath child = children[j]; + IPath result = child.makeRelativeTo(base); + assertTrue("1." + i + ',' + j, !result.isAbsolute()); + assertEquals("2." + i + ',' + j, base.append(result), child); + } + } + + //for equal/identical paths, the relative path should be empty + IPath equalBase = new Path("/a/b"); + assertEquals("3.1", "", new Path("/a/b").makeRelativeTo(equalBase).toString()); + assertEquals("3.2", "", new Path("/a/b/").makeRelativeTo(equalBase).toString()); + assertEquals("3.3", "", equalBase.makeRelativeTo(equalBase).toString()); + + //invalid cases (no common prefix) + bases = new IPath[] {new Path("/"), new Path("/b"), new Path("/b/c")}; + children = new IPath[] {new Path("/a/"), new Path("/a/b"), new Path("/a/b/c")}; + for (int i = 0; i < bases.length; i++) { + for (int j = 0; j < children.length; j++) { + final IPath base = bases[i]; + final IPath child = children[j]; + IPath result = child.makeRelativeTo(base); + assertTrue("4." + i + ',' + j, !result.isAbsolute()); + assertEquals("5." + i + ',' + j, base.append(result), child); + } + } + } + + /** + * Tests for {@link Path#makeRelativeTo(IPath)}. + */ + public void testMakeRelativeToWindows() { + IPath[] bases = new IPath[] { Path.forWindows("c:/a/"), Path.forWindows("c:/a/b") }; + IPath[] children = new IPath[] { Path.forWindows("d:/a/"), Path.forWindows("d:/a/b"), + Path.forWindows("d:/a/b/c") }; + for (int i = 0; i < bases.length; i++) { + for (int j = 0; j < children.length; j++) { + final IPath base = bases[i]; + final IPath child = children[j]; + IPath result = child.makeRelativeTo(base); + assertTrue("1." + i + ".win," + j, result.isAbsolute()); + assertEquals("2." + i + ".win," + j, child, result); + } + } + + } + + public void testMakeUNC() { + ArrayList<Path> inputs = new ArrayList<>(); + ArrayList<String> expected = new ArrayList<>(); + ArrayList<String> expectedNon = new ArrayList<>(); + + inputs.add(Path.ROOT); + expected.add("//"); + expectedNon.add("/"); + + inputs.add(Path.EMPTY); + expected.add("//"); + expectedNon.add(""); + + inputs.add(new Path("a")); + expected.add("//a"); + expectedNon.add("a"); + + inputs.add(new Path("a/b")); + expected.add("//a/b"); + expectedNon.add("a/b"); + + inputs.add(new Path("/a/b/")); + expected.add("//a/b/"); + expectedNon.add("/a/b/"); + + inputs.add(new Path("//")); + expected.add("//"); + expectedNon.add("/"); + + inputs.add(new Path("//a")); + expected.add("//a"); + expectedNon.add("/a"); + + inputs.add(new Path("//a/b")); + expected.add("//a/b"); + expectedNon.add("/a/b"); + + inputs.add(new Path("//a/b/")); + expected.add("//a/b/"); + expectedNon.add("/a/b/"); + + inputs.add(new Path("c:", "/")); + expected.add("//"); + expectedNon.add("c:/"); + + inputs.add(new Path("c:", "")); + expected.add("//"); + expectedNon.add("c:"); + + inputs.add(new Path("c:", "a")); + expected.add("//a"); + expectedNon.add("c:a"); + + inputs.add(new Path("c:", "a/b")); + expected.add("//a/b"); + expectedNon.add("c:a/b"); + + inputs.add(new Path("c:", "/a")); + expected.add("//a"); + expectedNon.add("c:/a"); + + inputs.add(new Path("c:", "/a/b")); + expected.add("//a/b"); + expectedNon.add("c:/a/b"); + + assertEquals("0.0", inputs.size(), expected.size()); + assertEquals("0.1", inputs.size(), expectedNon.size()); + + for (int i = 0; i < inputs.size(); i++) { + IPath path = inputs.get(i); + IPath result = path.makeUNC(true); + assertTrue("1.0." + path + " (" + result + ")", result.isUNC()); + assertEquals("1.1." + path, expected.get(i), result.toString()); + result = path.makeUNC(false); + assertTrue("1.2." + path, !result.isUNC()); + assertEquals("1.3." + path, expectedNon.get(i), result.toString()); + } + } + + /** + * This test is for bizarre cases that previously caused errors. + */ + public void testRegression() { + try { + new Path("C:\\/eclipse"); + } catch (Exception e) { + fail("1.0", e); + } + try { + IPath path = Path.forWindows("d:\\\\ive"); + assertTrue("2.0.win", !path.isUNC()); + assertEquals("2.1.win", 1, path.segmentCount()); + assertEquals("2.2.win", "ive", path.segment(0)); + } catch (Exception e) { + fail("2.99", e); + } + + } + + public void testRemoveFirstSegments() { + assertEquals("1.0", new Path("second"), new Path("/first/second").removeFirstSegments(1)); + assertEquals("1.1", new Path("second/third/"), new Path("/first/second/third/").removeFirstSegments(1)); + assertEquals("1.2", Path.EMPTY, new Path("first").removeFirstSegments(1)); + assertEquals("1.3", Path.EMPTY, new Path("/first/").removeFirstSegments(1)); + assertEquals("1.4", new Path("second"), new Path("first/second").removeFirstSegments(1)); + assertEquals("1.5", Path.EMPTY, new Path("").removeFirstSegments(1)); + assertEquals("1.6", Path.EMPTY, Path.ROOT.removeFirstSegments(1)); + assertEquals("1.7", Path.EMPTY, new Path("/first/second/").removeFirstSegments(2)); + assertEquals("1.8", Path.EMPTY, new Path("/first/second/").removeFirstSegments(3)); + assertEquals("1.9", new Path("third/fourth"), new Path("/first/second/third/fourth").removeFirstSegments(2)); + + assertEquals("2.0.win", Path.forWindows("c:second"), Path.forWindows("c:/first/second").removeFirstSegments(1)); + assertEquals("2.1.win", Path.forWindows("c:second/third/"), Path.forWindows("c:/first/second/third/") + .removeFirstSegments(1)); + assertEquals("2.2.win", Path.forWindows("c:"), Path.forWindows("c:first").removeFirstSegments(1)); + assertEquals("2.3.win", Path.forWindows("c:"), Path.forWindows("c:/first/").removeFirstSegments(1)); + assertEquals("2.4.win", Path.forWindows("c:second"), Path.forWindows("c:first/second").removeFirstSegments(1)); + assertEquals("2.5.win", Path.forWindows("c:"), Path.forWindows("c:").removeFirstSegments(1)); + assertEquals("2.6.win", Path.forWindows("c:"), Path.forWindows("c:/").removeFirstSegments(1)); + assertEquals("2.7.win", Path.forWindows("c:"), Path.forWindows("c:/first/second/").removeFirstSegments(2)); + assertEquals("2.8.win", Path.forWindows("c:"), Path.forWindows("c:/first/second/").removeFirstSegments(3)); + assertEquals("2.9.win", Path.forWindows("c:third/fourth"), Path.forWindows("c:/first/second/third/fourth") + .removeFirstSegments(2)); + + assertEquals("3.0", new Path("second"), new Path("//first/second").removeFirstSegments(1)); + assertEquals("3.1", new Path("second/third/"), new Path("//first/second/third/").removeFirstSegments(1)); + assertEquals("3.2", Path.EMPTY, new Path("//first/").removeFirstSegments(1)); + assertEquals("3.3", Path.EMPTY, new Path("//").removeFirstSegments(1)); + assertEquals("3.4", Path.EMPTY, new Path("//first/second/").removeFirstSegments(2)); + assertEquals("3.5", Path.EMPTY, new Path("//first/second/").removeFirstSegments(3)); + assertEquals("3.6", new Path("third/fourth"), new Path("//first/second/third/fourth").removeFirstSegments(2)); + } + + public void testRemoveLastSegments() { + + assertEquals("1.0", new Path("/first"), new Path("/first/second").removeLastSegments(1)); + assertEquals("1.1", new Path("//first"), new Path("//first/second").removeLastSegments(1)); + assertEquals("1.2", new Path("c:/first"), new Path("c:/first/second").removeLastSegments(1)); + assertEquals("1.3", new Path("c:first"), new Path("c:first/second").removeLastSegments(1)); + + assertEquals("2.0", new Path("/first/second/"), new Path("/first/second/third/").removeLastSegments(1)); + assertEquals("2.1", new Path("//first/second/"), new Path("//first/second/third/").removeLastSegments(1)); + assertEquals("2.2", new Path("c:/first/second/"), new Path("c:/first/second/third/").removeLastSegments(1)); + assertEquals("2.3", new Path("c:first/second/"), new Path("c:first/second/third/").removeLastSegments(1)); + + assertEquals("3.0", Path.EMPTY, new Path("first").removeLastSegments(1)); + assertEquals("3.1", Path.ROOT, new Path("/first/").removeLastSegments(1)); + assertEquals("3.2", new Path("first"), new Path("first/second").removeLastSegments(1)); + + assertEquals("4.0", Path.EMPTY, new Path("").removeLastSegments(1)); + assertEquals("4.1", Path.ROOT, Path.ROOT.removeLastSegments(1)); + assertEquals("4.2", new Path("//"), new Path("//").removeLastSegments(1)); + } + + public void testRemoveTrailingSeparator() { + + IPath with = new Path("/first/second/third/"); + IPath without = new Path("/first/second/third"); + + assertSame("1.0", without, without.removeTrailingSeparator()); + assertEquals("1.1", without, with.removeTrailingSeparator()); + // trailing separators have no bearing on path equality so check via + // other means.... + assertTrue("1.2", !with.removeTrailingSeparator().hasTrailingSeparator()); + assertTrue("1.3", !without.hasTrailingSeparator()); + assertEquals("1.4", without.toString(), with.removeTrailingSeparator().toString()); + + assertSame("2.0", Path.ROOT, Path.ROOT.removeTrailingSeparator()); + assertEquals("2.1", Path.EMPTY, new Path("").removeTrailingSeparator()); + + assertEquals("3.0", new Path("//"), new Path("//").removeTrailingSeparator()); + assertEquals("3.1", new Path("//a"), new Path("//a").removeTrailingSeparator()); + assertEquals("3.2", new Path("//a"), new Path("//a/").removeTrailingSeparator()); + assertEquals("3.3", new Path("//a/b"), new Path("//a/b").removeTrailingSeparator()); + assertEquals("3.4", new Path("//a/b"), new Path("//a/b/").removeTrailingSeparator()); + + assertEquals("4.0", new Path("c:"), new Path("c:").removeTrailingSeparator()); + assertEquals("4.1", new Path("c:/"), new Path("c:/").removeTrailingSeparator()); + assertEquals("4.2", new Path("c:/a"), new Path("c:/a/").removeTrailingSeparator()); + assertEquals("4.3", new Path("c:/a/b"), new Path("c:/a/b").removeTrailingSeparator()); + assertEquals("4.4", new Path("c:/a/b"), new Path("c:/a/b/").removeTrailingSeparator()); + + assertEquals("5.0", new Path("c:a"), new Path("c:a/").removeTrailingSeparator()); + assertEquals("5.1", new Path("c:a/b"), new Path("c:a/b").removeTrailingSeparator()); + assertEquals("5.2", new Path("c:a/b"), new Path("c:a/b/").removeTrailingSeparator()); + } + + public void testSegments() { + + IPath anyPath = null; + String[] segs = null; + + // Case One: typical case + anyPath = new Path("/first/second/third/fourth"); + segs = anyPath.segments(); + + assertEquals("1.0", 4, segs.length); + assertEquals("1.1", "first", segs[0]); + assertEquals("1.2", "second", segs[1]); + assertEquals("1.3", "third", segs[2]); + assertEquals("1.4", "fourth", segs[3]); + + // Case Two: trailing separator + anyPath = new Path("/first/second/"); + segs = anyPath.segments(); + + assertEquals("2.0", 2, segs.length); + assertEquals("2.1", "first", segs[0]); + assertEquals("2.2", "second", segs[1]); + + // Case Three: no leading or trailing separators + anyPath = new Path("first/second"); + segs = anyPath.segments(); + + assertEquals("3.0", 2, segs.length); + assertEquals("3.1", "first", segs[0]); + assertEquals("3.2", "second", segs[1]); + + // Case Four: single segment + anyPath = new Path("first"); + segs = anyPath.segments(); + + assertEquals("4.0", 1, segs.length); + assertEquals("4.1", "first", segs[0]); + + // Case Five(a): no segments + anyPath = Path.EMPTY; + segs = anyPath.segments(); + + assertEquals("5.0", 0, segs.length); + + // Case Five(b): no segments + anyPath = Path.ROOT; + segs = anyPath.segments(); + + assertEquals("6.0", 0, segs.length); + + // Case Six: UNC path + anyPath = new Path("//server/volume/a/b/c"); + segs = anyPath.segments(); + assertEquals("7.0", 5, segs.length); + assertEquals("7.1", "server", segs[0]); + assertEquals("7.2", "volume", segs[1]); + assertEquals("7.3", "a", segs[2]); + assertEquals("7.4", "b", segs[3]); + assertEquals("7.5", "c", segs[4]); + } + + public void testToString() { + + IPath anyPath = new Path("/first/second/third"); + assertEquals("1.0", "/first/second/third", anyPath.toString()); + + assertEquals("1.1", "/", Path.ROOT.toString()); + assertEquals("1.2", "", Path.EMPTY.toString()); + } + + public void testUptoSegment() { + + //Case 1, absolute path with no trailing separator + IPath anyPath = new Path("/first/second/third"); + + assertEquals("1.0", Path.ROOT, anyPath.uptoSegment(0)); + assertEquals("1.1", new Path("/first"), anyPath.uptoSegment(1)); + assertEquals("1.2", new Path("/first/second"), anyPath.uptoSegment(2)); + assertEquals("1.3", new Path("/first/second/third"), anyPath.uptoSegment(3)); + assertEquals("1.4", new Path("/first/second/third"), anyPath.uptoSegment(4)); + + //Case 2, absolute path with trailing separator + anyPath = new Path("/first/second/third/"); + + assertEquals("2.0", Path.ROOT, anyPath.uptoSegment(0)); + assertEquals("2.1", new Path("/first/"), anyPath.uptoSegment(1)); + assertEquals("2.2", new Path("/first/second/"), anyPath.uptoSegment(2)); + assertEquals("2.3", new Path("/first/second/third/"), anyPath.uptoSegment(3)); + assertEquals("2.4", new Path("/first/second/third/"), anyPath.uptoSegment(4)); + + //Case 3, relative path with no trailing separator + anyPath = new Path("first/second/third"); + + assertEquals("3.0", Path.EMPTY, anyPath.uptoSegment(0)); + assertEquals("3.1", new Path("first"), anyPath.uptoSegment(1)); + assertEquals("3.2", new Path("first/second"), anyPath.uptoSegment(2)); + assertEquals("3.3", new Path("first/second/third"), anyPath.uptoSegment(3)); + assertEquals("3.4", new Path("first/second/third"), anyPath.uptoSegment(4)); + + //Case 4, relative path with trailing separator + anyPath = new Path("first/second/third/"); + + assertEquals("4.0", Path.EMPTY, anyPath.uptoSegment(0)); + assertEquals("4.1", new Path("first/"), anyPath.uptoSegment(1)); + assertEquals("4.2", new Path("first/second/"), anyPath.uptoSegment(2)); + assertEquals("4.3", new Path("first/second/third/"), anyPath.uptoSegment(3)); + assertEquals("4.4", new Path("first/second/third/"), anyPath.uptoSegment(4)); + + // bug 58835 - upToSegment(0) needs to preserve device + anyPath = Path.forWindows("c:/first/second/third"); + assertEquals("5.0.win", Path.forWindows("c:/"), anyPath.uptoSegment(0)); + anyPath = Path.forWindows("c:/first/second/third/"); + assertEquals("5.1.win", Path.forWindows("c:/"), anyPath.uptoSegment(0)); + anyPath = Path.forWindows("c:first/second/third/"); + assertEquals("5.2.win", Path.forWindows("c:"), anyPath.uptoSegment(0)); + anyPath = new Path("//one/two/three"); + assertEquals("5.3", new Path("//"), anyPath.uptoSegment(0)); + anyPath = new Path("//one/two/three/"); + assertEquals("5.4", new Path("//"), anyPath.uptoSegment(0)); + } +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/PluginVersionIdentifierTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/PluginVersionIdentifierTest.java new file mode 100644 index 000000000..9ca3a12ad --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/PluginVersionIdentifierTest.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 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.equinox.common.tests; + +import org.eclipse.core.runtime.PluginVersionIdentifier; +import org.eclipse.core.tests.harness.CoreTest; + +@Deprecated +public class PluginVersionIdentifierTest extends CoreTest { + + public PluginVersionIdentifierTest() { + super(null); + } + + public PluginVersionIdentifierTest(String name) { + super(name); + } + + public void testConstructor() { + + assertEquals("1.0", "123.456.789", new PluginVersionIdentifier(123, 456, 789).toString()); + assertEquals("1.1", "123.456.789", new PluginVersionIdentifier("123.456.789").toString()); + assertEquals("1.2", "123.456.0", new PluginVersionIdentifier("123.456").toString()); + + assertTrue("2.0", "0.123.456" != new PluginVersionIdentifier("123.456").toString()); + + try { + new PluginVersionIdentifier("-1.123.456"); + } catch (Exception e) { + assertTrue("3.0", true); + } + + PluginVersionIdentifier plugin = new PluginVersionIdentifier("1.123.456"); + assertTrue("4.0", plugin.getMajorComponent() == 1); + assertTrue("4.1", plugin.getMinorComponent() == 123); + assertTrue("4.2", plugin.getServiceComponent() == 456); + + } + + // should test the hashcode() method that is currently missing. + public void testEqual() { + + assertTrue("1.0", new PluginVersionIdentifier(123, 456, 789).equals(new PluginVersionIdentifier("123.456.789"))); + assertTrue("1.1", !new PluginVersionIdentifier(123, 456, 789).equals(new PluginVersionIdentifier("123.456"))); + + } + + public void testComparisons() { + + PluginVersionIdentifier plugin1 = new PluginVersionIdentifier("1.896.456"); + PluginVersionIdentifier plugin2 = new PluginVersionIdentifier("1.123.456"); + PluginVersionIdentifier plugin3 = new PluginVersionIdentifier("2.123.456"); + PluginVersionIdentifier plugin4 = new PluginVersionIdentifier("2.123.222"); + + assertTrue("1.0", plugin1.isGreaterThan(plugin2)); + assertTrue("1.1", plugin3.isGreaterThan(plugin2)); + assertTrue("1.2", !plugin1.isGreaterThan(plugin4)); + + assertTrue("2.0", plugin3.isEquivalentTo(plugin4)); + assertTrue("2.1", !plugin1.isEquivalentTo(plugin2)); + assertTrue("2.2", !plugin1.isEquivalentTo(plugin3)); + + assertTrue("3.0", plugin1.isCompatibleWith(plugin2)); + assertTrue("3.1", !plugin1.isCompatibleWith(plugin3)); + + } + + public void testValidate() { + // success cases + assertTrue("1.0", PluginVersionIdentifier.validateVersion("1").isOK()); + assertTrue("1.1", PluginVersionIdentifier.validateVersion("1.0").isOK()); + assertTrue("1.2", PluginVersionIdentifier.validateVersion("1.0.2").isOK()); + assertTrue("1.3", PluginVersionIdentifier.validateVersion("1.0.2.3456").isOK()); + assertTrue("1.3", PluginVersionIdentifier.validateVersion("1.2.3.-4").isOK()); + + // failure cases + assertTrue("2.0", !PluginVersionIdentifier.validateVersion("").isOK()); + assertTrue("2.1", !PluginVersionIdentifier.validateVersion("-1").isOK()); + assertTrue("2.2", !PluginVersionIdentifier.validateVersion(null).isOK()); + assertTrue("2.3", !PluginVersionIdentifier.validateVersion("/").isOK()); + assertTrue("2.4", !PluginVersionIdentifier.validateVersion("1.foo.2").isOK()); + assertTrue("2.5", !PluginVersionIdentifier.validateVersion("1./.3").isOK()); + assertTrue("2.6", !PluginVersionIdentifier.validateVersion(".").isOK()); + assertTrue("2.7", !PluginVersionIdentifier.validateVersion(".1").isOK()); + assertTrue("2.8", !PluginVersionIdentifier.validateVersion("1.2.").isOK()); + assertTrue("2.9", !PluginVersionIdentifier.validateVersion("1.2.3.4.5").isOK()); + assertTrue("2.10", !PluginVersionIdentifier.validateVersion("1.-2").isOK()); + assertTrue("2.11", !PluginVersionIdentifier.validateVersion("1.2.-3").isOK()); + } +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/ProgressMonitorWrapperTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/ProgressMonitorWrapperTest.java new file mode 100644 index 000000000..dd7cc11e5 --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/ProgressMonitorWrapperTest.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 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.equinox.common.tests; + +import org.eclipse.core.runtime.*; +import org.eclipse.core.tests.harness.CoreTest; + +/** + * Test cases for the Path class. + */ +public class ProgressMonitorWrapperTest extends CoreTest { + /** + * Need a zero argument constructor to satisfy the test harness. + * This constructor should not do any real work nor should it be + * called by user code. + */ + public ProgressMonitorWrapperTest() { + super(null); + } + + public ProgressMonitorWrapperTest(String name) { + super(name); + } + + /** + * @deprecated to suppress deprecation warnings + */ + @Deprecated + public void testProgressMonitorWrapper() { + NullProgressMonitor nullMonitor = new NullProgressMonitor(); + SubProgressMonitor wrapped = new SubProgressMonitor(nullMonitor, 10); + ProgressMonitorWrapper wrapper = new ProgressMonitorWrapper(wrapped) {}; + + assertSame("1.0", nullMonitor, wrapped.getWrappedProgressMonitor()); + assertSame("1.1", wrapped, wrapper.getWrappedProgressMonitor()); + + assertTrue("1.2", !nullMonitor.isCanceled()); + assertTrue("1.3", !wrapped.isCanceled()); + assertTrue("1.4", !wrapper.isCanceled()); + + nullMonitor.setCanceled(true); + assertTrue("1.5", nullMonitor.isCanceled()); + assertTrue("1.6", wrapped.isCanceled()); + assertTrue("1.7", wrapper.isCanceled()); + } +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/QualifiedNameTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/QualifiedNameTest.java new file mode 100644 index 000000000..850a13329 --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/QualifiedNameTest.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2000, 2012 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.equinox.common.tests; + +import static org.junit.Assert.assertNotEquals; + +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.core.tests.harness.CoreTest; + +/** + * Test cases for the QualifiedName class. + */ + +public class QualifiedNameTest extends CoreTest { + + /** + * Need a zero argument constructor to satisfy the test harness. + * This constructor should not do any real work nor should it be + * called by user code. + */ + public QualifiedNameTest() { + super(null); + } + + /** + * Constructor for QualifiedNameTest + */ + public QualifiedNameTest(String name) { + super(name); + } + + public void testQualifiers() { + + try { + new QualifiedName("foo", "bar"); + } catch (Exception e) { + fail("1.0"); + } + + try { + new QualifiedName(null, "bar"); + } catch (Exception e) { + fail("1.1"); + } + + try { + new QualifiedName(" ", "bar"); + } catch (Exception e) { + fail("1.2"); + } + + try { + new QualifiedName("", "bar"); + } catch (Exception e) { + fail("1.3"); + } + + } + + public void testLocalNames() { + + try { + new QualifiedName("foo", null); + fail("2.0"); + } catch (Exception e) { + // expected + } + + try { + new QualifiedName("foo", ""); + fail("2.1"); + } catch (Exception e) { + // expected + } + + try { + new QualifiedName("foo", " "); + } catch (Exception e) { + fail("2.2"); + } + + try { + new QualifiedName("foo", " port "); + } catch (Exception e) { + fail("2.3"); + } + + } + + public void testEqualsAndHashcode() { + + QualifiedName qN1 = new QualifiedName("org.eclipse.runtime", "myClass"); + QualifiedName qN2 = new QualifiedName("org.eclipse.runtime", "myClass"); + assertTrue("1.0", qN1.equals(qN2)); + assertTrue("1.1", qN2.equals(qN1)); + assertEquals("1.2", qN1.hashCode(), qN2.hashCode()); + + assertNotEquals("2.0", "org.eclipse.runtime.myClass", qN1); + + QualifiedName qN3 = new QualifiedName(null, "myClass"); + assertTrue("3.0", !qN1.equals(qN3)); + + QualifiedName qN4 = new QualifiedName("org.eclipse.runtime", " myClass"); + assertTrue("3.1", !qN1.equals(qN4)); + + QualifiedName qN5 = new QualifiedName("org.eclipse.runtime", "myClass "); + assertTrue("3.2", !qN1.equals(qN5)); + + QualifiedName qN6 = new QualifiedName(null, "myClass"); + QualifiedName qN7 = new QualifiedName(null, "myClass"); + assertTrue("4.0", qN6.equals(qN7)); + assertTrue("4.1", qN7.equals(qN6)); + assertEquals("4.2", qN7.hashCode(), qN6.hashCode()); + + QualifiedName qN8 = new QualifiedName(" ", "myClass"); + assertTrue("5.0", !qN8.equals(qN7)); + QualifiedName qN9 = new QualifiedName("", "myClass"); + assertTrue("5.1", !qN8.equals(qN9)); + + } + +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/RuntimeTests.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/RuntimeTests.java new file mode 100644 index 000000000..89ceee98d --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/RuntimeTests.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2018 Julian Honnen + * + * 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: + * Julian Honnen - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.common.tests; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@SuppressWarnings("deprecation") +@RunWith(Suite.class) +@SuiteClasses({ + CoreExceptionTest.class, + OperationCanceledExceptionTest.class, + PathTest.class, + PluginVersionIdentifierTest.class, + ProgressMonitorWrapperTest.class, + QualifiedNameTest.class, + SafeRunnerTest.class, + StatusTest.class, + SubMonitorSmallTicksTest.class, + SubMonitorTest.class, + SubProgressTest.class, + URIUtilTest.class, + URLTest.class +}) +public class RuntimeTests { + // intentionally left blank +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SafeRunnerTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SafeRunnerTest.java new file mode 100644 index 000000000..7edf55d0a --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SafeRunnerTest.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2009, 2018 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.equinox.common.tests; + +import org.eclipse.core.runtime.*; +import org.eclipse.core.tests.harness.CoreTest; + +/** + * Tests for {@link SafeRunner}. + */ +public class SafeRunnerTest extends CoreTest { + + /** + * Ensures that cancelation exceptions are handled + */ + public void testCancel() { + boolean caught = false; + try { + SafeRunner.run(new ISafeRunnable() { + @Override + public void handleException(Throwable exception) { + } + + @Override + public void run() throws Exception { + throw new OperationCanceledException(); + } + }); + } catch (OperationCanceledException e) { + caught = true; + } + assertFalse("1.0", caught); + } + + /** + * Tests that SafeRunner catches the expected exception types. + */ + public void testCaughtExceptionTypes() { + Throwable[] failures = new Throwable[] {new AssertionError(), new LinkageError(), new RuntimeException()}; + for (int i = 0; i < failures.length; i++) { + final Throwable[] handled = new Throwable[1]; + final Throwable current = failures[i]; + try { + SafeRunner.run(new ISafeRunnable() { + @Override + public void handleException(Throwable exception) { + handled[0] = exception; + } + + @Override + public void run() throws Exception { + if (current instanceof Exception) { + throw (Exception) current; + } else if (current instanceof Error) { + throw (Error) current; + } + } + }); + } catch (Throwable t) { + fail("1." + i, t); + } + assertEquals("2." + i, current, handled[0]); + } + + } + + /** + * Tests that SafeRunner re-throws expected exception types. + */ + public void testThrownExceptionTypes() { + //thrown exceptions + final Throwable[] thrown = new Throwable[] {new Error(), new OutOfMemoryError()}; + for (int i = 0; i < thrown.length; i++) { + boolean caught = false; + final Throwable current = thrown[i]; + try { + SafeRunner.run(new ISafeRunnable() { + @Override + public void handleException(Throwable exception) { + } + + @Override + public void run() throws Exception { + if (current instanceof Exception) { + throw (Exception) current; + } else if (current instanceof Error) { + throw (Error) current; + } + } + }); + } catch (Throwable t) { + assertEquals("1." + i, current, t); + caught = true; + } + assertTrue("2." + i, caught); + } + } + + public void testNull() { + try { + SafeRunner.run(null); + fail("1.0"); + } catch (RuntimeException e) { + //expected + } + } + + /** + * Ensures that exceptions are propagated when the safe runner re-throws it + */ + public void testRethrow() { + boolean caught = false; + try { + SafeRunner.run(new ISafeRunnable() { + @Override + public void handleException(Throwable exception) { + if (exception instanceof IllegalArgumentException) { + throw (IllegalArgumentException) exception; + } + } + + @Override + public void run() throws Exception { + throw new IllegalArgumentException(); + } + }); + } catch (IllegalArgumentException e) { + caught = true; + } + assertTrue("1.0", caught); + + } +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SubMonitorSmallTicksTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SubMonitorSmallTicksTest.java new file mode 100644 index 000000000..535fe1e82 --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SubMonitorSmallTicksTest.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +package org.eclipse.equinox.common.tests; + +import junit.framework.TestCase; +import org.eclipse.core.runtime.SubMonitor; + +/** + * Ensures that creating a SubMonitor with a small number of + * ticks will not prevent it from reporting accurate progress. + */ +public class SubMonitorSmallTicksTest extends TestCase { + + private TestProgressMonitor topmostMonitor; + private SubMonitor smallTicksChild; + private long startTime; + + private static int TOTAL_WORK = 1000; + + @Override + protected void setUp() throws Exception { + topmostMonitor = new TestProgressMonitor(); + smallTicksChild = SubMonitor.convert(topmostMonitor, 10); + super.setUp(); + startTime = System.currentTimeMillis(); + } + + public void testWorked() { + SubMonitor bigTicksChild = smallTicksChild.newChild(10).setWorkRemaining(TOTAL_WORK); + for (int i = 0; i < TOTAL_WORK; i++) { + bigTicksChild.worked(1); + } + bigTicksChild.done(); + } + + public void testInternalWorked() { + double delta = 10.0d / TOTAL_WORK; + + for (int i = 0; i < TOTAL_WORK; i++) { + smallTicksChild.internalWorked(delta); + } + } + + public void testSplit() { + SubMonitor bigTicksChild = smallTicksChild.newChild(10).setWorkRemaining(TOTAL_WORK); + for (int i = 0; i < TOTAL_WORK; i++) { + bigTicksChild.split(1); + } + bigTicksChild.done(); + } + + @Override + protected void tearDown() throws Exception { + smallTicksChild.done(); + topmostMonitor.done(); + long endTime = System.currentTimeMillis(); + SubMonitorTest.reportPerformance(getClass().getName(), getName(), startTime, endTime); + topmostMonitor.assertOptimal(); + super.tearDown(); + } + +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SubMonitorTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SubMonitorTest.java new file mode 100644 index 000000000..d1da6453c --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SubMonitorTest.java @@ -0,0 +1,942 @@ +/******************************************************************************* + * 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: + * 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.equinox.common.tests; + +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); + + double test6 = runInfiniteProgress(2, 20); + assertEquals(1000.0, test6, 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) { + } + } + + public void testIsCanceled() { + NullProgressMonitor npm = new NullProgressMonitor(); + SubMonitor subMonitor = SubMonitor.convert(npm); + assertTrue("subMonitor should not be canceled", !subMonitor.isCanceled()); + npm.setCanceled(true); + assertTrue("subMonitor should be canceled", subMonitor.isCanceled()); + } + + public void testSuppressIsCanceled() { + NullProgressMonitor npm = new NullProgressMonitor(); + SubMonitor subMonitor = SubMonitor.convert(npm).newChild(0, SubMonitor.SUPPRESS_ISCANCELED); + + assertTrue("subMonitor should not be canceled", !subMonitor.isCanceled()); + npm.setCanceled(true); + assertTrue("subMonitor should not be canceled", !subMonitor.isCanceled()); + } + + public void testSuppressIsCanceledFlagIsInherited() { + NullProgressMonitor npm = new NullProgressMonitor(); + SubMonitor subMonitor = SubMonitor.convert(npm).newChild(0, SubMonitor.SUPPRESS_ISCANCELED).newChild(0); + + assertTrue("subMonitor should not be canceled", !subMonitor.isCanceled()); + npm.setCanceled(true); + assertTrue("subMonitor should not be canceled", !subMonitor.isCanceled()); + } + + public void testSuppressIsCanceledAffectsSplit() { + NullProgressMonitor npm = new NullProgressMonitor(); + SubMonitor subMonitor = SubMonitor.convert(npm, 100).newChild(100, SubMonitor.SUPPRESS_ISCANCELED); + npm.setCanceled(true); + // Should not throw an OperationCanceledException. + subMonitor.split(50); + } + + /** + * 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 style : styles) { + { + 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 innerStyle : styles) { + 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 ""; + } + + StringBuilder buf = new StringBuilder(); + boolean isFirst = true; + for (String nextValue : value) { + 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(); + } + + /** + * Verifies that no cancellation checks are performed within + * {@link SubMonitor#split(int, int)} when passing in the + * {@link SubMonitor#SUPPRESS_ISCANCELED} flag. + */ + public void testSplitDoesNotCancelWhenCancellationSuppressed() { + TestProgressMonitor monitor = new TestProgressMonitor(); + monitor.setCanceled(true); + SubMonitor progress = SubMonitor.convert(monitor, 100); + for (int idx = 0; idx < 100; idx++) { + progress.split(1, SubMonitor.SUPPRESS_ISCANCELED); + } + + // If we get this far, it means cancellation was suppressed and the test + // passed + } + + /** + * 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"); + } + +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SubProgressTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SubProgressTest.java new file mode 100644 index 000000000..59494520a --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/SubProgressTest.java @@ -0,0 +1,603 @@ +/******************************************************************************* + * 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 java.util.*; +import junit.framework.TestCase; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.junit.Assert; + +/** + * + */ +@SuppressWarnings("deprecation") +public class SubProgressTest extends TestCase { + + private long startTime; + /** + * <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; + + public SubProgressTest() { + super(); + } + + public SubProgressTest(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(); + SubMonitorTest.reportPerformance(getClass().getName(), getName(), startTime, endTime); + super.tearDown(); + } + + /** + * Calls done on the given progress monitor and all of its parents, to a maximum + * of the given depth. + * + * @deprecated to suppress deprecation warnings + * + * @param monitor + * @param depth + */ + @Deprecated + public static void callDoneOnChain(IProgressMonitor monitor, int depth) { + IProgressMonitor current = monitor; + for (int count = 0; count < depth; count++) { + current.done(); + if (!(current instanceof SubProgressMonitor)) { + return; + } + SubProgressMonitor cur = (SubProgressMonitor) current; + current = cur.getWrappedProgressMonitor(); + } + } + + /** + * Tests the style bits in SubProgressMonitor + * @deprecated to suppress deprecation warnings + */ + @Deprecated + 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(); + } + + 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()); + 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.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) { + StringBuilder buf = new StringBuilder(); + boolean isFirst = true; + for (String nextValue : value) { + if (!isFirst) { + buf.append(", "); + } + isFirst = false; + buf.append("\"" + nextValue + "\""); + } + String list = buf.toString(); + return list; + } + + 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 + 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 + + Assert.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(); + + Assert.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(); + + Assert.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(); + + Assert.assertEquals(1000.0, top.getTotalWork(), 0.01d); + fpMonitor.worked(100); + Assert.assertEquals(1000.0, top.getTotalWork(), 0.01d); + fpMonitor.done(); + Assert.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 + 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); + + Assert.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(); + + Assert.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(); + + Assert.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(); + + Assert.assertEquals(1000.0, top.getTotalWork(), 0.01d); + fpMonitor.worked(100); + Assert.assertEquals(1000.0, top.getTotalWork(), 0.01d); + fpMonitor.done(); + Assert.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 + 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); + + Assert.assertEquals("Ensure no work has been reported yet", 0.0, top.getTotalWork(), 0.01d); + monitor1.beginTask("", 1000); + Assert.assertEquals("Ensure no work has been reported yet", 0.0, top.getTotalWork(), 0.01d); + monitor2.beginTask("", 1000); + Assert.assertEquals("Should not have cleaned up monitor 1", 0.0, top.getTotalWork(), 0.01d); + monitor1.done(); + + Assert.assertEquals("Should have cleaned up monitor 1", 200.0, top.getTotalWork(), 0.01d); + monitor1.worked(1000); + Assert.assertEquals("Monitor1 shouldn't report work once it's complete", 200.0, top.getTotalWork(), 0.01d); + monitor2.worked(500); + Assert.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); + Assert.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); + + Assert.assertEquals("All leaked work should have been collected", 1000.0, top.getTotalWork(), 0.01d); + } + + /** + * @deprecated to suppress deprecation warnings + */ + @Deprecated + 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); + 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 + spm.beginTask("", 1000); + SubProgressMonitor spm2 = new SubProgressMonitor(spm, 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()); + } + + /** + * 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, 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. + + 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. + * + * @deprecated to suppress deprecation warnings + * + * @param parent + * @param depth + * @return the innermost SubProgressMonitor + */ + @Deprecated + private static SubProgressMonitor createSubProgressChain(IProgressMonitor parent, int depth) { + SubProgressMonitor current; + do { + parent.beginTask("", 100); + current = new SubProgressMonitor(parent, 100); + parent = current; + depth--; + } while (depth > 0); + return current; + } + + /** + * 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); + } + } + + /** + * 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. + * + * @deprecated to suppress deprecation warnings + * + * @param monitor progress monitor (callers are responsible for calling done() if necessary) + * @param loopSize total size of the recursion tree + */ + @Deprecated + public static void reportWorkInBalancedTree(IProgressMonitor monitor, int loopSize) { + monitor.beginTask("", 100); + int leftBranch = loopSize / 2; + int rightBranch = loopSize - leftBranch; + + if (leftBranch > 1) { + SubProgressMonitor leftProgress = new SubProgressMonitor(monitor, 50); + reportWorkInBalancedTree(leftProgress, leftBranch); + leftProgress.done(); + } else { + monitor.worked(25); + monitor.internalWorked(25.0); + monitor.isCanceled(); + } + + if (rightBranch > 1) { + SubProgressMonitor rightProgress = new SubProgressMonitor(monitor, 50); + reportWorkInBalancedTree(rightProgress, rightBranch); + rightProgress.done(); + } else { + monitor.worked(25); + monitor.internalWorked(25.0); + monitor.isCanceled(); + } + } + + /** + * 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. + * + * @deprecated to suppress deprecation warnings + * + * @param monitor progress monitor (callers are responsible for calling done() if necessary) + * @param loopSize total size of the recursion tree + */ + @Deprecated + public static void createBalancedTree(IProgressMonitor monitor, int loopSize) { + monitor.beginTask("", 100); + int leftBranch = loopSize / 2; + int rightBranch = loopSize - leftBranch; + + if (leftBranch > 1) { + SubProgressMonitor leftProgress = new SubProgressMonitor(monitor, 50); + createBalancedTree(leftProgress, leftBranch); + leftProgress.done(); + } + + if (rightBranch > 1) { + SubProgressMonitor rightProgress = new SubProgressMonitor(monitor, 50); + createBalancedTree(rightProgress, rightBranch); + rightProgress.done(); + } + } + + /** + * The innermost loop for the looping test. We make this a static method so + * that it can be used both in this performance test and in the correctness test. + * + * @deprecated to suppress deprecation warnings + */ + @Deprecated + public static void runTestWorked(IProgressMonitor monitor) { + SubProgressMonitor nestedMonitor = createSubProgressChain(monitor, SubProgressTest.CHAIN_DEPTH); + reportWorkInLoop(nestedMonitor, SubProgressTest.PROGRESS_SIZE); + callDoneOnChain(nestedMonitor, SubProgressTest.CHAIN_DEPTH + 1); + } + + /** + * The innermost loop for the looping test. We make this a static method so + * that it can be used both in this performance test and in the correctness test. + * + * @deprecated to suppress deprecation warnings + */ + @Deprecated + public static void runTestInternalWorked(IProgressMonitor monitor) { + SubProgressMonitor nestedMonitor = createSubProgressChain(monitor, SubProgressTest.CHAIN_DEPTH); + reportFloatingPointWorkInLoop(nestedMonitor, SubProgressTest.PROGRESS_SIZE); + callDoneOnChain(nestedMonitor, SubProgressTest.CHAIN_DEPTH + 1); + } + + /** + * 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. + * + * @deprecated to suppress deprecation warnings + */ + @Deprecated + public static void runTestTypicalUsage(IProgressMonitor monitor) { + SubProgressMonitor nestedMonitor = createSubProgressChain(monitor, SubProgressTest.CHAIN_DEPTH); + reportWorkInBalancedTree(nestedMonitor, SubProgressTest.PROGRESS_SIZE); + callDoneOnChain(nestedMonitor, SubProgressTest.CHAIN_DEPTH + 1); + } + + /** + * <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 SubProgressMonitors + * is enough to report progress, even if worked(int) and worked(double) are never called</p> + * + * @deprecated to suppress deprecation warnings + */ + @Deprecated + public static void runTestCreateTree(IProgressMonitor monitor) { + SubProgressMonitor nestedMonitor = createSubProgressChain(monitor, SubProgressTest.CHAIN_DEPTH); + createBalancedTree(nestedMonitor, SubProgressTest.PROGRESS_SIZE); + callDoneOnChain(nestedMonitor, SubProgressTest.CHAIN_DEPTH + 1); + } + + /** + * 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. + */ + public void testNegativeWorkValues() { + TestProgressMonitor top = new TestProgressMonitor(); + top.beginTask("", 10); + + SubProgressMonitor childMonitor = new SubProgressMonitor(top, IProgressMonitor.UNKNOWN); // -1 + childMonitor.beginTask("", 10); + childMonitor.worked(5); + Assert.assertEquals(0.0, top.getTotalWork(), 0.01d); + childMonitor.done(); + Assert.assertEquals(0.0, top.getTotalWork(), 0.01d); + + top.done(); + } + +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/TestProgressMonitor.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/TestProgressMonitor.java new file mode 100644 index 000000000..bafd6a5a5 --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/TestProgressMonitor.java @@ -0,0 +1,304 @@ +/******************************************************************************* + * 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 org.eclipse.core.runtime.IProgressMonitor; +import org.junit.Assert; + +/** + * + */ +public class TestProgressMonitor implements IProgressMonitor { + + private double totalWork; + + /** + * Records the number of times worked or internalWorked was called with + * an argument of 0. + */ + private int redundantWorkCalls; + + /** + * Records the number of times setTaskName was called without changing the + * existing task name. + */ + private int redundantSetTaskCalls; + + /** + * Records the number of times subTask was called without changing the + * existing task name + */ + private int redundantSubTaskCalls; + + /** + * Stores the number of calls to the integer worked(...) method + */ + private int intWorkedCalls; + + /** + * Stores the number of calls to the double internalWorked(...) method + */ + private int doubleWorkedCalls; + + /** + * Stores the total number of calls to worked and internalWorked + */ + private int workCalls; + + /** + * Stores the total number of calls to setTaskName + */ + private int taskNameCalls; + + /** + * Stores the total number of calls to subTask + */ + private int subTaskCalls; + + /** + * Stores the total number of calls to isCanceled + */ + private int isCanceledCalls; + + private int beginTaskCalls; + + private int doneCalls; + + private String taskName = null; + + private String subTaskName = null; + + private int expectedWork; + + private String beginTaskName = ""; + + private boolean cancelled = false; + + public String getBeginTaskName() { + return beginTaskName; + } + + private static boolean equals(Object o1, Object o2) { + if (o1 == null) + return o2 == null; + if (o2 == null) + return false; + return o1.equals(o2); + } + + /** + * Returns the number of times beginTask() was called. For a correctly written job, + * this should equal 1 on completion. + * + * @return the number of calls to beginTask + */ + public int getBeginTaskCalls() { + return beginTaskCalls; + } + + /** + * Returns the number of times done() was called. For a correctly written job, + * this should equal 1 on completion. + * + * @return the number of calls to done + */ + public int getDoneCalls() { + return doneCalls; + } + + /** + * Returns the number of times worked was called as a no-op. + * That is, it counts the number of times worked() or internalWorked() had + * ever been called with a value of 0. This should return 0 for an + * optimally-written job. + * + * @return true iff redundant calls were ever made to *worked() on this + * monitor. + */ + public int getRedundantWorkCalls() { + return redundantWorkCalls; + } + + /** + * Returns the number of calls to isCancelled(). Optimally-written + * jobs may call this an unbounded number of times. + * + * @return the number of calls to isCancelled(). + */ + public int getIsCanceledCalls() { + return isCanceledCalls; + } + + /** + * Returns the number of calls to subTask(). + */ + public int getSubTaskCalls() { + return subTaskCalls; + } + + /** + * Returs the number of calls to setTaskName(). + */ + public int getTaskNameCalls() { + return taskNameCalls; + } + + /** + * Returns the number of calls to work() and internalWorked(). For the top-level + * progress monitor in an optimally-written job, this should be at least 100 and + * no more than 1000. A job that reports work less often than this will seem to + * have jumpy progress, and a job that reports work more often than this is reporting + * progress that won't be visible to the user and is wasting time in progress monitoring + * code. + * + * @return the number of calls to worked(int) or internalWorked(double) + */ + public int getWorkCalls() { + return workCalls; + } + + /** + * Returns the number of calls to internalWorked. For an optimally-written job, + * this should be 0, since integer work is faster and has no chance + * of floating-point rounding errors. + * + * @return the number of calls to internalWorked + */ + public int getDoubleWorkedCalls() { + return doubleWorkedCalls; + } + + /** + * Returns the number of calls to worked(int). For an optimally-written job, + * this should equal getWorkCalls, since integer work is faster and has no + * chance of floating-point rounding errors. + * + * @return the number of calls to worked(int) + */ + public int getIntWorkedCalls() { + return intWorkedCalls; + } + + public int getRedundantSetTaskCalls() { + return redundantSetTaskCalls; + } + + public int getRedundantSubTaskCalls() { + return redundantSubTaskCalls; + } + + /** + * Returns the total work reported on this monitor. For an optimally-written job, + * this should be +/- a small epsilon to account for floating point error. + * + * @return the total work reported on this job + */ + public double getTotalWork() { + return totalWork; + } + + @Override + public void beginTask(String name, int workToDo) { + beginTaskCalls++; + this.expectedWork = workToDo; + this.beginTaskName = name; + } + + @Override + public void done() { + doneCalls++; + } + + @Override + public void internalWorked(double work) { + workCalls++; + doubleWorkedCalls++; + if (work == 0.0) + redundantWorkCalls++; + totalWork += work; + } + + @Override + public boolean isCanceled() { + isCanceledCalls++; + return cancelled; + } + + @Override + public void setCanceled(boolean value) { + this.cancelled = value; + } + + @Override + public void setTaskName(String name) { + taskNameCalls++; + if (equals(name, taskName)) + redundantSetTaskCalls++; + taskName = name; + } + + @Override + public void subTask(String name) { + subTaskCalls++; + if (equals(name, subTaskName)) + redundantSubTaskCalls++; + subTaskName = name; + } + + @Override + public void worked(int work) { + workCalls++; + intWorkedCalls++; + if (work == 0) + redundantWorkCalls++; + totalWork += work; + } + + public int getExpectedWork() { + return expectedWork; + } + + /** + * <p>Asserts that the progress reported on this monitor was optimal. That is, + * there were no redundant method calls, and progress was reported in between + * 100 and 1000 increments.</p> + */ + public void assertOptimal() { + Assert.assertEquals("The progress monitor did not reach 100%", expectedWork, getTotalWork(), 0.01d); + Assert.assertTrue("This monitor reported progress with less than 1% accuracy", getWorkCalls() >= 100); + Assert.assertTrue("This monitor reported progress with more than 0.1% accuracy (the job spent too much time reporting redundant progress)", getWorkCalls() <= 1000); + Assert.assertEquals("Null work was reported on this monitor", 0, getRedundantWorkCalls()); + + if (expectedWork >= 1000) { + // Only check for internalWorked usage if there were enough ticks allocated on this progress + // monitor that worked(int) could have been used + Assert.assertEquals("internalWorked was being used instead of worked()", 0, getDoubleWorkedCalls()); + } + + Assert.assertEquals("Redundant calls were made to setTaskName", 0, getRedundantSetTaskCalls()); + Assert.assertEquals("Redundant calls were made to subTask", 0, getRedundantSubTaskCalls()); + Assert.assertEquals("The number of calls to done should match the number of calls to beginTask", getBeginTaskCalls(), getDoneCalls()); + Assert.assertEquals("beginTask should be called exactly once", getBeginTaskCalls(), 1); + } + + public String getSubTaskName() { + return subTaskName == null ? "" : subTaskName; + } + + public String getTaskName() { + return taskName == null ? "" : taskName; + } + +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/URIUtilTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/URIUtilTest.java new file mode 100644 index 000000000..109b06267 --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/URIUtilTest.java @@ -0,0 +1,555 @@ +/******************************************************************************* + * Copyright (c) 2008, 2018 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.equinox.common.tests; + +import java.io.*; +import java.net.*; +import org.eclipse.core.runtime.*; +import org.eclipse.core.tests.harness.CoreTest; +import org.osgi.framework.FrameworkUtil; + +/** + * Tests for the {@link URIUtil} class. + */ +public class URIUtilTest extends CoreTest { + /** Constant value indicating if the current platform is Windows */ + private static final boolean WINDOWS = java.io.File.separatorChar == '\\'; + + private static final String[] testPaths = new String[] {"abc", "with spaces", "with%percent"}; + + // re-enable once bug 331314 is fixed + public void testBug331314() { + doTestBug331314("File with spaces"); + doTestBug331314("FileWithBrackets[]"); + doTestBug331314("normal"); + } + + private void doTestBug331314(String name) { + File f = new File(new File(System.getProperty("java.io.tmpdir")), name); + URI original = f.toURI(); + URI base = f.getParentFile().toURI(); + URI relative = URIUtil.makeRelative(original, base); + assertFalse(name, relative.isAbsolute()); + assertEquals("Wrong ssp", name, relative.getSchemeSpecificPart()); + } + + /** + * Tests for {@link URIUtil#toJarURI(URI, IPath)}. + */ + public void testToJARURI() { + URL locationURL = FileLocator.find(FrameworkUtil.getBundle(getClass()), new Path("Plugin_Testing/uriutil/test.jar"), null); + try { + locationURL = FileLocator.resolve(locationURL); + URI location = URIUtil.toURI(locationURL); + final String suffix = "test/1029/test.txt"; + URI jar = URIUtil.toJarURI(location, new Path(suffix)); + InputStream is = jar.toURL().openStream(); + is.close(); + + //null entry path + URI jar2 = URIUtil.toJarURI(location, null); + assertEquals("2.0", jar.toString(), jar2.toString() + suffix); + + } catch (MalformedURLException e) { + fail("1.0", e); + } catch (IOException e) { + fail("1.1", e); + } catch (URISyntaxException e) { + fail("1.2", e); + } + } + + /** + * Tests for {@link URIUtil#toFile(URI)}. + * @throws URISyntaxException + */ + public void testToFile() throws URISyntaxException { + File base = new File(System.getProperty("java.io.tmpdir")); + for (int i = 0; i < testPaths.length; i++) { + File original = new File(base, testPaths[i]); + URI uri = original.toURI(); + File result = URIUtil.toFile(uri); + assertEquals("1." + i, original, result); + } + } + + /** + * Tests for {@link URIUtil#toFile(URI)} involving UNC paths. + * @throws URISyntaxException + */ + public void testToFileUNC() throws URISyntaxException { + if (!WINDOWS) { + return; + } + //UNC paths + URI path = new URI("file://HOST/some/path"); + File result = URIUtil.toFile(path); + if (File.pathSeparatorChar == '/') { + assertTrue("2.0", result.getAbsolutePath().startsWith("//")); + } else { + assertTrue("2.1", result.getAbsolutePath().startsWith("\\\\")); + } + assertTrue("2.2", new Path(result.toString()).isUNC()); + } + + /** + * Tests for {@link URIUtil#toUnencodedString(URI)}. + */ + public void testToUnencodedString() throws URISyntaxException { + assertEquals("1.0", "http://foo.bar", URIUtil.toUnencodedString(new URI("http://foo.bar"))); + assertEquals("1.1", "http://foo.bar#fragment", URIUtil.toUnencodedString(new URI("http://foo.bar#fragment"))); + assertEquals("1.2", "foo.bar#fragment", URIUtil.toUnencodedString(new URI("foo.bar#fragment"))); + assertEquals("1.3", "#fragment", URIUtil.toUnencodedString(new URI("#fragment"))); + + //spaces + assertEquals("2.1", "http://foo.bar/a b", URIUtil.toUnencodedString(new URI("http://foo.bar/a%20b"))); + assertEquals("2.2", "http://foo.bar/a#b c", URIUtil.toUnencodedString(new URI("http://foo.bar/a#b%20c"))); + assertEquals("2.3", "foo.bar/a b", URIUtil.toUnencodedString(new URI("foo.bar/a%20b"))); + assertEquals("2.4", "#a b", URIUtil.toUnencodedString(new URI("#a%20b"))); + } + + /** + * Tests for {@link URIUtil#fromString(String)}. + */ + public void testFromString() throws URISyntaxException { + //spaces + assertEquals("1.1", new URI("http://foo.bar/a%20b"), URIUtil.fromString("http://foo.bar/a b")); + assertEquals("1.2", new URI("http://foo.bar/a#b%20c"), URIUtil.fromString("http://foo.bar/a#b c")); + assertEquals("1.3", new URI("foo.bar/a%20b"), URIUtil.fromString("foo.bar/a b")); + assertEquals("1.4", new URI("#a%20b"), URIUtil.fromString("#a b")); + assertEquals("1.5", new URI("file:/C:/foo.bar/a%20b"), URIUtil.fromString("file:/C:/foo.bar/a b")); + + //percent character + assertEquals("2.1", new URI("http://foo.bar/a%2520b"), URIUtil.fromString("http://foo.bar/a%20b")); + assertEquals("2.2", new URI("http://foo.bar/a#b%2520c"), URIUtil.fromString("http://foo.bar/a#b%20c")); + assertEquals("2.3", new URI("foo.bar/a%2520b"), URIUtil.fromString("foo.bar/a%20b")); + assertEquals("2.4", new URI("#a%2520b"), URIUtil.fromString("#a%20b")); + assertEquals("2.5", new URI("file:/C:/foo.bar/a%2520b"), URIUtil.fromString("file:/C:/foo.bar/a%20b")); + + //relative URI + assertEquals("3.1", new URI("a/b"), URIUtil.fromString("file:a/b")); + assertEquals("3.2", new URI("a/b"), URIUtil.fromString("a/b")); + if (WINDOWS) { + assertEquals("3.3", new URI("file:/c:/a/b"), URIUtil.fromString("file:c:/a/b")); + assertEquals("3.4", new URI("file:/c:/a/b"), URIUtil.fromString("file:c:\\a\\b")); + assertEquals("3.5", new URI("file:/c:/a/b"), URIUtil.fromString("file:/c:\\a\\b")); + assertEquals("3.6", new URI("file:/a/b/c"), URIUtil.fromString("file:/a/b/c"));//bug 264101 + //backslash + assertEquals("3.7", new URI("file:/a/b/c"), URIUtil.fromString("file:\\a\\b\\c"));//bug 264101 + } + + //encoded legal character + assertEquals("4.1", new URI("http://foo.bar/a%2Cb").getSchemeSpecificPart(), URIUtil.fromString("http://foo.bar/a,b").getSchemeSpecificPart()); + assertEquals("4.2", new URI("file:/foo.bar/a%2Cb").getSchemeSpecificPart(), URIUtil.fromString("file:/foo.bar/a,b").getSchemeSpecificPart()); + + //backslash + URI uri = URIUtil.fromString("a\\b"); + System.out.println(uri); + } + + /** + * Tests for {@link URIUtil#toURI(java.net.URL)}. + */ + public void testURLtoURI() throws MalformedURLException, URISyntaxException { + //spaces + assertEquals("1.1", new URI("http://foo.bar/a%20b"), URIUtil.toURI(new URL("http://foo.bar/a b"))); + assertEquals("1.2", new URI("http://foo.bar/a#b%20c"), URIUtil.toURI(new URL("http://foo.bar/a#b c"))); + + //% characters + assertEquals("2.1", new URI("http://foo.bar/a%25b"), URIUtil.toURI(new URL("http://foo.bar/a%b"))); + + //UNC paths + assertEquals("3.1", new URI("file:////SERVER/some/path"), URIUtil.toURI(new URL("file://SERVER/some/path"))); + assertEquals("3.2", new URI("file:////SERVER/some/path"), URIUtil.toURI(new URL("file:////SERVER/some/path"))); + } + + /** + * Tests for {@link URIUtil#toURL(java.net.URI)}. + */ + public void testURItoURL() throws MalformedURLException, URISyntaxException { + //spaces + assertEquals("1.1", new URL("http://foo.bar/a%20b"), URIUtil.toURL(new URI("http://foo.bar/a%20b"))); + assertEquals("1.2", new URL("http://foo.bar/a#b%20c"), URIUtil.toURL(new URI("http://foo.bar/a#b%20c"))); + + //% characters + assertEquals("2.1", new URL("http://foo.bar/a%25b"), URIUtil.toURL(new URI("http://foo.bar/a%25b"))); + + //UNC paths + assertEquals("3.1", new URL("file:////SERVER/some/path"), URIUtil.toURL(new URI("file:////SERVER/some/path"))); + assertEquals("3.2", new URL("file://SERVER/some/path"), URIUtil.toURL(new URI("file://SERVER/some/path"))); + } + + /** + * Tests handling of Absolute file system paths on Windows incorrectly encoded as + * relative URIs (file:c:/tmp). + */ + public void testWindowsPathsFromURI() throws MalformedURLException, URISyntaxException { + if (!WINDOWS) { + return; + } + assertEquals("1.1", new URI("file:/c:/foo/bar.txt"), URIUtil.toURI(new URL("file:c:/foo/bar.txt"))); + assertEquals("1.2", new URI("file:/c:/foo/bar.txt"), URIUtil.toURI(new URL("file:/c:/foo/bar.txt"))); + } + + /** + * Tests handling of Absolute file system paths on Windows incorrectly encoded as + * relative URIs (file:c:/tmp). + */ + public void testWindowsPathsFromString() throws URISyntaxException { + if (!WINDOWS) { + return; + } + assertEquals("1.1", new URI("file:/c:/foo/bar.txt"), URIUtil.fromString("file:c:/foo/bar.txt")); + assertEquals("1.2", new URI("file:/c:/foo/bar.txt"), URIUtil.fromString("file:/c:/foo/bar.txt")); + } + + /** + * Tests handling of conversion from a File with spaces to URL and File to URI and equivalence of the resulting URI + */ + public void testFileWithSpaces() throws MalformedURLException, URISyntaxException { + File fileWithSpaces = new File("/c:/with spaces/goo"); + URI correctURI = fileWithSpaces.toURI(); + URL fileURL = fileWithSpaces.toURL(); + URI fileURI = null; + try { + fileURI = fileURL.toURI(); + fail(); + } catch (URISyntaxException e) { + fileURI = URIUtil.toURI(fileURL); + } + assertEquals("1.1", correctURI, fileURI); + + try { + fileURI = new URI(fileURL.toString()); + fail(); + } catch (URISyntaxException e) { + fileURI = URIUtil.fromString(fileURL.toString()); + } + assertEquals("1.2", correctURI, fileURI); + } + + /** + * Tests handling of conversion from a File with spaces to URL and File to URI and equivalence of the resulting URI + */ + public void testFileWithBrackets() throws MalformedURLException, URISyntaxException { + File fileWithSpaces = new File("/c:/with[brackets]/goo"); + URI correctURI = fileWithSpaces.toURI(); + URL fileURL = fileWithSpaces.toURL(); + URI fileURI = null; + try { + fileURI = fileURL.toURI(); + fail(); + } catch (URISyntaxException e) { + fileURI = URIUtil.toURI(fileURL); + } + assertEquals("1.1", correctURI, fileURI); + + try { + fileURI = new URI(fileURL.toString()); + fail(); + } catch (URISyntaxException e) { + fileURI = URIUtil.fromString(fileURL.toString()); + } + assertEquals("1.2", correctURI, fileURI); + } + + /** + * Tests for {@link URIUtil#append(URI, String)}. + * @throws URISyntaxException + */ + public void testAppend() throws URISyntaxException { + URI base = new URI("http://a.b.c/a%20b/"); + URI result = URIUtil.append(base, "file.txt"); + assertEquals("1.0", "http://a.b.c/a%20b/file.txt", result.toString()); + assertEquals("1.1", "//a.b.c/a b/file.txt", result.getSchemeSpecificPart()); + + base = new URI("http://a.b.c/a%20b/"); + result = URIUtil.append(base, "a b.txt"); + assertEquals("2.0", "http://a.b.c/a%20b/a%20b.txt", result.toString()); + assertEquals("2.1", "//a.b.c/a b/a b.txt", result.getSchemeSpecificPart()); + + } + + /** + * Tests for {@link URIUtil#append(URI, String)} when dealing with UNC paths. + */ + public void testAppendUNC() throws URISyntaxException { + //UNC paths + URI base = new URI("file:////SERVER/some/path/"); + URI relative = new URI("plugins/javax.servlet_2.4.0.v200806031604.jar"); + URI expectedResolved = new URI("file:////SERVER/some/path/plugins/javax.servlet_2.4.0.v200806031604.jar"); + URI resolved = URIUtil.append(base, relative.toString()); + assertEquals("1.0", expectedResolved, resolved); + } + + /** + * Tests for {@link URIUtil#append(URI, String)} when dealing with paths containing brackets. + * @throws URISyntaxException + */ + public void testAppendWithBrackets() throws URISyntaxException { + //append a simple string + URI base = new URI("http://example.com/base/"); + URI result = URIUtil.append(base, "file[with brackets].txt"); + assertEquals("1.0", "http://example.com/base/file%5Bwith%20brackets%5D.txt", result.toString()); + assertEquals("1.1", "/base/file[with brackets].txt", result.getPath()); + + //append a relative path + result = URIUtil.append(base, "some/path/file[with brackets].txt"); + assertEquals("2.0", "http://example.com/base/some/path/file%5Bwith%20brackets%5D.txt", result.toString()); + assertEquals("2.1", "/base/some/path/file[with brackets].txt", result.getPath()); + + //simple string where base has no trailing separator + base = new URI("http://example.com/base"); + result = URIUtil.append(base, "file[with brackets].txt"); + assertEquals("3.0", "http://example.com/base/file%5Bwith%20brackets%5D.txt", result.toString()); + assertEquals("3.1", "/base/file[with brackets].txt", result.getPath()); + + //append a path where base has no trailing separator + result = URIUtil.append(base, "some/path/file[with brackets].txt"); + assertEquals("4.0", "http://example.com/base/some/path/file%5Bwith%20brackets%5D.txt", result.toString()); + assertEquals("4.1", "/base/some/path/file[with brackets].txt", result.getPath()); + + //TODO opaque URI + // URI opaque = new URI("opaque:something/opaque/"); + // result = URIUtil.append(opaque, "some/path/file[with brackets].txt"); + // assertEquals("5.0", "opaque:something/opaque/some/path/file%5Bwith%20brackets%5D.txt", result.toString()); + // assertEquals("5.1", null, result.getPath()); + } + + public void testBug286339() throws URISyntaxException { + + //single letter server path + URI base = new URI("file:////S/some/path/"); + URI relative = new URI("plugins/javax.servlet_2.4.0.v200806031604.jar"); + URI expectedResolved = new URI("file:////S/some/path/plugins/javax.servlet_2.4.0.v200806031604.jar"); + URI resolved = URIUtil.append(base, relative.toString()); + assertEquals("1.1", expectedResolved, resolved); + + } + + public void testAppendWindows() throws URISyntaxException { + if (!WINDOWS) { + return; + } + URI base = new URI("file:/C:/a%20b"); + URI result = URIUtil.append(base, "file.txt"); + assertEquals("1.0", "file:/C:/a%20b/file.txt", result.toString()); + assertEquals("1.1", "/C:/a b/file.txt", result.getSchemeSpecificPart()); + + base = new URI("file:/C:/Documents%20and%20Settings/aniefer/junit-workspace/pde.build/265726/buildRepo/"); + result = URIUtil.append(base, "content.jar"); + assertEquals("2.0", "file:/C:/Documents%20and%20Settings/aniefer/junit-workspace/pde.build/265726/buildRepo/content.jar", result.toString()); + assertEquals("2.1", "/C:/Documents and Settings/aniefer/junit-workspace/pde.build/265726/buildRepo/content.jar", result.getSchemeSpecificPart()); + } + + /** + * Tests handling of conversion from a File with %20 to URL and File to URI and equivalence of the resulting URI + */ + public void testFileWithPercent20() throws MalformedURLException, URISyntaxException { + File fileWithPercent20 = new File("/c:/with%20spaces/goo"); + URI correctURI = fileWithPercent20.toURI(); + + URL fileURL = fileWithPercent20.toURL(); + assertNotSame("1.1", correctURI, fileURL.toURI()); + assertEquals("1.2", correctURI, URIUtil.toURI(fileURL)); + assertNotSame("1.3", correctURI, new URI(fileURL.toString())); + // we expect these to not be the same because fromString assumes a decoded URL String + assertNotSame("1.4", correctURI, URIUtil.fromString(fileURL.toString())); + } + + public void testRemoveExtension() { + try { + URI uri1 = new URI("file:/foo/bar/zoo.txt"); + assertEquals(new URI("file:/foo/bar/zoo"), URIUtil.removeFileExtension(uri1)); + + URI uri2 = new URI("file:/foo/bar.zoo/foo.txt"); + assertEquals(new URI("file:/foo/bar.zoo/foo"), URIUtil.removeFileExtension(uri2)); + + URI uri3 = new URI("file:/foo/bar.zoo/foo"); + assertEquals(new URI("file:/foo/bar.zoo/foo"), URIUtil.removeFileExtension(uri3)); + + URI uri4 = new URI("file:/C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/testRepo/plugins/org.junit_3.8.2.v200706111738.jar"); + assertEquals(new URI("file:/C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/testRepo/plugins/org.junit_3.8.2.v200706111738"), URIUtil.removeFileExtension(uri4)); + } catch (URISyntaxException e) { + fail("URI syntax exception", e); + } + } + + public void testRemoveFileExtensionFromFile() { + String fileName = "/c:/some.dir/afile"; + File testFileWithExtension = new File(fileName + ".extension"); + File testFileWithOutExtension = new File(fileName); + URI correctURI = testFileWithOutExtension.toURI(); + + assertEquals(correctURI, URIUtil.removeFileExtension(testFileWithExtension.toURI())); + assertEquals(correctURI, URIUtil.removeFileExtension(testFileWithOutExtension.toURI())); + } + + public void testSameURI() throws URISyntaxException { + assertFalse("1.0", URIUtil.sameURI(new File("a").toURI(), URIUtil.fromString("file:a"))); + assertFalse("1.1", URIUtil.sameURI(new URI("file:/a"), URIUtil.fromString("file:a"))); + + //encoded characters + assertTrue("2.0", URIUtil.sameURI(new URI("foo:/a%2Cb"), new URI("foo:/a,b"))); + assertTrue("2.1", URIUtil.sameURI(new URI("file:/a%2Cb"), new URI("file:/a,b"))); + } + + public void testSameURIWindows() throws URISyntaxException { + if (!WINDOWS) { + return; + } + //device and case variants + assertTrue("1.0", URIUtil.sameURI(new URI("file:C:/a"), new URI("file:c:/a"))); + assertTrue("1.1", URIUtil.sameURI(new URI("file:/C:/a"), new URI("file:/c:/a"))); + assertTrue("1.2", URIUtil.sameURI(new URI("file:/A"), new URI("file:/a"))); + assertTrue("1.3", URIUtil.sameURI(new URI("file:A"), new URI("file:a"))); + assertTrue("1.4", URIUtil.sameURI(new URI("file:/A/"), new URI("file:/a/"))); + + //negative cases + assertFalse("2.0", URIUtil.sameURI(new URI("file:/a/b"), new URI("file:/c:/a/b"))); + } + + public void testMakeAbsolute() throws URISyntaxException { + URI[][] data = new URI[][] { + // simple path + new URI[] {new URI("b"), new URI("file:/a/"), new URI("file:/a/b")}, // + new URI[] {new URI("b"), new URI("file:/a"), new URI("file:/a/b")}, + // common root + new URI[] {new URI("plugins/foo.jar"), new URI("file:/eclipse/"), new URI("file:/eclipse/plugins/foo.jar")}, + // non-local + new URI[] {new URI("http:/foo.com/a/b"), new URI("file:/a/x"), new URI("http:/foo.com/a/b")}, // + new URI[] {new URI("file:/a/b"), new URI("http:/foo.com/a/x"), new URI("file:/a/b")}, // + // + new URI[] {new URI("../plugins/foo.jar"), new URI("file:/eclipse/configuration"), new URI("file:/eclipse/plugins/foo.jar")}, // + //cases that can't be made absolute + //different scheme + new URI[] {new URI("file:../plugins/foo.jar"), new URI("http:/eclipse/configuration"), new URI("file:../plugins/foo.jar")}, // + //already absolute + new URI[] {new URI("file:../plugins/foo.jar"), new URI("file:/eclipse/configuration"), new URI("file:../plugins/foo.jar")}, // + new URI[] {new URI("file:/foo.jar"), new URI("file:/eclipse/configuration"), new URI("file:/foo.jar")}, // + //encoded characters + new URI[] {new URI("plugins%5Cfoo.jar"), new URI("file:/eclipse/"), new URI("file:/eclipse/plugins%5Cfoo.jar")},// + new URI[] {new URI("a%20b"), new URI("file:/eclipse/"), new URI("file:/eclipse/a%20b")},// + }; + + for (int i = 0; i < data.length; i++) { + URI location = data[i][0]; + URI root = data[i][1]; + URI expected = data[i][2]; + URI actual = URIUtil.makeAbsolute(location, root); + assertEquals("1." + Integer.toString(i), expected, actual); + } + + // run some Windows-specific tests with drive letters + if (!WINDOWS) { + return; + } + data = new URI[][] { + // simple path + new URI[] {new URI("b"), new URI("file:/c:/a/"), new URI("file:/c:/a/b")}, // + new URI[] {new URI("b"), new URI("file:/c:/a"), new URI("file:/c:/a/b")}, + // common root + new URI[] {new URI("plugins/foo.jar"), new URI("file:/c:/eclipse/"), new URI("file:/c:/eclipse/plugins/foo.jar")}, + // different drives + new URI[] {new URI("file:/c:/a/b"), new URI("file:/d:/a/x"), new URI("file:/c:/a/b")}, // + new URI[] {new URI("file:/c:/eclipse/plugins/foo.jar"), new URI("file:/d:/eclipse/"), new URI("file:/c:/eclipse/plugins/foo.jar")}, + // non-local + new URI[] {new URI("http:/c:/a/b"), new URI("file:/c:/a/x"), new URI("http:/c:/a/b")}, // + new URI[] {new URI("file:/c:/a/b"), new URI("http:/c:/a/x"), new URI("file:/c:/a/b")}, // + // + new URI[] {new URI("b"), new URI("file:/C:/a/"), new URI("file:/C:/a/b")}, // + new URI[] {new URI("b"), new URI("file:/C:/a"), new URI("file:/C:/a/b")}, // + new URI[] {new URI("file:/c:/"), new URI("file:/d:/"), new URI("file:/c:/")}, // + new URI[] {new URI("/c:/a/b/c"), new URI("file:/d:/a/b/"), new URI("file:/c:/a/b/c")}, // + new URI[] {new URI(""), new URI("file:/c:/"), new URI("file:/c:/")}, // + // + new URI[] {new URI("../plugins/foo.jar"), new URI("file:/c:/eclipse/configuration"), new URI("file:/c:/eclipse/plugins/foo.jar")}, // + //already absolute + new URI[] {new URI("file:../plugins/foo.jar"), new URI("file:/c:/eclipse/configuration"), new URI("file:../plugins/foo.jar")}, // + }; + for (int i = 0; i < data.length; i++) { + URI location = data[i][0]; + URI root = data[i][1]; + URI expected = data[i][2]; + URI actual = URIUtil.makeAbsolute(location, root); + assertEquals("2." + Integer.toString(i), expected, actual); + } + } + + /** + * Tests for {@link URIUtil#makeAbsolute(URI, URI)} involving UNC paths. + */ + public void testMakeAbsoluteUNC() throws URISyntaxException { + URI base = new URI("file:////SERVER/some/path/"); + URI relative = new URI("plugins/javax.servlet_2.4.0.v200806031604.jar"); + URI result = URIUtil.makeAbsolute(relative, base); + assertEquals("1.0", new URI("file:////SERVER/some/path/plugins/javax.servlet_2.4.0.v200806031604.jar"), result); + + //an absolute URI should not be resolved + URI absolute = new URI("file:////ANOTHERSERVER/another/path"); + URI resolved = URIUtil.makeAbsolute(absolute, base); + assertEquals("1.1", absolute, resolved); + + } + + public void testMakeRelative() throws URISyntaxException { + URI[][] data = new URI[][] { + // simple path + new URI[] {new URI("file:/a/b"), new URI("file:/a/x"), new URI("../b")}, + // common root + new URI[] {new URI("file:/eclipse/plugins/foo.jar"), new URI("file:/eclipse/"), new URI("plugins/foo.jar")}, + // non-local + new URI[] {new URI("http:/foo.com/a/b"), new URI("file:/a/x"), new URI("http:/foo.com/a/b")}, // + new URI[] {new URI("file:/a/b"), new URI("http:/foo.com/a/x"), new URI("file:/a/b")}, // + // + new URI[] {new URI("file:/"), new URI("file:/"), new URI("")}, // + }; + + for (int i = 0; i < data.length; i++) { + URI location = data[i][0]; + URI root = data[i][1]; + URI expected = data[i][2]; + URI actual = URIUtil.makeRelative(location, root); + assertEquals("1." + Integer.toString(i), expected, actual); + } + + // test some Windows-specific paths with drive letters + if (!WINDOWS) { + return; + } + data = new URI[][] { + // simple path + new URI[] {new URI("file:/c:/a/b"), new URI("file:/c:/a/x"), new URI("../b")}, + // common root + new URI[] {new URI("file:/c:/eclipse/plugins/foo.jar"), new URI("file:/c:/eclipse/"), new URI("plugins/foo.jar")}, + // different drives + new URI[] {new URI("file:/c:/a/b"), new URI("file:/d:/a/x"), new URI("file:/c:/a/b")}, // + new URI[] {new URI("file:/c:/eclipse/plugins/foo.jar"), new URI("file:/d:/eclipse/"), new URI("file:/c:/eclipse/plugins/foo.jar")}, + // non-local + new URI[] {new URI("http:/c:/a/b"), new URI("file:/c:/a/x"), new URI("http:/c:/a/b")}, // + new URI[] {new URI("file:/c:/a/b"), new URI("http:/c:/a/x"), new URI("file:/c:/a/b")}, // + // + new URI[] {new URI("file:/c:/a/b"), new URI("file:/C:/a/x"), new URI("../b")}, // + new URI[] {new URI("file:/c:/"), new URI("file:/d:/"), new URI("file:/c:/")}, // + new URI[] {new URI("file:/c:/"), new URI("file:/c:/"), new URI("")}, // + }; + for (int i = 0; i < data.length; i++) { + URI location = data[i][0]; + URI root = data[i][1]; + URI expected = data[i][2]; + URI actual = URIUtil.makeRelative(location, root); + assertEquals("2." + Integer.toString(i), expected, actual); + } + } +} diff --git a/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/URLTest.java b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/URLTest.java new file mode 100644 index 000000000..7c342dfa8 --- /dev/null +++ b/bundles/org.eclipse.equinox.common.tests/src/org/eclipse/equinox/common/tests/URLTest.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 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.equinox.common.tests; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import org.eclipse.core.tests.harness.CoreTest; + +public class URLTest extends CoreTest { + + public URLTest(String name) { + super(name); + } + + public void testPlatformPlugin() throws IOException { + URL url = new URL("platform:/plugin/org.eclipse.equinox.common.tests/test.xml"); + InputStream is = url.openStream(); + is.close(); + } +} |