Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean Michel-Lemieux2002-02-26 14:58:15 -0500
committerJean Michel-Lemieux2002-02-26 14:58:15 -0500
commit0d41a84d0066a4d891dd7c0f8d27cf4c2a06a7a1 (patch)
tree520873bb0ff30625279b5887f0656e3bff26cc17
parent441a9ad7724ad3ba488e20209584106c66405317 (diff)
downloadeclipse.platform.team-0d41a84d0066a4d891dd7c0f8d27cf4c2a06a7a1.tar.gz
eclipse.platform.team-0d41a84d0066a4d891dd7c0f8d27cf4c2a06a7a1.tar.xz
eclipse.platform.team-0d41a84d0066a4d891dd7c0f8d27cf4c2a06a7a1.zip
Initial release of ui benchmark tests
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/.classpath8
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/.vcm_meta15
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/about.html82
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/plugin.xml27
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkBig.zipbin0 -> 2571357 bytes
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkSmall.zipbin0 -> 213162 bytes
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkTiny.zipbin0 -> 2826 bytes
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/CVSTestSetup.java52
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/CVSUITestCase.java480
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/EclipseUITestHarnessApplication.java159
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/EmptyPerspective.java51
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/FakeSelectionProvider.java36
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/ICriteria.java17
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/LoggingTestResult.java217
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/PerformanceTimer.java49
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/SequenceGenerator.java57
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/Util.java709
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/Waiter.java30
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/AllTests.java25
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/BenchmarkTestSetup.java49
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/SyncTests.java117
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/WorkflowTests.java180
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/CaseEntry.java32
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/GroupEntry.java22
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/ILogEntryVisitor.java13
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/LogEntry.java117
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/LogEntryContainer.java57
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/MergeRunsVisitor.java67
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintAverageMain.java31
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintDiffMain.java52
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintDiffVisitor.java172
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintRawMain.java28
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintSummaryVisitor.java99
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/RootEntry.java42
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/TaskEntry.java72
35 files changed, 3091 insertions, 73 deletions
diff --git a/tests/org.eclipse.team.tests.cvs.core/.classpath b/tests/org.eclipse.team.tests.cvs.core/.classpath
index fef1a186d..49469e263 100644
--- a/tests/org.eclipse.team.tests.cvs.core/.classpath
+++ b/tests/org.eclipse.team.tests.cvs.core/.classpath
@@ -8,6 +8,12 @@
<classpathentry kind="src" path="/org.junit"/>
<classpathentry kind="src" path="/org.eclipse.core.tests.harness"/>
<classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="/org.eclipse.core.boot"/>
+ <classpathentry kind="src" path="/org.eclipse.team.cvs.ui"/>
+ <classpathentry kind="src" path="/org.eclipse.team.ui"/>
+ <classpathentry kind="src" path="/org.eclipse.ui"/>
+ <classpathentry kind="src" path="/org.eclipse.swt"/>
+ <classpathentry kind="src" path="/org.apache.xerces"/>
+ <classpathentry kind="src" path="/org.eclipse.compare"/>
<classpathentry kind="output" path="bin"/>
</classpath>
-
diff --git a/tests/org.eclipse.team.tests.cvs.core/.vcm_meta b/tests/org.eclipse.team.tests.cvs.core/.vcm_meta
index 58210c4e6..691cc5016 100644
--- a/tests/org.eclipse.team.tests.cvs.core/.vcm_meta
+++ b/tests/org.eclipse.team.tests.cvs.core/.vcm_meta
@@ -1,14 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-description>
- <comment></comment>
<nature id="org.eclipse.jdt.core.javanature"/>
- <nature id="org.eclipse.team.cvs.core.cvsnature"/>
+ <reference project-name="org.apache.xerces"/>
+ <reference project-name="org.eclipse.compare"/>
+ <reference project-name="org.eclipse.core.boot"/>
<reference project-name="org.eclipse.core.resources"/>
- <reference project-name="org.eclipse.team.cvs.core"/>
+ <reference project-name="org.eclipse.core.runtime"/>
<reference project-name="org.eclipse.core.tests.harness"/>
- <reference project-name="org.junit"/>
+ <reference project-name="org.eclipse.swt"/>
<reference project-name="org.eclipse.team.core"/>
- <reference project-name="org.eclipse.core.runtime"/>
+ <reference project-name="org.eclipse.team.cvs.core"/>
+ <reference project-name="org.eclipse.team.cvs.ui"/>
+ <reference project-name="org.eclipse.team.ui"/>
+ <reference project-name="org.eclipse.ui"/>
+ <reference project-name="org.junit"/>
<builder name="org.eclipse.jdt.core.javabuilder">
</builder>
</project-description>
diff --git a/tests/org.eclipse.team.tests.cvs.core/about.html b/tests/org.eclipse.team.tests.cvs.core/about.html
index 768324e89..441774f92 100644
--- a/tests/org.eclipse.team.tests.cvs.core/about.html
+++ b/tests/org.eclipse.team.tests.cvs.core/about.html
@@ -1,42 +1,42 @@
-<html>
-<head>
-<title>About</title>
-<style type="text/css">
-p, table, td, th { font-family: arial, helvetica, geneva; font-size: 10pt}
-pre { font-family: "Courier New", Courier, mono; font-size: 10pt}
-h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}
-code { font-family: "Courier New", Courier, mono; font-size: 10pt}
-sup { font-family: arial,helvetica,geneva; font-size: 10px}
-h3 { font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}
-li { font-family: arial, helvetica, geneva; font-size: 10pt}
-h1 { font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}
-body { font-family: arial, helvetica, geneva; font-size: 10pt; clip: rect( ); margin-top: 5mm; margin-left: 3mm}
-</style>
-</head>
-<body lang="EN-US" link="blue" vlink="purple">
-<table border="0" cellspacing="5" cellpadding="2" width="100%" >
- <tr>
- <td align="LEFT" valign="TOP" colspan="2" bgcolor="#0080C0"><b><font color="#FFFFFF">About This Content</font></b></td>
- </tr>
- <tr>
- <td>
-<p>11th December, 2001</p>
-<h3>License</h3>
-<p>Eclipse.org makes available all content in this plug-in &quot;Content&quot;. Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
-<a href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License Version 0.5</a> &quot;CPL&quot;. For purposes of the CPL, &quot;Program&quot; will mean the Content.</p>
-
-<h3>Contributions</h3>
-
-<p>If this Content is licensed to you under the terms and conditions of the CPL, any Contributions, as defined in the CPL, uploaded, submitted, or otherwise
-made available to Eclipse.org, members of Eclipse.org and/or the host of Eclipse.org web site, by you that relate to such
-Content are provided under the terms and conditions of the CPL and can be made available to others under the terms of the CPL.</p>
-
-<p>If this Content is licensed to you under license terms and conditions other than the CPL &quot;Other License&quot;, any modifications, enhancements and/or
-other code and/or documentation &quot;Modifications&quot; uploaded, submitted, or otherwise made available to Eclipse.org, members of Eclipse.org and/or the
-host of Eclipse.org, by you that relate to such Content are provided under terms and conditions of the Other License and can be made available
-to others under the terms of the Other License. In addition, with regard to Modifications for which you are the copyright holder, you are also
-providing the Modifications under the terms and conditions of the CPL and such Modifications can be made available to others under the terms of
-the CPL.</p>
-</td></tr></table>
-</body>
+<html>
+<head>
+<title>About</title>
+<style type="text/css">
+p, table, td, th { font-family: arial, helvetica, geneva; font-size: 10pt}
+pre { font-family: "Courier New", Courier, mono; font-size: 10pt}
+h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}
+code { font-family: "Courier New", Courier, mono; font-size: 10pt}
+sup { font-family: arial,helvetica,geneva; font-size: 10px}
+h3 { font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}
+li { font-family: arial, helvetica, geneva; font-size: 10pt}
+h1 { font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}
+body { font-family: arial, helvetica, geneva; font-size: 10pt; clip: rect( ); margin-top: 5mm; margin-left: 3mm}
+</style>
+</head>
+<body lang="EN-US" link="blue" vlink="purple">
+<table border="0" cellspacing="5" cellpadding="2" width="100%" >
+ <tr>
+ <td align="LEFT" valign="TOP" colspan="2" bgcolor="#0080C0"><b><font color="#FFFFFF">About This Content</font></b></td>
+ </tr>
+ <tr>
+ <td>
+<p>11th December, 2001</p>
+<h3>License</h3>
+<p>Eclipse.org makes available all content in this plug-in &quot;Content&quot;. Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
+<a href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License Version 0.5</a> &quot;CPL&quot;. For purposes of the CPL, &quot;Program&quot; will mean the Content.</p>
+
+<h3>Contributions</h3>
+
+<p>If this Content is licensed to you under the terms and conditions of the CPL, any Contributions, as defined in the CPL, uploaded, submitted, or otherwise
+made available to Eclipse.org, members of Eclipse.org and/or the host of Eclipse.org web site, by you that relate to such
+Content are provided under the terms and conditions of the CPL and can be made available to others under the terms of the CPL.</p>
+
+<p>If this Content is licensed to you under license terms and conditions other than the CPL &quot;Other License&quot;, any modifications, enhancements and/or
+other code and/or documentation &quot;Modifications&quot; uploaded, submitted, or otherwise made available to Eclipse.org, members of Eclipse.org and/or the
+host of Eclipse.org, by you that relate to such Content are provided under terms and conditions of the Other License and can be made available
+to others under the terms of the Other License. In addition, with regard to Modifications for which you are the copyright holder, you are also
+providing the Modifications under the terms and conditions of the CPL and such Modifications can be made available to others under the terms of
+the CPL.</p>
+</td></tr></table>
+</body>
</html> \ No newline at end of file
diff --git a/tests/org.eclipse.team.tests.cvs.core/plugin.xml b/tests/org.eclipse.team.tests.cvs.core/plugin.xml
index 4fe45682e..56d514b33 100644
--- a/tests/org.eclipse.team.tests.cvs.core/plugin.xml
+++ b/tests/org.eclipse.team.tests.cvs.core/plugin.xml
@@ -6,10 +6,15 @@
provider-name="Object Technology International, Inc.">
<requires>
+ <import plugin="org.apache.xerces"/>
+ <import plugin="org.eclipse.core.tests.harness"/>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.team.core"/>
<import plugin="org.eclipse.team.cvs.core"/>
- <import plugin="org.eclipse.core.tests.harness"/>
+ <import plugin="org.eclipse.team.ui"/>
+ <import plugin="org.eclipse.team.cvs.ui"/>
+ <import plugin="org.eclipse.compare"/>
+ <import plugin="org.eclipse.ui"/>
<import plugin="org.junit"/>
</requires>
@@ -46,6 +51,26 @@
<test id="cvs.remote">
<run class="org.eclipse.team.tests.ccvs.core.provider.RemoteResourceTest"/>
</test>
+
+ <test id="cvsui.benchmark.all">
+ <run class="org.eclipse.team.tests.ccvs.ui.benchmark.AllTests"/>
+ </test>
+ <test id="cvsui.benchmark.sync">
+ <run class="org.eclipse.team.tests.ccvs.ui.benchmark.SyncTests"/>
+ </test>
+ <test id="cvsui.benchmark.workflow">
+ <run class="org.eclipse.team.tests.ccvs.ui.benchmark.WorkflowTests"/>
+ </test>
+ </extension>
+
+ <extension point="org.eclipse.ui.perspectives">
+ <perspective id="org.eclipse.team.tests.ccvs.ui.EmptyPerspective" name="Empty Perspective" class="org.eclipse.team.tests.ccvs.ui.EmptyPerspective" />
+ </extension>
+
+ <extension id="harness" point="org.eclipse.core.runtime.applications">
+ <application>
+ <run class="org.eclipse.team.tests.ccvs.ui.EclipseUITestHarnessApplication"/>
+ </application>
</extension>
</plugin>
diff --git a/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkBig.zip b/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkBig.zip
new file mode 100644
index 000000000..ec9b17bdd
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkBig.zip
Binary files differ
diff --git a/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkSmall.zip b/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkSmall.zip
new file mode 100644
index 000000000..c56846755
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkSmall.zip
Binary files differ
diff --git a/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkTiny.zip b/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkTiny.zip
new file mode 100644
index 000000000..a127ff3d7
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkTiny.zip
Binary files differ
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/CVSTestSetup.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/CVSTestSetup.java
index 4207841b0..dfbeb058b 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/CVSTestSetup.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/CVSTestSetup.java
@@ -17,9 +17,8 @@ import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
public class CVSTestSetup extends TestSetup {
-
public static String REPOSITORY_LOCATION;
- static boolean INITIALIZE_REPO;
+ public static boolean INITIALIZE_REPO;
public static final boolean DEBUG;
public static final String RSH;
@@ -28,18 +27,35 @@ public class CVSTestSetup extends TestSetup {
// Static initializer for constants
static {
+ loadProperties();
+ REPOSITORY_LOCATION = System.getProperty("eclipse.cvs.repository");
+ INITIALIZE_REPO = Boolean.valueOf(System.getProperty("eclipse.cvs.initrepo", "false")).booleanValue();
+ DEBUG = Boolean.valueOf(System.getProperty("eclipse.cvs.debug", "false")).booleanValue();
+ RSH = System.getProperty("eclipse.cvs.rsh", "rsh");
+ }
+
+ public static void loadProperties() {
String propertiesFile = System.getProperty("eclipse.cvs.properties");
- if (propertiesFile != null)
+ if (propertiesFile == null) return;
+ File file = new File(propertiesFile);
+ if (file.isDirectory()) file = new File(file, "repository.properties");
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
try {
- readRepositoryProperties(propertiesFile);
- } catch (IOException e) {
- System.out.println("Could not read repository properties file: " + propertiesFile);
+ for (String line; (line = reader.readLine()) != null; ) {
+ int sep = line.indexOf("=");
+ String property = line.substring(0, sep).trim();
+ String value = line.substring(sep + 1).trim();
+ System.setProperty("eclipse.cvs." + property, value);
+ }
+ } finally {
+ reader.close();
}
- REPOSITORY_LOCATION = System.getProperty("eclipse.cvs.repository");
- INITIALIZE_REPO = (System.getProperty("eclipse.cvs.initrepo")==null)?false:(new Boolean(System.getProperty("eclipse.cvs.initrepo")).booleanValue());
- DEBUG= (System.getProperty("eclipse.cvs.debug")==null)?false:(new Boolean(System.getProperty("eclipse.cvs.debug")).booleanValue());
- RSH= (System.getProperty("eclipse.cvs.rsh")==null)?"rsh":System.getProperty("eclipse.cvs.rsh");
- }
+ } catch (Exception e) {
+ System.err.println("Could not read repository properties file: " + file.getAbsolutePath());
+ }
+ }
+
/**
* Constructor for CVSTestSetup.
*/
@@ -67,20 +83,6 @@ public class CVSTestSetup extends TestSetup {
}
}
- static void readRepositoryProperties(String filename) throws IOException {
- File file = new File(filename);
- if (file.isDirectory())
- file = new File(file, "repository.properties");
- BufferedReader reader = new BufferedReader(new FileReader(file));
- String line;
- while ((line = reader.readLine()) != null) {
- int sep = line.indexOf("=");
- String property = line.substring(0, sep).trim();
- String value = line.substring(sep + 1).trim();
- System.setProperty("eclipse.cvs." + property, value);
- }
-
- }
public void setUp() throws CVSException {
if (repository == null)
repository = setupRepository(REPOSITORY_LOCATION);
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/CVSUITestCase.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/CVSUITestCase.java
new file mode 100644
index 000000000..78d20d723
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/CVSUITestCase.java
@@ -0,0 +1,480 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.IDiffContainer;
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceDescription;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.team.ccvs.core.CVSTag;
+import org.eclipse.team.ccvs.core.ICVSRemoteFolder;
+import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.RepositoryManager;
+import org.eclipse.team.internal.ccvs.ui.actions.AddToWorkspaceAction;
+import org.eclipse.team.internal.ccvs.ui.actions.CommitAction;
+import org.eclipse.team.internal.ccvs.ui.actions.ReplaceWithRemoteAction;
+import org.eclipse.team.internal.ccvs.ui.actions.TagAction;
+import org.eclipse.team.internal.ccvs.ui.actions.UpdateAction;
+import org.eclipse.team.internal.ccvs.ui.sync.CVSSyncCompareInput;
+import org.eclipse.team.internal.ccvs.ui.sync.CommitSyncAction;
+import org.eclipse.team.internal.ccvs.ui.sync.UpdateSyncAction;
+import org.eclipse.team.internal.ccvs.ui.wizards.SharingWizard;
+import org.eclipse.team.tests.ccvs.core.CVSTestSetup;
+import org.eclipse.team.ui.sync.ITeamNode;
+import org.eclipse.team.ui.sync.SyncSet;
+import org.eclipse.team.ui.sync.SyncView;
+import org.eclipse.ui.IActionDelegate;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.WorkbenchException;
+
+public class CVSUITestCase extends TestCase {
+ private List testWindows;
+ protected IWorkbenchWindow testWindow;
+ protected LoggingTestResult logResult;
+ protected CVSRepositoryLocation testRepository;
+
+ public CVSUITestCase(String name) {
+ super(name);
+ testWindows = new ArrayList(3);
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ testRepository = CVSTestSetup.repository;
+ testWindow = openTestWindow();
+
+ // disable auto-build
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IWorkspaceDescription description = workspace.getDescription();
+ description.setAutoBuilding(false);
+ workspace.setDescription(description);
+
+ // wait for UI to settle
+ processEventsUntil(100);
+ }
+
+ protected void tearDown() throws Exception {
+ // wait for UI to settle
+ processEventsUntil(100);
+ closeAllTestWindows();
+ super.tearDown();
+ }
+
+ public void run(TestResult result) {
+ logResult = (result instanceof LoggingTestResult) ? (LoggingTestResult) result : null;
+ super.run(result);
+ }
+
+ /**
+ * Marks the beginning of a new task group.
+ * @param groupName the name for the group
+ */
+ protected void startGroup(String groupName) {
+ if (logResult != null) logResult.startGroup(groupName);
+ }
+
+ /**
+ * Marks the ends of the active task group.
+ */
+ protected void endGroup() {
+ if (logResult != null) logResult.endGroup();
+ }
+
+ /**
+ * Marks the beginning of a new task.
+ * @param taskName the name for the task
+ */
+ protected void startTask(String taskName) {
+ if (logResult != null) logResult.startTask(taskName);
+ }
+
+ /**
+ * Marks the ends of the active task.
+ */
+ protected void endTask() {
+ if (logResult != null) logResult.endTask();
+ }
+
+ /**
+ * Returns the name of the active group, or null if none.
+ */
+ protected String getGroupName() {
+ return logResult == null ? "unknown" : logResult.getGroupName();
+ }
+
+ /**
+ * Returns the name of the active task, or null if none.
+ */
+ protected String getTaskName() {
+ return logResult == null ? "unknown" : logResult.getTaskName();
+ }
+
+ /**
+ * Open a test window with the empty perspective.
+ */
+ public IWorkbenchWindow openTestWindow() {
+ try {
+ IWorkbenchWindow win = PlatformUI.getWorkbench().openWorkbenchWindow(
+ EmptyPerspective.PERSP_ID, ResourcesPlugin.getWorkspace());
+ testWindows.add(win);
+ return win;
+ } catch (WorkbenchException e) {
+ fail();
+ return null;
+ }
+ }
+
+ /**
+ * Close all test windows.
+ */
+ public void closeAllTestWindows() {
+ Iterator iter = testWindows.iterator();
+ IWorkbenchWindow win;
+ while (iter.hasNext()) {
+ win = (IWorkbenchWindow) iter.next();
+ win.close();
+ }
+ testWindows.clear();
+ }
+
+ /**
+ * Process pending events until at least the specified number of milliseconds elapses.
+ */
+ public void processEventsUntil(int hiatus) {
+ if (testWindow == null) return;
+ Display display = testWindow.getShell().getDisplay();
+ final boolean done[] = new boolean[] { hiatus == 0 };
+ if (hiatus != 0) display.timerExec(hiatus, new Runnable() {
+ public void run() { done[0] = true; }
+ });
+ for (;;) {
+ while (display.readAndDispatch());
+ if (done[0]) return;
+ display.sleep();
+ }
+ }
+
+ /**
+ * Process pending events until the tests are resumed by the user.
+ * Very useful for inspecting intermediate results while debugging.
+ */
+ public void processEventsUntilResumed() {
+ if (testWindow == null) return;
+ Display display = testWindow.getShell().getDisplay();
+ Shell shell = new Shell(testWindow.getShell(), SWT.CLOSE);
+ shell.setText("Close me to resume tests");
+ shell.setBounds(0, 0, 300, 30);
+ shell.open();
+ while (! shell.isDisposed()) {
+ while (! display.readAndDispatch()) display.sleep();
+ }
+ }
+
+ /**
+ * Checks out the projects with the specified tags from the test repository.
+ */
+ protected void actionCheckoutProjects(String[] projectNames, CVSTag[] tags) throws Exception {
+ ICVSRemoteFolder[] projects = lookupRemoteProjects(projectNames, tags);
+ runActionDelegate(new AddToWorkspaceAction(), projects, "Repository View Checkout action");
+ processEventsUntil(100); // let the UI settle before continuing
+ }
+
+ /**
+ * Replaces the specified resources with the remote contents using the action contribution.
+ */
+ protected void actionReplaceWithRemote(IResource[] resources) {
+ ReplaceWithRemoteAction action = new ReplaceWithRemoteAction() {
+ protected boolean confirmOverwrite(String message) {
+ return true;
+ }
+ };
+ runActionDelegate(action, resources, "Replace with Remote action");
+ processEventsUntil(100); // let the UI settle before continuing
+ }
+
+ /**
+ * Shares the specified project with the test repository.
+ * @param project the project to share
+ */
+ protected void actionShareProject(IProject project) {
+ final SharingWizard wizard = new SharingWizard();
+ wizard.init(PlatformUI.getWorkbench(), project);
+ Util.waitForWizardToOpen(testWindow.getShell(), wizard, new Waiter() {
+ public boolean notify(Object object) {
+ WizardDialog dialog = (WizardDialog) object;
+ startTask("set sharing, pop up sync viewer");
+ wizard.performFinish();
+ endTask();
+ dialog.close();
+ return false;
+ }
+ });
+ processEventsUntil(100); // let the UI settle before continuing
+ }
+
+ /**
+ * Updates the specified resources using the action contribution.
+ */
+ protected void actionCVSCommit(IResource[] resources, final String comment) {
+ CommitAction action = new CommitAction() {
+ protected String promptForComment() {
+ return comment;
+ }
+ };
+ runActionDelegate(action, resources, "CVS Commit action");
+ processEventsUntil(100); // let the UI settle before continuing
+ }
+
+ /**
+ * Tags the specified resources using the action contribution.
+ */
+ protected void actionCVSTag(IResource[] resources, final String name) {
+ TagAction action = new TagAction() {
+ protected String promptForTag() {
+ return name;
+ }
+ };
+ runActionDelegate(action, resources, "CVS Tag action");
+ processEventsUntil(100); // let the UI settle before continuing
+ }
+
+ /**
+ * Updates the specified resources using the action contribution.
+ */
+ protected void actionCVSUpdate(IResource[] resources, final String name) {
+ runActionDelegate(new UpdateAction(), resources, "CVS Update action");
+ processEventsUntil(100); // let the UI settle before continuing
+ }
+
+ /**
+ * Pops up the synchronizer view for the specified resources.
+ * @param resources the resources to sync
+ * @return the compare input used
+ */
+ protected CVSSyncCompareInput syncResources(IResource[] resources) {
+ startTask("Synchronize with Repository action");
+ SyncView syncView = getSyncView();
+ CVSSyncCompareInput input = new CVSSyncCompareInput(resources) {
+ // overridden to prevent "nothing to synchronize" dialog from popping up
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+ super.run(monitor);
+ DiffNode result = getDiffRoot(); // (DiffNode) getCompareResult()
+ if (result == null || Util.isEmpty(result)) throw new InterruptedException();
+ }
+ };
+ syncView.showSync(input);
+ endTask();
+ return input;
+ }
+
+ /**
+ * Commits the specified resources using the synchronizer view.
+ * @param resources the resources to commit
+ * @param input the compare input for the sync view, or null to create a new one
+ * @param comment the comment string, or ""
+ */
+ protected void syncCommitResources(IResource[] resources, CVSSyncCompareInput input, String comment) {
+ if (input == null) input = syncResources(resources);
+ IDiffContainer diffRoot = input.getDiffRoot();
+ if (Util.isEmpty(diffRoot)) {
+ startTask("Nothing to Commit");
+ } else {
+ ITeamNode[] nodes = getTeamNodesForResources(diffRoot, resources);
+ startTask("Sync View Commit action");
+ syncCommitInternal(input, nodes, comment);
+ }
+ endTask();
+ processEventsUntil(100); // let the UI settle before continuing
+ }
+
+ /**
+ * Updates the specified resources using the synchronizer view.
+ * @param resources the resources to update
+ * @param input the compare input for the sync view, or null to create a new one
+ * @param comment the comment string, or ""
+ */
+ protected void syncUpdateResources(IResource[] resources, CVSSyncCompareInput input) {
+ if (input == null) input = syncResources(resources);
+ IDiffContainer diffRoot = input.getDiffRoot();
+ if (Util.isEmpty(diffRoot)) {
+ startTask("Nothing to Update");
+ } else {
+ ITeamNode[] nodes = getTeamNodesForResources(diffRoot, resources);
+ startTask("Sync View Update action");
+ syncGetInternal(input, nodes);
+ }
+ endTask();
+ processEventsUntil(100); // let the UI settle before continuing
+ }
+
+ /**
+ * Creates and imports project contents from a zip file.
+ */
+ protected IProject createAndImportProject(String prefix, File zipFile) throws Exception {
+// beginTask("create test project and import initial contents");
+ IProject project = Util.createUniqueProject(prefix);
+ Util.importZipIntoProject(project, zipFile);
+// endTask();
+ processEventsUntil(100); // let the UI settle before continuing
+ return project;
+ }
+
+ /**
+ * Deletes a project safely.
+ */
+ protected void deleteProject(IProject project) {
+// beginTask("delete test project");
+ try {
+ processEventsUntil(250); // wait for things to settle before deleting
+ Util.deleteProject(project);
+ } catch (CoreException e) {
+ System.err.println("Error occurred while deleting project, disregarding it");
+ e.printStackTrace();
+ }
+// endTask();
+ processEventsUntil(100); // let the UI settle before continuing
+ }
+
+ /**
+ * Looks up handles for remote projects by name.
+ */
+ protected ICVSRemoteFolder[] lookupRemoteProjects(String[] projectNames, CVSTag[] tags) throws Exception {
+ ICVSRemoteFolder[] folders = new ICVSRemoteFolder[projectNames.length];
+ for (int i = 0; i < projectNames.length; ++i) {
+ folders[i] = testRepository.getRemoteFolder(projectNames[i], tags[i]);
+ }
+ return folders;
+ }
+
+ /**
+ * Gets an instance of the Synchronize view
+ */
+ protected SyncView getSyncView() {
+ // based on org.eclipse.team.internal.ccvs.ui.wizards.SharingWizard
+ SyncView view = (SyncView)CVSUIPlugin.getActivePage().findView(SyncView.VIEW_ID);
+ if (view == null) {
+ view = SyncView.findInActivePerspective();
+ }
+ assertNotNull("Could not obtain a Sync View.", view);
+ try {
+ CVSUIPlugin.getActivePage().showView(SyncView.VIEW_ID);
+ } catch (PartInitException e) {
+ CVSUIPlugin.log(e.getStatus());
+ }
+ return view;
+ }
+
+ /**
+ * Runs an IActionDelegate prototype instance on a given selection.
+ */
+ protected void runActionDelegate(IActionDelegate delegate, Object[] selection, String taskName) {
+ Action action = new Action() { };
+ if (delegate instanceof IObjectActionDelegate) {
+ ((IObjectActionDelegate) delegate).setActivePart(action,
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getPartService().getActivePart());
+ }
+ delegate.selectionChanged(action, new StructuredSelection(selection));
+ startTask(taskName);
+ delegate.run(action);
+ endTask();
+ }
+
+ /**
+ * Commits the resources represented by an array of synchronizer nodes.
+ */
+ private void syncCommitInternal(CVSSyncCompareInput input, ITeamNode[] nodes, final String comment) {
+ FakeSelectionProvider selectionProvider = new FakeSelectionProvider(nodes);
+ CommitSyncAction action = new CommitSyncAction(input, selectionProvider, "Commit",
+ testWindow.getShell()) {
+ protected int promptForConflicts(SyncSet syncSet) {
+ return 0; // yes! sync conflicting changes
+ }
+ protected String promptForComment(RepositoryManager manager) {
+ return comment; // use our comment
+ }
+ };
+ action.run();
+ }
+
+ /**
+ * Commits the resources represented by an array of synchronizer nodes.
+ */
+ private void syncGetInternal(CVSSyncCompareInput input, ITeamNode[] nodes) {
+ FakeSelectionProvider selectionProvider = new FakeSelectionProvider(nodes);
+ UpdateSyncAction action = new UpdateSyncAction(input, selectionProvider, "Get",
+ testWindow.getShell()) {
+ protected boolean promptForConflicts() {
+ return true;
+ }
+ protected int promptForMergeableConflicts() {
+ return 2;
+ }
+ };
+ action.run();
+ }
+
+ /**
+ * Gets an array of synchronizer nodes corresponding to an array of resouces.
+ */
+ protected static ITeamNode[] getTeamNodesForResources(IDiffContainer root, IResource[] resources) {
+ ITeamNode[] nodes = new ITeamNode[resources.length];
+ for (int i = 0; i < resources.length; ++i) {
+ nodes[i] = findTeamNodeForResource(root, resources[i]);
+ assertNotNull(nodes[i]);
+ }
+ return nodes;
+ }
+
+ private static ITeamNode findTeamNodeForResource(IDiffElement root, IResource resource) {
+ if (root instanceof ITeamNode) {
+ ITeamNode node = (ITeamNode) root;
+ if (resource.equals(node.getResource())) return node;
+ // prune the backtracking tree
+ IResource parent = resource.getParent();
+ do {
+ if (parent == null) return null; // can't possibly be child of this node
+ } while (! resource.equals(parent));
+ }
+ if (root instanceof IDiffContainer) {
+ IDiffContainer container = (IDiffContainer) root;
+ if (container.hasChildren()) {
+ IDiffElement[] children = container.getChildren();
+ for (int i = 0; i < children.length; ++i) {
+ ITeamNode node = findTeamNodeForResource(children[i], resource);
+ if (node != null) return node;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/EclipseUITestHarnessApplication.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/EclipseUITestHarnessApplication.java
new file mode 100644
index 000000000..d963864d7
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/EclipseUITestHarnessApplication.java
@@ -0,0 +1,159 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.tests.harness.EclipseTestHarnessApplication;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.internal.Workbench;
+
+/**
+ * A test harness with UI and logging support.
+ */
+public class EclipseUITestHarnessApplication extends EclipseTestHarnessApplication {
+ protected boolean ignoreFirst;
+ protected int repeatCount;
+ protected LoggingTestResult logResult;
+
+ /**
+ * Application entry point.
+ */
+ public Object run(Object userArgs) throws Exception {
+ PrintStream logStream = System.err;
+ String logFilename = null;
+ repeatCount = 1;
+ ignoreFirst = false;
+ if (userArgs instanceof String[]) {
+ // parse args, no error handling
+ String[] args = (String[]) userArgs;
+ List argsList = new ArrayList(args.length);
+ for (int i = 0; i < args.length; ++i) {
+ if ("-repeat".equals(args[i])) {
+ repeatCount = Integer.parseInt(args[++i]);
+ } else if ("-ignorefirst".equals(args[i])) {
+ ignoreFirst = true;
+ } else if ("-nolog".equals(args[i])) {
+ logStream = null;
+ } else if ("-log".equals(args[i])) {
+ logFilename = args[++i];
+ } else {
+ argsList.add(args[i]);
+ }
+ }
+ userArgs = argsList.toArray(new String[argsList.size()]);
+ }
+ // setup logging
+ if (logFilename != null) {
+ File file = new File(logFilename);
+ logStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(logFilename)));
+ }
+ logResult = new LoggingTestResult(logStream);
+ try {
+ logResult.startLog(System.currentTimeMillis(), getSDKBuildId());
+ return launchWorkbench(userArgs);
+ } finally {
+ logResult.endLog();
+ if (logFilename != null) logStream.close();
+ }
+ }
+
+ /**
+ * Launches the Workbench UI.
+ */
+ protected Object launchWorkbench(final Object userArgs) throws Exception {
+ final Exception[] exception = new Exception[1];
+ Workbench workbench = new Workbench() {
+ /*** this code should be kept in sync with Workbench.runEventLoop() ***/
+ protected void runEventLoop() {
+ // Dispatch all events.
+ Display display = Display.getCurrent();
+ while (true) {
+ try {
+ if (!display.readAndDispatch())
+ break;
+ } catch (Throwable e) {
+ break;
+ }
+ }
+
+ // Run our hook.
+ try {
+ workbenchHook(this);
+ } catch (Exception e) {
+ exception[0] = e;
+ }
+
+ // Close the workbench.
+ close();
+ }
+ };
+ Object result = workbench.run(userArgs);
+ if (exception[0] != null) throw exception[0];
+ return result;
+ }
+
+ /**
+ * Callback from Workbench if it launched successfully.
+ */
+ protected Object workbenchHook(Workbench workbench) throws Exception {
+ // run the underlying non-ui test launcher to locate and start the test cases
+ return super.run(workbench.getCommandLineArgs());
+ }
+
+ /**
+ * Runs the specified test suite. Called from the non-ui test launcher.
+ */
+ protected void run(Test suite) {
+ junit.textui.TestRunner runner = new junit.textui.TestRunner() {
+ public TestResult createTestResult() {
+ return logResult;
+ }
+ };
+ for (int i = 0; i < repeatCount; ++i) {
+ logResult.setLogging(! (i == 0 && ignoreFirst));
+ TestResult result = runner.doRun(suite, false);
+
+ // quit if an error occurred
+ if (! result.wasSuccessful()) {
+ System.out.println("Aborted due to error after " + (i + 1) + " repetition(s).");
+ break;
+ }
+ }
+ }
+
+ /**
+ * Gets the SDK build id
+ */
+ public static String getSDKBuildId() {
+ try {
+ URL url = Platform.getPluginRegistry().getPluginDescriptor("org.eclipse.sdk").getInstallURL();
+ url = new URL(url, "platform.ini");
+ InputStream is = url.openStream();
+ try {
+ Properties sdkProperties = new Properties();
+ sdkProperties.load(is);
+ String buildId = sdkProperties.getProperty("buildID");
+ if (buildId != null) return buildId;
+ } finally {
+ is.close();
+ }
+ } catch (Exception e) {
+ }
+ return "unknown";
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/EmptyPerspective.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/EmptyPerspective.java
new file mode 100644
index 000000000..3fd4f6f57
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/EmptyPerspective.java
@@ -0,0 +1,51 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.ui.*;
+import org.eclipse.ui.part.*;
+import org.eclipse.ui.part.*;
+import org.eclipse.swt.SWT;
+
+/**
+ * This perspective is used for testing api. It defines an initial
+ * layout with no parts, just an editor area.
+ *
+ * Note: originally borrowed from org.eclipse.jdt.junit.eclipse.util
+ */
+public class EmptyPerspective implements IPerspectiveFactory {
+
+ /**
+ * The perspective id.
+ */
+ public static final String PERSP_ID = "org.eclipse.team.tests.ccvs.ui.EmptyPerspective";
+
+ /**
+ * Constructs a new Default layout engine.
+ */
+ public EmptyPerspective() {
+ super();
+ }
+
+ /**
+ * Defines the initial layout for a perspective.
+ *
+ * Implementors of this method may add additional views to a
+ * perspective. The perspective already contains an editor folder
+ * with <code>ID = ILayoutFactory.ID_EDITORS</code>. Add additional views
+ * to the perspective in reference to the editor folder.
+ *
+ * This method is only called when a new perspective is created. If
+ * an old perspective is restored from a persistence file then
+ * this method is not called.
+ *
+ * @param factory the factory used to add views to the perspective
+ */
+ public void createInitialLayout(IPageLayout layout) {
+ //layout.addView( MockViewPart.ID, IPageLayout.BOTTOM, 0.5f, layout.getEditorArea() );
+ }
+} \ No newline at end of file
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/FakeSelectionProvider.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/FakeSelectionProvider.java
new file mode 100644
index 000000000..ca90addca
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/FakeSelectionProvider.java
@@ -0,0 +1,36 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+/**
+ * Serves up fake selections.
+ *
+ * Note: originally borrowed from org.eclipse.jdt.ui.tests.actions
+ */
+public class FakeSelectionProvider implements ISelectionProvider {
+ private Object[] fElems;
+ public FakeSelectionProvider(Object[] elements){
+ fElems = elements;
+ }
+
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ }
+
+ public ISelection getSelection() {
+ return new StructuredSelection(fElems);
+ }
+
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ }
+
+ public void setSelection(ISelection selection) {
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/ICriteria.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/ICriteria.java
new file mode 100644
index 000000000..bacd0aff1
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/ICriteria.java
@@ -0,0 +1,17 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * Generic object filter mechanism.
+ */
+public interface ICriteria {
+ /**
+ * Returns true if the candidate object satisfies the specified
+ * criteria value according to a particular algorithm.
+ */
+ public boolean test(Object candidate, Object value);
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/LoggingTestResult.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/LoggingTestResult.java
new file mode 100644
index 000000000..b7828c1de
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/LoggingTestResult.java
@@ -0,0 +1,217 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Stack;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+
+public class LoggingTestResult extends TestResult {
+ protected Stack groupStack;
+ protected PerformanceTimer currentTask;
+ protected PrintStream logStream;
+ protected Stack /* of String */ elements;
+ protected String indent;
+ protected boolean loggingEnabled;
+
+ public LoggingTestResult(PrintStream logStream) {
+ this.logStream = logStream;
+ this.elements = new Stack();
+ this.indent = "";
+ this.loggingEnabled = true;
+ groupStack = new Stack();
+ currentTask = null;
+ }
+
+ /**
+ * Enables or disables logging.
+ */
+ public void setLogging(boolean loggingEnabled) {
+ this.loggingEnabled = loggingEnabled;
+ }
+
+ /**
+ * Marks the beginning of a series of log entries.
+ */
+ public void startLog(long timestamp, String sdkBuild) {
+ println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ startXMLElement("log", new String[] { "timestamp", "sdkbuild" }, new String[] {
+ new SimpleDateFormat("dd/MMM/yyyy HH:mm:ss").format(new Date(timestamp)),
+ sdkBuild });
+ }
+
+ /**
+ * Marks the end of a series of log entries.
+ */
+ public void endLog() {
+ endXMLElement();
+ }
+
+ /**
+ * Called by the JUnit framework when an error occurs.
+ * @param test the test
+ * @param error the exception that occurred
+ */
+ public void addError(Test test, Throwable error) {
+ printAbort("error", error);
+ super.addError(test, error);
+ }
+
+ /**
+ * Called by the JUnit framework when an assertion failure occurs.
+ * @param test the test
+ * @param error the exception that occurred
+ */
+ public void addFailure(Test test, AssertionFailedError error) {
+ printAbort("failure", error);
+ super.addFailure(test, error);
+ }
+
+ /**
+ * Called by the JUnit framework to mark the beginning of a test case.
+ * @param test the test
+ */
+ public void startTest(Test test) {
+ if (test instanceof TestCase) {
+ TestCase testCase = (TestCase) test;
+ startXMLElement("case", new String[] { "class", "name" },
+ new String[] { testCase.getClass().getName(), testCase.getName() });
+ groupStack.clear();
+ currentTask = null;
+ }
+ super.startTest(test);
+ }
+
+ /**
+ * Called by the JUnit framework to mark the end of a test case.
+ * @param test the test
+ */
+ public void endTest(Test test) {
+ if (test instanceof TestCase) {
+ TestCase testCase = (TestCase) test;
+ if (currentTask != null) endTask();
+ while (! groupStack.isEmpty()) endGroup();
+ endXMLElement();
+ }
+ super.endTest(test);
+ }
+
+ /**
+ * Marks the beginning of a new task group.
+ * @param groupName the name for the group
+ */
+ public void startGroup(String groupName) {
+ Assert.assertNull(currentTask);
+ startXMLElement("group", new String[] { "name" }, new String[] { groupName });
+ groupStack.push(groupName);
+ }
+
+ /**
+ * Marks the end of the active task group.
+ */
+ public void endGroup() {
+ Assert.assertNull(currentTask);
+ endXMLElement();
+ groupStack.pop();
+ }
+
+ /**
+ * Marks the beginning of a new task.
+ * @param taskName the name for the task
+ */
+ public void startTask(String taskName) {
+ Assert.assertNull(currentTask);
+ startXMLElement("task", new String[] { "name" }, new String[] { taskName });
+ currentTask = new PerformanceTimer(taskName);
+ currentTask.start();
+ }
+
+ /**
+ * Marks the end of the active task.
+ */
+ public void endTask() {
+ Assert.assertNotNull(currentTask);
+ currentTask.stop();
+ printXMLElement("result", new String[] { "elapsed" },
+ new String[] { Integer.toString(currentTask.getTotalMillis()) });
+ endXMLElement();
+ currentTask = null;
+ }
+
+ /**
+ * Returns the name of the active group, or null if none.
+ */
+ public String getGroupName() {
+ return groupStack.isEmpty() ? null : (String) groupStack.peek();
+ }
+
+ /**
+ * Returns the name of the active task, or null if none.
+ */
+ public String getTaskName() {
+ return (currentTask == null) ? null : currentTask.getName();
+ }
+
+ protected void startXMLElement(String name, String[] attributes, String[] values) {
+ println(formatXMLElement(name, attributes, values, false));
+ elements.push(name);
+ indent += " ";
+ }
+
+ protected void printXMLElement(String name, String[] attributes, String[] values) {
+ println(formatXMLElement(name, attributes, values, true));
+ }
+
+ protected String formatXMLElement(String name, String[] attributes, String[] values, boolean quickEnd) {
+ // XXX need to escape certain characters in attribute values
+ StringBuffer buffer = new StringBuffer("<");
+ buffer.append(name);
+ if (attributes != null && values != null) {
+ for (int i = 0; i < attributes.length; ++i) {
+ buffer.append(' ');
+ buffer.append(attributes[i]);
+ buffer.append("=\"");
+ buffer.append(values[i]);
+ buffer.append('"');
+ }
+ }
+ if (quickEnd) buffer.append('/');
+ buffer.append('>');
+ return buffer.toString();
+ }
+
+ protected void endXMLElement() {
+ indent = indent.substring(2);
+ String name = (String) elements.pop();
+ println("</" + name + ">");
+ }
+
+ protected void printAbort(String type, Throwable error) {
+ String message = error.getMessage();
+ if (message == null) message = "";
+ startXMLElement("abort", new String[] { "type", "message" },
+ new String[] { type, message });
+ if (loggingEnabled) {
+ // XXX need to escape certain characters
+ // XXX need a better way of serializing the stack trace
+ error.printStackTrace(logStream);
+ }
+ endXMLElement();
+ }
+
+ protected void println(String line) {
+ if (loggingEnabled) {
+ logStream.println(indent + line);
+ }
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/PerformanceTimer.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/PerformanceTimer.java
new file mode 100644
index 000000000..1a12e6508
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/PerformanceTimer.java
@@ -0,0 +1,49 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+public class PerformanceTimer {
+ private long startTime;
+ private int totalMillis;
+ private String name;
+
+ /**
+ * Creates a timer, initially not running.
+ */
+ public PerformanceTimer(String name) {
+ this.totalMillis = 0;
+ this.name = name;
+ }
+
+ /**
+ * Starts the timer. Timer must not be running.
+ */
+ public void start() {
+ startTime = System.currentTimeMillis();
+ }
+
+ /**
+ * Stops the timer. Timer must be running.
+ */
+ public void stop() {
+ totalMillis += System.currentTimeMillis() - startTime;
+ startTime = 0;
+ }
+
+ /**
+ * Returns the total number of milliseconds elapsed over all measured intervals.
+ */
+ public int getTotalMillis() {
+ return totalMillis;
+ }
+
+ /**
+ * Returns the name of this timer.
+ */
+ public String getName() {
+ return name;
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/SequenceGenerator.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/SequenceGenerator.java
new file mode 100644
index 000000000..e8e5c71e1
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/SequenceGenerator.java
@@ -0,0 +1,57 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.Random;
+
+/**
+ * Encapsulates algorithms and state for generating deterministic sequences.
+ * The sequence of numbers generated will always follow the same pattern,
+ * regardless of the time, place, or platform.
+ */
+public class SequenceGenerator {
+ private static long globalSeqNum = System.currentTimeMillis() * 1000;
+ private final Random random;
+ private int uniqueInt;
+
+ /**
+ * Constructs a new sequence generator with a known seed.
+ */
+ public SequenceGenerator() {
+ random = new Random(3141592653589793238L); // a known constant
+ uniqueInt = 1000000;
+ }
+
+ /**
+ * Returns a globally unique long integer.
+ */
+ public static long nextGloballyUniqueLong() {
+ return globalSeqNum++;
+ }
+
+ /**
+ * Returns a unique 7-digit integer.
+ */
+ public int nextUniqueInt() {
+ return uniqueInt++;
+ }
+
+ /**
+ * Returns a pseudo-random integer between 0 and n-1.
+ * @see Random#nextInt(int)
+ */
+ public int nextInt(int n) {
+ return random.nextInt(n);
+ }
+
+ /**
+ * Returns a pseudo-random real number following a gaussian distribution.
+ * @see Random#nextGaussian()
+ */
+ public double nextGaussian() {
+ return random.nextGaussian();
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/Util.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/Util.java
new file mode 100644
index 000000000..a20c7e4ec
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/Util.java
@@ -0,0 +1,709 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+import junit.framework.Assert;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.IDiffContainer;
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.ccvs.core.CVSTag;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
+import org.eclipse.team.internal.ccvs.ui.RepositoryManager;
+import org.eclipse.team.internal.ccvs.ui.sync.CVSSyncCompareInput;
+import org.eclipse.team.internal.ccvs.ui.sync.CommitSyncAction;
+import org.eclipse.team.ui.sync.ITeamNode;
+import org.eclipse.team.ui.sync.SyncSet;
+import org.eclipse.ui.wizards.datatransfer.ImportOperation;
+import org.eclipse.ui.wizards.datatransfer.ZipFileStructureProvider;
+
+/**
+ * Provides helpers for:
+ * <ul>
+ * <li>Resource manipulation</li>
+ * <li>Diff trees</li>
+ * <li>UI automation</li>
+ * <li>Parallel development simulation</li>
+ * </ul>
+ *
+ * Note: This class is referenced from the VCM 1.0 performance tests.
+ */ public class Util {
+ /*** RESOURCE MANIPULATION SUPPORT ***/
+
+ /**
+ * Creates a new project.
+ *
+ * @param name the project name
+ */
+ public static IProject createProject(String name) throws CoreException {
+ IWorkspace ws = ResourcesPlugin.getWorkspace();
+ IWorkspaceRoot root = ws.getRoot();
+ IProject proj = root.getProject(name);
+ if (!proj.exists()) proj.create(null);
+ if (!proj.isOpen()) proj.open(null);
+ return proj;
+ }
+
+ /**
+ * Deletes a project.
+ * @param project the project
+ */
+ public static void deleteProject(IProject project) throws CoreException {
+ project.delete(true, null);
+ }
+
+ /**
+ * Deletes a file and prunes empty containing folders.
+ * @param file the file to delete
+ */
+ public static void deleteFileAndPrune(IFile file) throws CoreException {
+ file.delete(true, null);
+ IContainer container = file.getParent();
+ while (container != null && container instanceof IFolder &&
+ isFolderEmpty((IFolder) container)) {
+ container.delete(true, null);
+ container = container.getParent();
+ }
+ }
+
+ /**
+ * Creates a uniquely named project.
+ * @param prefix a string prepended to the generated name
+ * @return the new project
+ */
+ public static IProject createUniqueProject(String prefix) throws CoreException {
+ return createProject(makeUniqueName(null, prefix, null));
+ }
+
+ /**
+ * Creates a uniquely named file in the parent folder or project with random contents.
+ * @param gen the sequence generator
+ * @param parent the parent IFolder or IProject for the new file
+ * @param meanSize the mean size of file to create (in bytes)
+ * @param variance 69% of files with be within this amount of the mean
+ * @param probBinary the probability of a new file being binary as a percentage
+ * @return the new file
+ */
+ public static IFile createUniqueFile(SequenceGenerator gen, IContainer parent,
+ int meanSize, int variance, int probBinary) throws IOException, CoreException {
+ int fileSize;
+ do {
+ fileSize = (int) Math.abs(gen.nextGaussian() * variance + meanSize);
+ } while (fileSize > meanSize + variance * 4); // avoid huge files
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ String fileName;
+ if (gen.nextInt(100) < probBinary) {
+ fileName = makeUniqueName(gen, "file", "class"); // binary
+ writeRandomBytes(gen, os, fileSize);
+ } else {
+ fileName = makeUniqueName(gen, "file", "txt"); // text
+ writeRandomText(gen, os, fileSize);
+ }
+ IFile file = parent.getFile(new Path(fileName));
+ file.create(new ByteArrayInputStream(os.toByteArray()), true, null);
+ os.close();
+ return file;
+ }
+
+ /**
+ * Creates a uniquely named folder in the parent folder.
+ * @param gen the sequence generator
+ * @param parent the parent IFolder or IProject for the new folder
+ * @return the new folder
+ */
+ public static IFolder createUniqueFolder(SequenceGenerator gen, IContainer parent) throws CoreException {
+ IFolder folder = parent.getFolder(new Path(Util.makeUniqueName(gen, "folder", null)));
+ folder.create(true, true, null);
+ return folder;
+ }
+
+ /**
+ * Creates a unique name.
+ * Ensures that a deterministic sequence of names is generated for all files
+ * and folders within a project, though not across sessions.
+ *
+ * @param gen the generator, or null if this name is to be globally unique
+ * @param prefix a string prepended to the generated name
+ * @param extension the file extension not including the period, null if none
+ * @return the new name
+ */
+ public static String makeUniqueName(SequenceGenerator gen, String prefix, String extension)
+ throws CoreException {
+ StringBuffer name = new StringBuffer(prefix);
+ name.append('-');
+ if (gen == null) {
+ name.append(SequenceGenerator.nextGloballyUniqueLong());
+ } else {
+ name.append(gen.nextUniqueInt());
+ }
+ if (extension != null) {
+ name.append('.');
+ name.append(extension);
+ }
+ return name.toString();
+ }
+
+ /**
+ * Imports a .zip file into a project's root folder.
+ * @param project the project
+ * @param file the path of the .zip file
+ */
+ public static void importZipIntoProject(IProject project, File file)
+ throws IOException, ZipException, InterruptedException, InvocationTargetException {
+ ZipFile zipFile = new ZipFile(file);
+ ZipFileStructureProvider provider = new ZipFileStructureProvider(zipFile);
+ ImportOperation importOperation = new ImportOperation(project.getFullPath(),
+ provider.getRoot(), provider, null);
+ importOperation.setOverwriteResources(true); // don't ask
+ importOperation.run(new NullProgressMonitor());
+ Assert.assertTrue(importOperation.getStatus().isOK());
+ }
+
+ /**
+ * Writes random text to an output stream.
+ * @param gen the sequence generator
+ */
+ public static void writeRandomText(SequenceGenerator gen, OutputStream os, int count) throws IOException {
+ while (count-- > 0) {
+ int c = gen.nextInt(99);
+ os.write((c >= 95) ? '\n' : c + ' ');
+ }
+ }
+
+ /**
+ * Writes random bytes to an output stream.
+ * @param gen the sequence generator
+ */
+ public static void writeRandomBytes(SequenceGenerator gen, OutputStream os, int count) throws IOException {
+ while (count-- > 0) {
+ os.write(gen.nextInt(256));
+ }
+ }
+
+ /**
+ * Creates a random folder deeply below the root folder.
+ * @param gen the sequence generator
+ * @param root the root IFolder or IProject for the operation
+ * @return the new folder
+ */
+ public static IFolder createRandomDeepFolder(SequenceGenerator gen, IContainer root) throws CoreException {
+ IContainer container = pickRandomDeepContainer(gen, root);
+ for (;;) {
+ IFolder folder = createUniqueFolder(gen, container);
+ container = folder;
+ // 12.5% chance of creating a nested folder
+ if (gen.nextInt(8) != 0) return folder;
+ }
+ }
+
+ /**
+ * Creates several random files deeply below the root folder.
+ * @param gen the sequence generator
+ * @param root the root IFolder or IProject for the operation
+ * @param count the number of files to create
+ * @param meanSize the mean size of file to create (in bytes)
+ * @param probBinary the probability of a new file being binary as a percentage
+ */
+ public static void createRandomDeepFiles(SequenceGenerator gen, IContainer root, int count,
+ int meanSize, int variance, int probBinary) throws IOException, CoreException {
+ while (count-- > 0) {
+ createUniqueFile(gen, pickRandomDeepContainer(gen, root), meanSize, variance, probBinary);
+ }
+ }
+
+ /**
+ * Deletes several random files deeply below the root folder.
+ * @param gen the sequence generator
+ * @param root the root IFolder or IProject for the operation
+ * @param count the number of files to delete
+ */
+ public static void deleteRandomDeepFiles(SequenceGenerator gen, IContainer root, int count) throws CoreException {
+ while (count-- > 0) {
+ IFile file = pickRandomDeepFile(gen, root);
+ if (file == null) break;
+ deleteFileAndPrune(file);
+ }
+ }
+
+ /**
+ * Modifies several random files deeply below the root folder.
+ * @param gen the sequence generator
+ * @param root the root IFolder or IProject for the operation
+ * @param count the number of files to modify
+ */
+ public static void modifyRandomDeepFiles(SequenceGenerator gen, IContainer root, int count)
+ throws IOException, CoreException {
+ // perhaps we can add a parameter for the "magnitude" of the change
+ while (count-- > 0) {
+ IFile file = pickRandomDeepFile(gen, root);
+ if (file == null) break;
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ InputStream is = file.getContents(true);
+ byte[] buffer = new byte[8192];
+ int rsize;
+ boolean changed = false;
+ while ((rsize = is.read(buffer)) != -1) {
+ double gaussian;
+ do {
+ gaussian = gen.nextGaussian() * 0.5; // large changes are less likely than small ones
+ } while (gaussian > 1.0 || gaussian < -1.0);
+ int changeSize = (int) (gaussian * rsize);
+ changed = changed || changeSize != 0;
+ os.write(buffer, 0, changeSize < 0 ? - changeSize : rsize); // shrink file
+ writeRandomText(gen, os, changeSize); // enlarge file
+ }
+ if (! changed) os.write('!'); // make sure we actually did change the file
+ file.setContents(new ByteArrayInputStream(os.toByteArray()), true, false, null);
+ os.close();
+ }
+ }
+
+ /**
+ * Touches several random files deeply below the root folder.
+ * @param gen the sequence generator
+ * @param root the root IFolder or IProject for the operation
+ * @param count the number of files to touch
+ */
+ public static void touchRandomDeepFiles(SequenceGenerator gen, IContainer root, int count) throws CoreException {
+ while (count-- > 0) {
+ IFile file = pickRandomDeepFile(gen, root);
+ if (file == null) break;
+ file.touch(null);
+ }
+ }
+
+ /**
+ * Renames several random files deeply below the root folder.
+ * @param gen the sequence generator
+ * @param root the root IFolder or IProject for the operation
+ * @param count the number of files to touch
+ */
+ public static void renameRandomDeepFiles(SequenceGenerator gen, IContainer root, int count) throws CoreException {
+ IProject project = root.getProject();
+ while (count-- > 0) {
+ IFile file = pickRandomDeepFile(gen, root);
+ if (file == null) break;
+ file.move(new Path(makeUniqueName(gen, "file", file.getFileExtension())), true, null);
+ }
+ }
+
+ /**
+ * Picks a random file from the parent folder or project.
+ * @param gen the sequence generator
+ * @param parent the parent IFolder or IProject for the operation
+ * @return the file that was chosen, or null if no suitable files
+ */
+ public static IFile pickRandomFile(SequenceGenerator gen, IContainer parent) throws CoreException {
+ IResource[] members = filterResources(parent.members());
+ for (int size = members.length; size != 0; --size) {
+ int elem = gen.nextInt(size);
+ if (members[elem] instanceof IFile) return (IFile) members[elem];
+ System.arraycopy(members, elem + 1, members, elem, size - elem - 1);
+ }
+ return null;
+ }
+
+ /**
+ * Picks a random folder from the parent folder or project.
+ * @param gen the sequence generator
+ * @param parent the parent IFolder or IProject for the operation
+ * @return the folder, or null if no suitable folders
+ */
+ public static IFolder pickRandomFolder(SequenceGenerator gen, IContainer parent) throws CoreException {
+ IResource[] members = filterResources(parent.members());
+ for (int size = members.length; size != 0; --size) {
+ int elem = gen.nextInt(size);
+ if (members[elem] instanceof IFolder) return (IFolder) members[elem];
+ System.arraycopy(members, elem + 1, members, elem, size - elem - 1);
+ }
+ return null;
+ }
+
+ /**
+ * Picks a random file deeply from the root folder or project.
+ * @param gen the sequence generator
+ * @param root the root IFolder or IProject for the operation
+ * @return the file that was chosen, or null if no suitable files
+ */
+ public static IFile pickRandomDeepFile(SequenceGenerator gen, IContainer root) throws CoreException {
+ IResource[] members = filterResources(root.members());
+ for (int size = members.length; size != 0; --size) {
+ int elem = gen.nextInt(size);
+ IResource resource = members[elem];
+ if (resource instanceof IFile) return (IFile) resource;
+ if (resource instanceof IFolder) {
+ IFile file = pickRandomDeepFile(gen, (IFolder) resource);
+ if (file != null) return file;
+ }
+ System.arraycopy(members, elem + 1, members, elem, size - elem - 1);
+ }
+ return null;
+ }
+
+ /**
+ * Picks a random folder deeply from the root folder or project.
+ * May pick the project's root container.
+ * @param gen the sequence generator
+ * @param root the root IFolder or IProject for the operation
+ * @return the container that was chosen, never null
+ */
+ public static IContainer pickRandomDeepContainer(SequenceGenerator gen, IContainer root) throws CoreException {
+ if (gen.nextInt(6) == 0) {
+ IResource[] members = filterResources(root.members());
+ for (int size = members.length; size != 0; --size) {
+ int elem = gen.nextInt(size);
+ IResource resource = members[elem];
+ if (resource instanceof IFolder) {
+ return pickRandomDeepContainer(gen, (IFolder) resource);
+ }
+ System.arraycopy(members, elem + 1, members, elem, size - elem - 1);
+ }
+ }
+ Assert.assertTrue(isValidContainer(root));
+ return root;
+ }
+
+ /**
+ * Returns true if the folder does not contain any real files.
+ */
+ public static boolean isFolderEmpty(IFolder folder) throws CoreException {
+ IResource[] members = folder.members();
+ for (int i = 0; i < members.length; ++i) {
+ if (isValidFile(members[i]) || isValidFolder(members[i])) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true iff file is a valid IFile (that should not be ignored).
+ */
+ public static boolean isValidFile(IResource file) throws CoreException {
+ return file instanceof IFile
+ // && file.isAccessible()
+ && ! file.isPhantom()
+ && ! file.getName().equals(".vcm_meta"); // XXX fixme when core meta-data support is implemented
+ }
+
+ /**
+ * Returns true iff folder is a valid IFolder (that should not be ignored).
+ */
+ public static boolean isValidFolder(IResource folder) throws CoreException {
+ return folder instanceof IFolder
+ // && folder.isAccessible()
+ && ! folder.isPhantom()
+ && ! folder.getName().equals("CVS")
+ && ! folder.getName().equals("bin"); // XXX fixme when core meta-data support is implemented
+ }
+
+ /**
+ * Returns true iff container is a valid IFolder or IProject (that should not be ignored).
+ */
+ public static boolean isValidContainer(IResource container) throws CoreException {
+ return container instanceof IProject || isValidFolder(container);
+ }
+
+ /**
+ * Returns true iff resource is a valid IFile, IFolder or IProject (that should not be ignored).
+ */
+ public static boolean isValidResource(IResource resource) throws CoreException {
+ return isValidFile(resource) || isValidContainer(resource);
+ }
+
+ /**
+ * Filters and sorts an array of resources to ensure deterministic behaviour across
+ * sessions. The general idea is to guarantee that given a known sequence of
+ * pseudo-random numbers, we will always pick the same sequence of files and
+ * folders each time we repeat the test.
+ */
+ public static IResource[] filterResources(IResource[] resources) throws CoreException {
+ List list = new ArrayList(resources.length);
+ for (int i = 0; i < resources.length; ++i) {
+ if (isValidResource(resources[i])) list.add(resources[i]);
+ }
+ if (list.size() != resources.length) {
+ resources = (IResource[]) list.toArray(new IResource[list.size()]);
+ }
+ Arrays.sort(resources, new Comparator() {
+ public int compare(Object a, Object b) {
+ return ((IResource) a).getName().compareTo(((IResource) b).getName());
+ }
+ });
+ return resources;
+ }
+
+ /*** DIFF SUPPORT ***/
+
+ public static boolean isEmpty(IDiffContainer node) {
+ if (node == null) return true;
+ if (node.getKind() != 0) return false;
+ IDiffElement[] children = node.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (!isEmpty(children[i])) return false;
+ }
+ return true;
+ }
+ public static boolean isEmpty(IDiffElement element) {
+ if (element == null) return true;
+ if (element.getKind() != 0) return false;
+ if (element instanceof IDiffContainer) {
+ IDiffElement[] children = ((DiffNode)element).getChildren();
+ for (int i = 0; i < children.length; i++) {
+ if (!isEmpty(children[i])) return false;
+ }
+ }
+ return true;
+ }
+
+ /*** UI SUPPORT ***/
+
+ /**
+ * Opens the specified wizard, then notifies the waiter.
+ * The WizardDialog instance is passed as argument to notify().
+ */
+ public static void waitForWizardToOpen(Shell parent, IWizard wizard, final Waiter waiter) {
+ WizardDialog dialog = new WizardDialog(parent, wizard) {
+ public int open() {
+ // create the window's controls
+ create();
+ // hook into the event loop so we get called back when the wizard is up and running
+ final Display display = getContents().getDisplay();
+ final WizardDialog dialog = this;
+ display.asyncExec(new Runnable() {
+ public void run() {
+ while (display.readAndDispatch()); // process any other pending messages first
+ waiter.notify(dialog);
+ }
+ });
+ // call open (does not create the window's controls a second time)
+ return super.open();
+ }
+ };
+ dialog.open();
+ }
+
+ /**
+ * Notifies the waiter when a Shell with the specified title opens.
+ * The Shell instance is passed as argument to notify().
+ */
+ public static void waitForShellToOpen(final Display display, final String title,
+ final Waiter waiter) {
+ final Runnable hook = new Runnable() {
+ public void run() {
+ Shell[] shells = display.getShells();
+ for (int i = 0; i < shells.length; ++i) {
+ final Shell shell = shells[i];
+ final String shellTitle = shell.getText();
+ if (title.equals(shellTitle)) {
+ if (! waiter.notify(shell)) return;
+ }
+ }
+ // poll again as soon as possible
+ if (waiter.keepWaiting()) {
+ display.timerExec(50, this); // reschedule with a timer instead of spinning
+ }
+ }
+ };
+ hook.run();
+ }
+
+ /**
+ * Finds a Control in a Composite hierarchy matching the specified criteria.
+ *
+ * @param root the root of the hierarchy to search
+ * @param clazz the Class representing the precise type of Control to find
+ * @param value a value used for matching
+ * @param criteria a strategy for matching the controls against a value,
+ * or null to match anything of the right class.
+ * @return the first matching Control, or null if none found.
+ */
+ public static Control findControl(Composite root, Class clazz, Object value, ICriteria criteria) {
+ if (clazz.isAssignableFrom(root.getClass())) {
+ if (criteria == null || criteria.test(root, value)) return root;
+ }
+ Control[] children = root.getChildren();
+ for (int i = 0; i < children.length; ++i) {
+ final Control candidate = children[i];
+ if (candidate instanceof Composite) {
+ Control c = findControl((Composite) candidate, clazz, value, criteria);
+ if (c != null) return c;
+ } else {
+ if (clazz.isAssignableFrom(candidate.getClass())) {
+ if (criteria == null || criteria.test(candidate, value)) return candidate;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds a Control in a Composite hierarchy with the specified text string.
+ * Note: clazz must specify a Control subclass that defines getText()
+ *
+ * @param root the root of the hierarchy to search
+ * @param clazz the Class representing the precise type of Control to find
+ * @param text the text string to find
+ * @return the first matching Control, or null if none found.
+ */
+ public static Control findControlWithText(Composite root, Class clazz, String text) {
+ return findControl(root, clazz, text, new ICriteria() {
+ public boolean test(Object control, Object value) {
+ // getText is only defined on certain subclasses of Composite
+ // so we must use reflection to find the method
+ try {
+ Method m = control.getClass().getMethod("getText", new Class[0]);
+ String text = (String) m.invoke(control, new Object[0]);
+ return value.equals(stripMnemonicEscapes(text));
+ } catch (Exception e) {
+ e.printStackTrace();
+ Assert.fail("Could not invoke method getText()");
+ }
+ return false;
+ }
+ });
+ }
+
+ /**
+ * Posts a fake event to the queue.
+ * Fills in the event type and widget fields.
+ * @param event the Event
+ */
+ public static void postEvent(final Widget widget, final int eventType, final Event event) {
+ Display display = widget.getDisplay();
+ event.type = eventType;
+ event.widget = widget;
+ display.asyncExec(new Runnable() {
+ public void run() {
+ widget.notifyListeners(eventType, event);
+ }
+ });
+ }
+
+ /**
+ * Strips mnemonic escapes from a text label.
+ */
+ public static String stripMnemonicEscapes(String label) {
+ StringBuffer buf = new StringBuffer();
+ int length = label.length();
+ for (int i = 0; i < length; ++i) {
+ char c = label.charAt(i);
+ if (c == '&') {
+ i += 1;
+ if (i < length) c = label.charAt(i);
+ }
+ buf.append(c);
+ }
+ return buf.toString();
+ }
+
+ /*** PARALLEL WORKFLOW SUPPORT (NON-UI) ***/
+
+ /**
+ * Creates a project to simulate parallel development by another developer.
+ * Not intended to test UI interaction.
+ */
+ public static IProject createParallelProject(String repoLocation, String moduleName) throws Exception {
+ IProject project = Util.createUniqueProject("testParallel");
+ CVSRepositoryLocation location = CVSRepositoryLocation.fromString(repoLocation);
+ CVSProviderPlugin.getProvider().createModule(location, project, moduleName,
+ new NullProgressMonitor());
+ return project;
+ }
+
+ /**
+ * Commits a project created to simulate parallel development.
+ * Not intended to test UI interaction.
+ */
+ public static void checkinParallelProject(IProject project, final String comment) throws Exception {
+ // FIXME: quick hack -- need a better way to do this
+ CVSSyncCompareInput input = new CVSSyncCompareInput(new IResource[] { project }) {
+ protected void updateView() {
+ // don't update the viewer
+ }
+ };
+ input.run(new NullProgressMonitor());
+ IDiffContainer diffRoot = input.getDiffRoot();
+ if (isEmpty(diffRoot)) return;
+ IDiffElement[] diffElements = diffRoot.getChildren();
+ ITeamNode projectNode = (ITeamNode) diffElements[0];
+ FakeSelectionProvider selectionProvider = new FakeSelectionProvider(new ITeamNode[] { projectNode });
+ CommitSyncAction action = new CommitSyncAction(input, selectionProvider, "Commit", null) {
+ protected void run(IRunnableWithProgress op, String problemMessage)
+ throws InterruptedException {
+ try {
+ op.run(new NullProgressMonitor());
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ throw new InterruptedException();
+ }
+ }
+ protected int promptForConflicts(SyncSet syncSet) {
+ return 0; // yes! sync conflicting changes
+ }
+ protected String promptForComment(RepositoryManager manager) {
+ return comment; // use our comment
+ }
+ };
+ action.run();
+ }
+
+ /**
+ * Updates a project created to simulate parallel development.
+ * Not intended to test UI interaction.
+ */
+ public static void checkoutParallelProject(IProject project) throws Exception {
+ CVSTeamProvider provider = (CVSTeamProvider) TeamPlugin.getManager().getProvider(project);
+ provider.get(new IResource[] { project }, IResource.DEPTH_INFINITE, null,
+ new NullProgressMonitor());
+ }
+
+ /**
+ * Versions a project created to simulate parallel development.
+ * Not intended to test UI interaction.
+ */
+ public static void versionParallelProject(IProject project, String name) throws Exception {
+ CVSTeamProvider provider = (CVSTeamProvider) TeamPlugin.getManager().getProvider(project);
+ provider.tag(new IResource[] { project }, IResource.DEPTH_INFINITE, new CVSTag(name, CVSTag.VERSION),
+ new NullProgressMonitor());
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/Waiter.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/Waiter.java
new file mode 100644
index 000000000..69f184424
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/Waiter.java
@@ -0,0 +1,30 @@
+package org.eclipse.team.tests.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Abstract listener used for the generic problem of waiting for
+ * something to happen and retrieving some related information.
+ * e.g. Waiting for a window with a given title to open and getting its handle.
+ */
+public abstract class Waiter {
+ /**
+ * Called when the desired event has occurred.
+ * @param object an object related to the event, type depends on the context
+ * @return true to keep waiting, otherwise false
+ */
+ public abstract boolean notify(Object object);
+
+ /**
+ * Called after each unsuccessful poll for the event.
+ * @return true to keep waiting, otherwise false
+ */
+ public boolean keepWaiting() {
+ return true;
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/AllTests.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/AllTests.java
new file mode 100644
index 000000000..bbc867946
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/AllTests.java
@@ -0,0 +1,25 @@
+package org.eclipse.team.tests.ccvs.ui.benchmark;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests extends TestSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTestSuite(SyncTests.class);
+ suite.addTestSuite(WorkflowTests.class);
+ return new BenchmarkTestSetup(suite);
+ }
+
+ public AllTests(String name) {
+ super(name);
+ }
+ public AllTests() {
+ super();
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/BenchmarkTestSetup.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/BenchmarkTestSetup.java
new file mode 100644
index 000000000..6b1c46260
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/BenchmarkTestSetup.java
@@ -0,0 +1,49 @@
+package org.eclipse.team.tests.ccvs.ui.benchmark;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import junit.framework.Test;
+import org.eclipse.core.runtime.IPluginDescriptor;
+import org.eclipse.core.runtime.IPluginRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.team.tests.ccvs.core.CVSTestSetup;
+
+public class BenchmarkTestSetup extends CVSTestSetup {
+ public static final File BIG_ZIP_FILE;
+ public static final File SMALL_ZIP_FILE;
+ public static final File TINY_ZIP_FILE;
+
+ // Static initializer for constants
+ static {
+ try {
+ BIG_ZIP_FILE = getTestFile("benchmarkBig.zip");
+ SMALL_ZIP_FILE = getTestFile("benchmarkSmall.zip");
+ TINY_ZIP_FILE = getTestFile("benchmarkTiny.zip");
+ } catch (IOException e) {
+ throw new Error(e.getMessage());
+ }
+ }
+
+ public static File getTestFile(String name) throws IOException {
+ IPluginRegistry registry = Platform.getPluginRegistry();
+ IPluginDescriptor descriptor = registry.getPluginDescriptor("org.eclipse.team.tests.cvs.core");
+ URL baseURL = descriptor.getInstallURL();
+ URL url = new URL(baseURL, "resources/BenchmarkTest/" + name);
+ url = Platform.asLocalURL(url);
+ if (url.getProtocol().equals("file")) {
+ return new File(url.getFile()).getAbsoluteFile();
+ }
+ throw new IOException("Cannot find test file: " + name);
+ }
+
+ public BenchmarkTestSetup(Test test) {
+ super(test);
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/SyncTests.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/SyncTests.java
new file mode 100644
index 000000000..82ca27675
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/SyncTests.java
@@ -0,0 +1,117 @@
+package org.eclipse.team.tests.ccvs.ui.benchmark;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.team.tests.ccvs.ui.CVSUITestCase;
+import org.eclipse.team.tests.ccvs.ui.Util;
+import org.eclipse.team.tests.ccvs.ui.SequenceGenerator;
+import org.eclipse.team.tests.ccvs.ui.Util;
+
+public class SyncTests extends CVSUITestCase {
+ private int FILE_SIZE_MEAN = 20000;
+ private int FILE_SIZE_VARIANCE = 0;
+ private int PROB_BINARY = 0;
+ private IProject project;
+ private IProject parallelProject;
+
+ public SyncTests(String name) {
+ super(name);
+ }
+ public SyncTests() {
+ super("");
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ project = createAndImportProject("testSync", BenchmarkTestSetup.TINY_ZIP_FILE);
+ startGroup("initial project commit");
+ actionShareProject(project);
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+ parallelProject = Util.createParallelProject(
+ testRepository.getLocation(), project.getName());
+ }
+
+ public static Test suite() {
+ return new BenchmarkTestSetup(new TestSuite(SyncTests.class));
+ }
+
+ public void testSync0() throws Exception {
+ // test sync on project with no changes
+ startGroup("test sync with no changes");
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+ }
+
+ public void testSync1() throws Exception {
+ runTestSync(1);
+ }
+
+ public void testSync10() throws Exception {
+ runTestSync(10);
+ }
+
+ public void testSync100() throws Exception {
+ runTestSync(100);
+ }
+
+ /**
+ * Runs a sequence of operations for the synchronizer tests.
+ * A parallel project is used to generate incoming changes.
+ */
+ protected void runTestSync(int size) throws Exception {
+ final SequenceGenerator gen = new SequenceGenerator();
+
+ /*** outgoing changes ***/
+ startGroup("checking in " + size + " added file(s)");
+ Util.createRandomDeepFiles(gen, project, size, FILE_SIZE_MEAN, FILE_SIZE_VARIANCE, PROB_BINARY);
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+ startGroup("checking in " + size + " modified file(s)");
+ Util.modifyRandomDeepFiles(gen, project, size);
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+ startGroup("checking in " + size + " renamed file(s)");
+ Util.renameRandomDeepFiles(gen, project, size);
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+ startGroup("checking in " + size + " touched file(s)");
+ Util.touchRandomDeepFiles(gen, project, size);
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+ startGroup("checking in " + size + " removed file(s)");
+ Util.deleteRandomDeepFiles(gen, project, size);
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+
+ /*** incoming changes ***/
+ Util.checkoutParallelProject(parallelProject);
+ startGroup("checking out " + size + " added file(s)");
+ Util.createRandomDeepFiles(gen, project, size, FILE_SIZE_MEAN, FILE_SIZE_VARIANCE, PROB_BINARY);
+ Util.checkinParallelProject(parallelProject, getGroupName());
+ syncUpdateResources(new IResource[] { project }, null);
+ endGroup();
+ startGroup("checking out " + size + " modified file(s)");
+ Util.modifyRandomDeepFiles(gen, parallelProject, size);
+ Util.checkinParallelProject(parallelProject, getGroupName());
+ syncUpdateResources(new IResource[] { project }, null);
+ endGroup();
+ startGroup("checking out " + size + " renamed file(s)");
+ Util.renameRandomDeepFiles(gen, parallelProject, size);
+ Util.checkinParallelProject(parallelProject, getGroupName());
+ syncUpdateResources(new IResource[] { project }, null);
+ endGroup();
+ startGroup("checking out " + size + " removed file(s)");
+ Util.deleteRandomDeepFiles(gen, parallelProject, size);
+ Util.checkinParallelProject(parallelProject, getGroupName());
+ syncUpdateResources(new IResource[] { project }, null);
+ endGroup();
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/WorkflowTests.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/WorkflowTests.java
new file mode 100644
index 000000000..a86249d3a
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/benchmark/WorkflowTests.java
@@ -0,0 +1,180 @@
+package org.eclipse.team.tests.ccvs.ui.benchmark;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.team.ccvs.core.CVSTag;
+import org.eclipse.team.tests.ccvs.ui.CVSUITestCase;
+import org.eclipse.team.tests.ccvs.ui.Util;
+import org.eclipse.team.tests.ccvs.ui.SequenceGenerator;
+import org.eclipse.team.tests.ccvs.ui.Util;
+
+public class WorkflowTests extends CVSUITestCase {
+ private int FILE_SIZE_MEAN = 20000;
+ private int FILE_SIZE_VARIANCE = 10000;
+ private int PROB_BINARY = 5;
+ public WorkflowTests(String name) {
+ super(name);
+ }
+ public WorkflowTests() {
+ super("");
+ }
+
+ public static Test suite() {
+ return new BenchmarkTestSetup(new TestSuite(WorkflowTests.class));
+ }
+
+ public void testBigWorkflow() throws Exception {
+ runWorkflowTests(createAndImportProject("testBig", BenchmarkTestSetup.BIG_ZIP_FILE));
+ }
+
+ public void testSmallWorkflow() throws Exception {
+ runWorkflowTests(createAndImportProject("testSmall", BenchmarkTestSetup.SMALL_ZIP_FILE));
+ }
+
+ public void testTinyWorkflow() throws Exception {
+ runWorkflowTests(createAndImportProject("testTiny", BenchmarkTestSetup.TINY_ZIP_FILE));
+ }
+
+ /**
+ * Runs a series of workflow-related tests on a project.
+ */
+ protected void runWorkflowTests(IProject project) throws Exception {
+ final SequenceGenerator gen = new SequenceGenerator();
+ // test project sharing
+ startGroup("test project sharing");
+ actionShareProject(project);
+ endGroup();
+
+ // test initial project commit
+ startGroup("test initial project commit");
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+
+ // test initial project checkout
+ deleteProject(project);
+ startGroup("test initial project checkout");
+ actionCheckoutProjects(new String[] { project.getName() }, new CVSTag[] { new CVSTag() });
+ endGroup();
+ project = Util.createProject(project.getName());
+
+ // create a project for testing incoming changes
+ IProject parallelProject = Util.createParallelProject(
+ testRepository.getLocation(), project.getName());
+
+ // test typical outgoing workflows
+ runOutgoingWorkflowTests(gen, project, parallelProject);
+
+ // test tagging a project
+ startGroup("tag project");
+ actionCVSTag(new IResource[] { project }, "v101");
+ endGroup();
+
+ // test typical incoming workflows
+ runIncomingWorkflowTests(gen, project, parallelProject);
+ }
+
+ protected void runOutgoingWorkflowTests(SequenceGenerator gen, IProject project, IProject parallelProject)
+ throws Exception {
+ startGroup("test outgoing change scenarios");
+ startGroup("adding a new component - localized additions and some changes");
+ Util.modifyRandomDeepFiles(gen, project, 5);
+ Util.touchRandomDeepFiles(gen, project, 2);
+ IFolder componentRoot = Util.createRandomDeepFolder(gen, project);
+ Util.createRandomDeepFiles(gen, componentRoot, 12, FILE_SIZE_MEAN, FILE_SIZE_VARIANCE, PROB_BINARY);
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+
+ startGroup("fixing a bug - localized changes");
+ Util.modifyRandomDeepFiles(gen, componentRoot, 2);
+ Util.touchRandomDeepFiles(gen, componentRoot, 2);
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+
+// beginGroup("moving a package - scattered changes, files moved");
+// FileUtil.modifyRandomDeepFiles(gen, project, 5); // a few scattered changes
+// FileUtil.modifyRandomDeepFiles(gen, componentRoot, 12); // changes to "package" stmt
+// componentRoot.move(new Path(FileUtil.makeUniqueName("folder", null, project)), true, null);
+// syncCommitResources(new IResource[] { project }, null, getGroupName());
+// endGroup();
+
+ startGroup("big refactoring - scattered changes, files renamed and balanced additions/deletions");
+ Util.deleteRandomDeepFiles(gen, project, 4); // some stuff deleted
+ Util.modifyRandomDeepFiles(gen, project, 20); // many scattered changes
+ Util.renameRandomDeepFiles(gen, project, 5); // renamed some stuff
+ Util.createRandomDeepFiles(gen, project, 4, FILE_SIZE_MEAN, FILE_SIZE_VARIANCE, PROB_BINARY); // some new stuff added
+ syncCommitResources(new IResource[] { project }, null, getGroupName());
+ endGroup();
+ endGroup();
+ }
+
+ protected void runIncomingWorkflowTests(SequenceGenerator gen, IProject project, IProject parallelProject)
+ throws Exception {
+ startGroup("test incoming change scenarios");
+ Util.checkoutParallelProject(parallelProject);
+
+ startGroup("catching up to a new component - localized additions and some changes");
+ Util.modifyRandomDeepFiles(gen, parallelProject, 5);
+ Util.touchRandomDeepFiles(gen, parallelProject, 2);
+ IFolder componentRoot = Util.createRandomDeepFolder(gen, parallelProject);
+ Util.createRandomDeepFiles(gen, componentRoot, 12, FILE_SIZE_MEAN, FILE_SIZE_VARIANCE, PROB_BINARY);
+ Util.checkinParallelProject(parallelProject, getGroupName());
+ syncUpdateResources(new IResource[] { project }, null);
+ endGroup();
+
+ startGroup("catching up to a bug fix - localized changes");
+ Util.modifyRandomDeepFiles(gen, componentRoot, 2);
+ Util.touchRandomDeepFiles(gen, componentRoot, 2);
+ Util.checkinParallelProject(parallelProject, getGroupName());
+ syncUpdateResources(new IResource[] { project }, null);
+ endGroup();
+
+// beginGroup("catching up to a moved package - scattered changes, files moved");
+// FileUtil.modifyRandomDeepFiles(gen, parallelProject, 5); // a few scattered changes
+// FileUtil.modifyRandomDeepFiles(gen, componentRoot, 12); // changes to "package" stmt
+// componentRoot.move(new Path(FileUtil.makeUniqueName("folder", null, parallelProject)), true, null);
+// Util.checkinParallelProject(parallelProject, getGroupName());
+// syncUpdateResources(new IResource[] { project }, null);
+// endGroup();
+
+ startGroup("catching up to a big refactoring - scattered changes, files renamed and balanced additions/deletions");
+ Util.deleteRandomDeepFiles(gen, parallelProject, 4); // some stuff deleted
+ Util.modifyRandomDeepFiles(gen, parallelProject, 20); // many scattered changes
+ Util.renameRandomDeepFiles(gen, parallelProject, 5); // renamed some stuff
+ Util.createRandomDeepFiles(gen, parallelProject, 4, FILE_SIZE_MEAN, FILE_SIZE_VARIANCE, PROB_BINARY); // some new stuff added
+ Util.checkinParallelProject(parallelProject, getGroupName());
+ syncUpdateResources(new IResource[] { project }, null);
+ endGroup();
+
+ startGroup("replacing with remote contents - no local dirty files, no remote changes");
+ actionReplaceWithRemote(new IResource[] { project});
+ endGroup();
+
+ startGroup("replacing with remote contents - abandoning some local work, no remote changes");
+ Util.deleteRandomDeepFiles(gen, project, 4); // some stuff locally deleted
+ Util.modifyRandomDeepFiles(gen, project, 6); // a few unimportant changes to forget
+ Util.createRandomDeepFiles(gen, project, 2, FILE_SIZE_MEAN, FILE_SIZE_VARIANCE, PROB_BINARY); // some new work to abandon
+ actionReplaceWithRemote(new IResource[] { project});
+ endGroup();
+
+ startGroup("replacing with remote contents - no local dirty files, many remote changes");
+ // e.g. returning from a long vacation
+ Util.deleteRandomDeepFiles(gen, parallelProject, 10); // some components obsoleted
+ Util.modifyRandomDeepFiles(gen, parallelProject, 42); // many changes
+ Util.renameRandomDeepFiles(gen, parallelProject, 8); // evidence of some refactoring
+ Util.createRandomDeepFiles(gen, parallelProject, 10, FILE_SIZE_MEAN, FILE_SIZE_VARIANCE, PROB_BINARY); // a few new components added
+ Util.checkinParallelProject(parallelProject, getGroupName());
+ actionReplaceWithRemote(new IResource[] { project});
+ endGroup();
+ endGroup();
+ }
+
+
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/CaseEntry.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/CaseEntry.java
new file mode 100644
index 000000000..3ae0ba97e
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/CaseEntry.java
@@ -0,0 +1,32 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import org.xml.sax.Attributes;
+
+public class CaseEntry extends LogEntryContainer {
+ private String className;
+
+ public CaseEntry(LogEntryContainer parent, Attributes attributes) {
+ this(parent, attributes.getValue("name"), attributes.getValue("class"));
+ }
+
+ public CaseEntry(LogEntryContainer parent, String name, String className) {
+ super(parent, name);
+ this.className = (className != null) ? className : "unknown";
+ }
+
+ public void accept(ILogEntryVisitor visitor) {
+ visitor.visitCaseEntry(this);
+ }
+
+ /**
+ * Returns the class name of the test case.
+ */
+ public String getClassName() {
+ return className;
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/GroupEntry.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/GroupEntry.java
new file mode 100644
index 000000000..20c15f3b5
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/GroupEntry.java
@@ -0,0 +1,22 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import org.xml.sax.Attributes;
+
+public class GroupEntry extends LogEntryContainer {
+ public GroupEntry(LogEntryContainer parent, Attributes attributes) {
+ this(parent, attributes.getValue("name"));
+ }
+
+ public GroupEntry(LogEntryContainer parent, String name) {
+ super(parent, name);
+ }
+
+ public void accept(ILogEntryVisitor visitor) {
+ visitor.visitGroupEntry(this);
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/ILogEntryVisitor.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/ILogEntryVisitor.java
new file mode 100644
index 000000000..884f86206
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/ILogEntryVisitor.java
@@ -0,0 +1,13 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+public interface ILogEntryVisitor {
+ public void visitRootEntry(RootEntry entry);
+ public void visitCaseEntry(CaseEntry entry);
+ public void visitGroupEntry(GroupEntry entry);
+ public void visitTaskEntry(TaskEntry entry);
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/LogEntry.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/LogEntry.java
new file mode 100644
index 000000000..97ebb43f7
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/LogEntry.java
@@ -0,0 +1,117 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.SAXParserFactory;
+
+import junit.framework.Assert;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+public abstract class LogEntry {
+ LogEntryContainer parent;
+ String name;
+
+ /**
+ * Creates a new log entry with the specified parent.
+ * @param parent the parent container
+ * @param name the name of the entry
+ */
+ public LogEntry(LogEntryContainer parent, String name) {
+ this.parent = parent;
+ this.name = name != null ? name : "unknown";
+ if (parent != null) parent.addEntry(this);
+ }
+
+ /**
+ * Accepts a visitor.
+ * @param visitor the visitor
+ */
+ public abstract void accept(ILogEntryVisitor visitor);
+
+ /**
+ * Returns the name of this entry.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the parent container of this entry, or null if none.
+ */
+ public LogEntryContainer getParent() {
+ return parent;
+ }
+
+ /**
+ * Reads an array of log entries from a file.
+ * @return the log entries
+ */
+ public static RootEntry readLog(File file) throws IOException, SAXException {
+ XMLReader reader = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
+ LogContentHandler contentHandler = new LogContentHandler();
+ reader.setContentHandler(contentHandler);
+ reader.parse(new InputSource(new FileInputStream(file)));
+ return contentHandler.getLogEntries();
+ }
+
+ private static class LogContentHandler extends DefaultHandler implements ContentHandler {
+ private RootEntry root = null;
+ private LogEntry current = null;
+
+ public RootEntry getLogEntries() {
+ return root;
+ }
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ if ("log".equals(localName)) {
+ Assert.assertNull(current);
+ current = root = new RootEntry(null, attributes);
+ } else if ("case".equals(localName)) {
+ Assert.assertNotNull(current);
+ Assert.assertTrue(current instanceof RootEntry);
+ current = new CaseEntry((LogEntryContainer) current, attributes);
+ } else if ("group".equals(localName)) {
+ Assert.assertNotNull(current);
+ Assert.assertTrue(current instanceof CaseEntry || current instanceof GroupEntry);
+ current = new GroupEntry((LogEntryContainer) current, attributes);
+ } else if ("task".equals(localName)) {
+ Assert.assertNotNull(current);
+ Assert.assertTrue(current instanceof CaseEntry || current instanceof GroupEntry);
+ current = new TaskEntry((LogEntryContainer) current, attributes);
+ } else if ("result".equals(localName)) {
+ Assert.assertNotNull(current);
+ Assert.assertTrue(current instanceof TaskEntry);
+ ((TaskEntry) current).addResult(attributes);
+ } else if ("abort".equals(localName)) {
+ // currently we ignore failure entries
+ // XXX need a good way to represent failures
+ } else {
+ throw new SAXException("Unrecognized element: " + localName);
+ }
+ }
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException {
+ Assert.assertNotNull(current);
+ if ("result".equals(localName) || "abort".equals(localName)) {
+ // nothing to do
+ } else {
+ current = current.getParent();
+ }
+ }
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/LogEntryContainer.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/LogEntryContainer.java
new file mode 100644
index 000000000..71c6eb471
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/LogEntryContainer.java
@@ -0,0 +1,57 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class LogEntryContainer extends LogEntry {
+ private List /* of LogEntry */ list = new ArrayList();
+
+ public LogEntryContainer(LogEntryContainer parent, String name) {
+ super(parent, name);
+ }
+
+ /**
+ * Accepts a visitor for each child in the order in which they are listed.
+ * @param visitor the visitor
+ */
+ public void acceptChildren(ILogEntryVisitor visitor) {
+ Iterator it = list.iterator();
+ while (it.hasNext()) {
+ LogEntry entry = (LogEntry) it.next();
+ entry.accept(visitor);
+ }
+ }
+
+ /**
+ * Returns the list of children in this container.
+ */
+ public LogEntry[] members() {
+ return (LogEntry[]) list.toArray(new LogEntry[list.size()]);
+ }
+
+ /**
+ * Returns the member with the specified name and class.
+ */
+ public LogEntry findMember(String name, Class clazz) {
+ Iterator it = list.iterator();
+ while (it.hasNext()) {
+ LogEntry entry = (LogEntry) it.next();
+ if (name.equals(entry.getName()) &&
+ clazz.isAssignableFrom(entry.getClass())) return entry;
+ }
+ return null;
+ }
+
+ /*
+ * Adds the specified entry to the end of the list.
+ */
+ void addEntry(LogEntry entry) {
+ list.add(entry);
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/MergeRunsVisitor.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/MergeRunsVisitor.java
new file mode 100644
index 000000000..c500cd9e4
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/MergeRunsVisitor.java
@@ -0,0 +1,67 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+public class MergeRunsVisitor implements ILogEntryVisitor {
+ private RootEntry defaultRoot;
+ private RootEntry root;
+ private LogEntryContainer parent;
+
+ /**
+ * Creates a new visitor to merge series of log entries.
+ * @param root the root of an existing log to merge into, or null
+ */
+ public MergeRunsVisitor(RootEntry root) {
+ this.defaultRoot = defaultRoot;
+ this.parent = null;
+ }
+
+ /**
+ * Returns the root of the newly merged log, or null if none.
+ */
+ public RootEntry getMergedRoot() {
+ return root;
+ }
+
+ public void visitRootEntry(RootEntry entry) {
+ root = defaultRoot;
+ if (root == null) {
+ root = new RootEntry(null, entry.getName(), entry.getSDKBuildId(), entry.getTimestamp());
+ }
+ parent = root;
+ entry.acceptChildren(this);
+ }
+
+ public void visitCaseEntry(CaseEntry entry) {
+ LogEntryContainer oldParent = parent;
+ CaseEntry newEntry = (CaseEntry) parent.findMember(entry.getName(), CaseEntry.class);
+ if (newEntry == null) {
+ newEntry = new CaseEntry(parent, entry.getName(), entry.getClassName());
+ }
+ parent = newEntry;
+ entry.acceptChildren(this);
+ parent = oldParent;
+ }
+
+ public void visitGroupEntry(GroupEntry entry) {
+ LogEntryContainer oldParent = parent;
+ GroupEntry newEntry = (GroupEntry) parent.findMember(entry.getName(), GroupEntry.class);
+ if (newEntry == null) {
+ newEntry = new GroupEntry(parent, entry.getName());
+ }
+ parent = newEntry;
+ entry.acceptChildren(this);
+ parent = oldParent;
+ }
+
+ public void visitTaskEntry(TaskEntry entry) {
+ TaskEntry newEntry = (TaskEntry) parent.findMember(entry.getName(), TaskEntry.class);
+ if (newEntry == null) {
+ newEntry = new TaskEntry(parent, entry.getName());
+ }
+ newEntry.addRuns(entry.getTotalMillis(), entry.getTotalRuns());
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintAverageMain.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintAverageMain.java
new file mode 100644
index 000000000..0a7a2b63a
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintAverageMain.java
@@ -0,0 +1,31 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+
+public class PrintAverageMain {
+ public static void main(String[] args) {
+ if (args.length != 1) {
+ System.err.println("Usage: java PrintAverageMain <log>");
+ return;
+ }
+ File file = new File(args[0]);
+ try {
+ // read and merge the log
+ RootEntry root = LogEntry.readLog(file);
+ MergeRunsVisitor mergeVisitor = new MergeRunsVisitor(null);
+ root.accept(mergeVisitor);
+ root = mergeVisitor.getMergedRoot();
+ // print the log summary
+ root.accept(new PrintSummaryVisitor(System.out));
+ } catch (Exception e) {
+ System.err.println("An error occurred while parsing: " + file);
+ e.printStackTrace();
+ return;
+ }
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintDiffMain.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintDiffMain.java
new file mode 100644
index 000000000..83f7f61b2
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintDiffMain.java
@@ -0,0 +1,52 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+
+public class PrintDiffMain {
+ public static void main(String[] args) {
+ File newerFile, olderFile;
+ int thresh = 1;
+ boolean ignore = false;
+ try {
+ // lazy argument parsing
+ newerFile = new File(args[args.length - 2]);
+ olderFile = new File(args[args.length - 1]);
+ for (int i = 0; i < args.length - 2; ++i) {
+ if ("-i".equals(args[i])) {
+ ignore = true;
+ } else if ("-t".equals(args[i]) && i < args.length - 3) {
+ thresh = Integer.parseInt(args[++i], 10);
+ } else throw new IllegalArgumentException(args[i]);
+ }
+ } catch (Exception e) {
+ System.err.println("Usage: java CompareSummaryMain [-t thresh] [-i] <newer log> <older log>");
+ System.err.println(" -t thresh: minimum absolute non-negligible difference in ms");
+ System.err.println(" -i : ignore negligible changes in results");
+ return;
+ }
+
+ try {
+ // read and merge newer log
+ RootEntry newerRoot = LogEntry.readLog(newerFile);
+ MergeRunsVisitor mergeVisitor = new MergeRunsVisitor(null);
+ newerRoot.accept(mergeVisitor);
+ newerRoot = mergeVisitor.getMergedRoot();
+ // read and merge older log
+ RootEntry olderRoot = LogEntry.readLog(olderFile);
+ olderRoot.accept(mergeVisitor);
+ olderRoot = mergeVisitor.getMergedRoot();
+ // compute and print the differences
+ PrintDiffVisitor diffVisitor = new PrintDiffVisitor(System.out, olderRoot, thresh, ignore);
+ newerRoot.accept(diffVisitor);
+ } catch (Exception e) {
+ System.err.println("An error occurred while parsing logs");
+ e.printStackTrace();
+ return;
+ }
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintDiffVisitor.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintDiffVisitor.java
new file mode 100644
index 000000000..ae7533e47
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintDiffVisitor.java
@@ -0,0 +1,172 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class PrintDiffVisitor implements ILogEntryVisitor {
+ private PrintStream os;
+ private RootEntry olderRoot;
+ private int threshold; // threshold for negligible changes
+ private boolean ignoreNegligible; // if true, ignores negligible changes
+ private LogEntryContainer olderParent; // corresponding parent in older root
+ private List diffText; // list of things to print
+ private String indent;
+
+ /**
+ * Creates a visitor to print a summary of the changes between a log
+ * and an older one. Optionally ignores differences within a certain threshold.
+ * Does not print older entries for which there are no corresponding newer ones.
+ *
+ * @param os the output stream
+ * @param olderRoot the root of the older log
+ * @param threshold the smallest non-negligible difference
+ * @param ignoreNegligible if true, does not display negligible changes
+ */
+ public PrintDiffVisitor(PrintStream os, RootEntry olderRoot, int threshold, boolean ignoreNegligible) {
+ this.os = os;
+ this.olderRoot = olderRoot;
+ this.olderParent = null;
+ this.threshold = threshold;
+ this.ignoreNegligible = ignoreNegligible;
+ this.diffText = null;
+ this.indent = "";
+ }
+
+ private void visitContainer(LogEntryContainer container) {
+ LogEntryContainer prevOlderParent = olderParent;
+ indent += " ";
+ if (olderParent != null) {
+ olderParent = (LogEntryContainer) olderParent.findMember(
+ container.getName(), container.getClass());
+ }
+ container.acceptChildren(this);
+ indent = indent.substring(2);
+ olderParent = prevOlderParent;
+ }
+
+ /**
+ * Prints the root entry information.
+ */
+ public void visitRootEntry(RootEntry entry) {
+ os.println("=== TEST LOG DIFF ===");
+ os.println("Newer file:");
+ os.println(" Generated: " + entry.getTimestamp());
+ os.println(" SDK Build: " + entry.getSDKBuildId());
+ os.println("Older file:");
+ os.println(" Generated: " + olderRoot.getTimestamp());
+ os.println(" SDK Build: " + olderRoot.getSDKBuildId());
+ os.println();
+ olderParent = olderRoot;
+ entry.acceptChildren(this);
+ }
+
+ /**
+ * Visits all of the subgroups and subtasks of the newer log's test cases
+ */
+ public void visitCaseEntry(CaseEntry entry) {
+ StringBuffer line = new StringBuffer(indent);
+ line.append("%%% ");
+ line.append(entry.getName());
+ line.append(", class=");
+ line.append(entry.getClassName());
+ line.append(':');
+ os.println(line);
+ diffText = null;
+ visitContainer(entry);
+ if (diffText != null) {
+ Iterator it = diffText.iterator();
+ while (it.hasNext()) os.println((String) it.next());
+ }
+ diffText = null;
+ os.println();
+ }
+
+ /**
+ * Visits all of the subgroups and subtasks of the newer log's test cases
+ */
+ public void visitGroupEntry(GroupEntry entry) {
+ List oldDiffText = diffText;
+ diffText = null;
+ visitContainer(entry);
+ if (diffText != null) {
+ StringBuffer line = new StringBuffer(indent);
+ line.append("+ ");
+ line.append(entry.getName());
+ line.append(':');
+ diffText.add(0, line.toString());
+ if (oldDiffText != null) diffText.addAll(0, oldDiffText);
+ } else {
+ diffText = oldDiffText;
+ }
+ }
+
+ /**
+ * Prints the average amount of time spent by a task.
+ */
+ public void visitTaskEntry(TaskEntry task) {
+ int diff = Integer.MAX_VALUE;
+ TaskEntry olderTask = null;
+ if (olderParent != null) {
+ olderTask = (TaskEntry) olderParent.findMember(task.getName(), TaskEntry.class);
+ if (olderTask != null && task.getTotalRuns() != 0 && olderTask.getTotalRuns() != 0) {
+ diff = task.getAverageMillis() - olderTask.getAverageMillis();
+ // ignore negligible changes
+ if (ignoreNegligible && Math.abs(diff) <= threshold) return;
+ }
+ }
+ // print task description
+ if (diffText == null) diffText = new LinkedList(); // using a list for speedy prepending
+ StringBuffer line = new StringBuffer(indent);
+ line.append("- ");
+ line.append(task.getName());
+ line.append(": ");
+ diffText.add(line.toString());
+
+ // print new entry performance
+ printTaskEntry(" newer: ", task);
+
+ // print older entry performance
+ if (olderTask == null) return;
+ printTaskEntry(" older: ", olderTask);
+
+ // print difference
+ if (diff == Integer.MAX_VALUE) return;
+ line = new StringBuffer(indent);
+ line.append(" diff : ");
+ if (Math.abs(diff) > threshold) {
+ if (diff > 0) line.append("SLOWER");
+ else line.append("FASTER");
+ line.append(" by ");
+ line.append(Integer.toString(Math.abs(diff)));
+ line.append(" ms avg.");
+ } else {
+ line.append("NEGLIGIBLE");
+ }
+ diffText.add(line.toString());
+ }
+
+ protected void printTaskEntry(String prefix, TaskEntry task) {
+ StringBuffer line = new StringBuffer(indent);
+ line.append(prefix);
+ if (task.getTotalRuns() != 0) {
+ int averageTime = task.getAverageMillis();
+ line.append(Integer.toString(averageTime));
+ line.append(" ms");
+ if (task.getTotalRuns() > 1) {
+ line.append(" avg. over ");
+ line.append(Integer.toString(task.getTotalRuns()));
+ line.append(" runs");
+ }
+ } else {
+ line.append("skipped!");
+ }
+ diffText.add(line.toString());
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintRawMain.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintRawMain.java
new file mode 100644
index 000000000..6db750065
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintRawMain.java
@@ -0,0 +1,28 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+
+public class PrintRawMain {
+ public static void main(String[] args) {
+ if (args.length != 1) {
+ System.err.println("Usage: java PrintSummaryMain <log>");
+ return;
+ }
+ File file = new File(args[0]);
+ try {
+ // read the log
+ RootEntry root = LogEntry.readLog(file);
+ // print the log summary
+ root.accept(new PrintSummaryVisitor(System.out));
+ } catch (Exception e) {
+ System.err.println("An error occurred while parsing: " + file);
+ e.printStackTrace();
+ return;
+ }
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintSummaryVisitor.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintSummaryVisitor.java
new file mode 100644
index 000000000..684e44b4b
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/PrintSummaryVisitor.java
@@ -0,0 +1,99 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+public class PrintSummaryVisitor implements ILogEntryVisitor {
+ private PrintStream os;
+ private String indent;
+ private int totalAverageTime;
+
+ /**
+ * Creates a visitor to print a summary of all entries contained in a log.
+ * @param os the output stream
+ */
+ public PrintSummaryVisitor(PrintStream os) {
+ this.os = os;
+ this.indent = "";
+ this.totalAverageTime = 0;
+ }
+
+ protected void visitContainer(LogEntryContainer container) {
+ int oldTotalAverageTime = totalAverageTime;
+ indent += " ";
+ container.acceptChildren(this);
+ int averageTime = totalAverageTime - oldTotalAverageTime;
+ StringBuffer line = new StringBuffer(indent);
+ line.append("* total: ");
+ line.append(Integer.toString(averageTime));
+ line.append(" ms");
+ os.println(line);
+ indent = indent.substring(2);
+ }
+
+ /**
+ * Prints the root entry information.
+ */
+ public void visitRootEntry(RootEntry entry) {
+ os.println("=== TEST LOG SUMMARY ===");
+ os.println("Generated: " + entry.getTimestamp());
+ os.println("SDK Build: " + entry.getSDKBuildId());
+ os.println();
+ entry.acceptChildren(this);
+ }
+
+ /**
+ * Prints the total average time spent by all subgroups and subtasks.
+ */
+ public void visitCaseEntry(CaseEntry entry) {
+ StringBuffer line = new StringBuffer(indent);
+ line.append("%%% ");
+ line.append(entry.getName());
+ line.append(", class=");
+ line.append(entry.getClassName());
+ line.append(':');
+ os.println(line);
+ visitContainer(entry);
+ os.println();
+ }
+
+ /**
+ * Prints the total average time spent by all subtasks.
+ */
+ public void visitGroupEntry(GroupEntry entry) {
+ StringBuffer line = new StringBuffer(indent);
+ line.append("+ ");
+ line.append(entry.getName());
+ line.append(':');
+ os.println(line);
+ visitContainer(entry);
+ }
+
+ /**
+ * Prints the average amount of time spent by a task.
+ */
+ public void visitTaskEntry(TaskEntry task) {
+ StringBuffer line = new StringBuffer(indent);
+ line.append("- ");
+ line.append(task.getName());
+ line.append(": ");
+ if (task.getTotalRuns() != 0) {
+ int averageTime = task.getAverageMillis();
+ totalAverageTime += averageTime;
+ line.append(Integer.toString(averageTime));
+ line.append(" ms");
+ if (task.getTotalRuns() > 1) {
+ line.append(" avg. over ");
+ line.append(Integer.toString(task.getTotalRuns()));
+ line.append(" runs");
+ }
+ } else {
+ line.append("skipped!");
+ }
+ os.println(line);
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/RootEntry.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/RootEntry.java
new file mode 100644
index 000000000..914669176
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/RootEntry.java
@@ -0,0 +1,42 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import org.xml.sax.Attributes;
+
+public class RootEntry extends LogEntryContainer {
+ private String sdkBuildId;
+ private String timestamp;
+
+ public RootEntry(LogEntryContainer parent, Attributes attributes) {
+ this(parent, attributes.getValue("name"),
+ attributes.getValue("sdkbuild"), attributes.getValue("timestamp"));
+ }
+
+ public RootEntry(LogEntryContainer parent, String name, String sdkBuildId, String timestamp) {
+ super(parent, name);
+ this.sdkBuildId = (sdkBuildId != null) ? sdkBuildId : "unknown";
+ this.timestamp = (timestamp != null) ? timestamp : "unknown";
+ }
+
+ public void accept(ILogEntryVisitor visitor) {
+ visitor.visitRootEntry(this);
+ }
+
+ /**
+ * Returns the SDK Build id.
+ */
+ public String getSDKBuildId() {
+ return sdkBuildId;
+ }
+
+ /**
+ * Returns the class name of the test case.
+ */
+ public String getTimestamp() {
+ return timestamp;
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/TaskEntry.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/TaskEntry.java
new file mode 100644
index 000000000..ee6a6ce38
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/logformatter/TaskEntry.java
@@ -0,0 +1,72 @@
+package org.eclipse.team.tests.ccvs.ui.logformatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import org.xml.sax.Attributes;
+
+public class TaskEntry extends LogEntry {
+ private int totalMillis = 0;
+ private int totalRuns = 0;
+
+ public TaskEntry(LogEntryContainer parent, Attributes attributes) {
+ this(parent, attributes.getValue("name"));
+ }
+
+ public TaskEntry(LogEntryContainer parent, String name) {
+ super(parent, name);
+ }
+
+ public void accept(ILogEntryVisitor visitor) {
+ visitor.visitTaskEntry(this);
+ }
+
+ /**
+ * Returns the average number of milliseconds elapsed, or -1 if unknown.
+ */
+ public int getAverageMillis() {
+ if (totalRuns == 0) return -1;
+ return totalMillis / totalRuns;
+ }
+
+ /**
+ * Returns the total number over all runs, or 0 if no runs.
+ */
+ public int getTotalMillis() {
+ return totalMillis;
+ }
+
+ /**
+ * Returns the number of times this task was run.
+ */
+ public int getTotalRuns() {
+ return totalRuns;
+ }
+
+ /**
+ * Adds a set of runs.
+ * @param millisElapsed the number of milliseconds elapsed over all runs
+ * @param runs the number of runs to add
+ */
+ public void addRuns(int millisElapsed, int runs) {
+ totalMillis += millisElapsed;
+ totalRuns += runs;
+ }
+
+ /*
+ * Adds some results corresponding to this task.
+ */
+ void addResult(Attributes attributes) {
+ int elapsed = 0;
+ int runs = 0;
+ boolean aborted = false;
+ String value = attributes.getValue("elapsed");
+ if (value != null) {
+ elapsed = Integer.parseInt(value, 10);
+ runs = 1;
+ }
+ addRuns(elapsed, runs);
+ }
+}

Back to the top