diff options
author | Jean Michel-Lemieux | 2002-02-26 19:58:15 +0000 |
---|---|---|
committer | Jean Michel-Lemieux | 2002-02-26 19:58:15 +0000 |
commit | 0d41a84d0066a4d891dd7c0f8d27cf4c2a06a7a1 (patch) | |
tree | 520873bb0ff30625279b5887f0656e3bff26cc17 /tests | |
parent | 441a9ad7724ad3ba488e20209584106c66405317 (diff) | |
download | eclipse.platform.team-0d41a84d0066a4d891dd7c0f8d27cf4c2a06a7a1.tar.gz eclipse.platform.team-0d41a84d0066a4d891dd7c0f8d27cf4c2a06a7a1.tar.xz eclipse.platform.team-0d41a84d0066a4d891dd7c0f8d27cf4c2a06a7a1.zip |
Initial release of ui benchmark tests
Diffstat (limited to 'tests')
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 "Content". 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> "CPL". For purposes of the CPL, "Program" 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 "Other License", any modifications, enhancements and/or
-other code and/or documentation "Modifications" 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 "Content". 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> "CPL". For purposes of the CPL, "Program" 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 "Other License", any modifications, enhancements and/or +other code and/or documentation "Modifications" 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 Binary files differnew file mode 100644 index 000000000..ec9b17bdd --- /dev/null +++ b/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkBig.zip 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 Binary files differnew file mode 100644 index 000000000..c56846755 --- /dev/null +++ b/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkSmall.zip 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 Binary files differnew file mode 100644 index 000000000..a127ff3d7 --- /dev/null +++ b/tests/org.eclipse.team.tests.cvs.core/resources/BenchmarkTest/benchmarkTiny.zip 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); + } +} |