summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authoraclement <aclement>2005-04-26 18:05:55 +0000
committeraclement <aclement>2005-04-26 18:05:55 +0000
commit175d006555a39d12c2a05d8e519fb534f1d87d33 (patch)
tree02b7b40b2c61d46264717748342c9c3de903f080 /tests
parent4b602c7b6c1b269c74bf46be603dc2a9f8210771 (diff)
downloadaspectj-175d006555a39d12c2a05d8e519fb534f1d87d33.tar.gz
aspectj-175d006555a39d12c2a05d8e519fb534f1d87d33.zip
MultiIncrementalSupport: the test harness for managing Ajde interactions and several multi incremental tests.
Diffstat (limited to 'tests')
-rw-r--r--tests/src/org/aspectj/systemtest/AllTests.java2
-rw-r--r--tests/src/org/aspectj/systemtest/incremental/tools/AjdeInteractionTestbed.java709
-rw-r--r--tests/src/org/aspectj/systemtest/incremental/tools/MultiProjectIncrementalTests.java424
3 files changed, 1135 insertions, 0 deletions
diff --git a/tests/src/org/aspectj/systemtest/AllTests.java b/tests/src/org/aspectj/systemtest/AllTests.java
index d8b88d0d7..ca01cd05c 100644
--- a/tests/src/org/aspectj/systemtest/AllTests.java
+++ b/tests/src/org/aspectj/systemtest/AllTests.java
@@ -19,6 +19,7 @@ import org.aspectj.systemtest.base.BaseTests;
import org.aspectj.systemtest.design.DesignTests;
import org.aspectj.systemtest.incremental.IncrementalTests;
import org.aspectj.systemtest.incremental.model.IncrementalModelTests;
+import org.aspectj.systemtest.incremental.tools.MultiProjectIncrementalTests;
import org.aspectj.systemtest.inpath.InPathTests;
import org.aspectj.systemtest.options.OptionsTests;
import org.aspectj.systemtest.pre10x.AjcPre10xTests;
@@ -45,6 +46,7 @@ public class AllTests {
suite.addTest(BaseTests.suite());
suite.addTest(DesignTests.suite());
suite.addTest(IncrementalTests.suite());
+ suite.addTestSuite(MultiProjectIncrementalTests.class);
suite.addTest(IncrementalModelTests.suite());
//suite.addTest(KnownLimitationsTests.class);
suite.addTest(OptionsTests.suite());
diff --git a/tests/src/org/aspectj/systemtest/incremental/tools/AjdeInteractionTestbed.java b/tests/src/org/aspectj/systemtest/incremental/tools/AjdeInteractionTestbed.java
new file mode 100644
index 000000000..a7005d6b4
--- /dev/null
+++ b/tests/src/org/aspectj/systemtest/incremental/tools/AjdeInteractionTestbed.java
@@ -0,0 +1,709 @@
+/* *******************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Andy Clement initial implementation
+ * ******************************************************************/
+package org.aspectj.systemtest.incremental.tools;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.aspectj.ajde.Ajde;
+import org.aspectj.ajde.BuildOptionsAdapter;
+import org.aspectj.ajde.BuildProgressMonitor;
+import org.aspectj.ajde.ErrorHandler;
+import org.aspectj.ajde.ProjectPropertiesAdapter;
+import org.aspectj.ajde.TaskListManager;
+import org.aspectj.ajdt.internal.core.builder.AjState;
+import org.aspectj.ajdt.internal.core.builder.IStateListener;
+import org.aspectj.ajdt.internal.core.builder.IncrementalStateManager;
+import org.aspectj.asm.AsmManager;
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.ISourceLocation;
+import org.aspectj.bridge.IMessage.Kind;
+
+/**
+ * This class uses Ajde in the same way that an IDE (e.g. AJDT) does.
+ *
+ * The build is driven through 'build(projectName,configFile)' but the
+ * build can be configured by the methods beginning 'configure***'.
+ * Information about what happened during a build is accessible
+ * through the get*, was*, print* public methods...
+ *
+ * There are many methods across the multiple listeners that communicate
+ * info with Ajde - not all are implemented. Those that are are
+ * task tagged DOESSOMETHING :)
+ */
+public class AjdeInteractionTestbed extends TestCase {
+
+ public static boolean VERBOSE = false; // do you want the gory details?
+
+ public final static String testdataSrcDir = "../tests/multiIncremental";
+ protected static File sandboxDir;
+
+ private static final String SANDBOX_NAME = "ajcSandbox";
+ private static boolean buildModel;
+
+ // Methods for configuring the build
+ public static void configureBuildStructureModel(boolean b) { buildModel = b;}
+
+ public static void configureNewProjectDependency(String fromProject, String projectItDependsOn) {
+ MyProjectPropertiesAdapter.addDependancy(fromProject,projectItDependsOn);
+ }
+ // End of methods for configuring the build
+
+
+ protected File getWorkingDir() { return sandboxDir; }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ // Create a sandbox in which to work
+ createEmptySandbox();
+ }
+
+ /** Drives a build */
+ public boolean build(String projectName,String configFile) {
+ return AjdeManager.build(projectName,configFile);
+ }
+
+ /** Looks after communicating with the singleton Ajde instance */
+ public static class AjdeManager {
+
+ static {
+ Ajde.init(null,
+ MyTaskListManager.getInstance(),
+ MyBuildProgressMonitor.getInstance(),
+ MyProjectPropertiesAdapter.getInstance(),
+ MyBuildOptionsAdapter.getInstance(),
+ null,null,
+ MyErrorHandler.getInstance());
+
+ MyStateListener sl = MyStateListener.getInstance();
+ AjState.stateListener = sl;
+ }
+
+ /**
+ * Builds a specified project using a specified config file. Subsequent
+ * calls to build the same project should result in incremental builds.
+ */
+ private static boolean build(String projectName,String configFile) {
+ pause(1000); // delay to allow previous runs build stamps to be OK
+ lognoln("Building project '"+projectName+"'");
+
+ // Ajde.getDefault().enableLogging(System.out);
+
+ //Ajde.getDefault().getBuildManager().setReportInfoMessages(true);
+
+ // Configure the necessary providers and listeners for this compile
+ MyBuildProgressMonitor.reset();
+ MyTaskListManager.reset();
+ MyStateListener.reset();
+
+ MyProjectPropertiesAdapter.setActiveProject(projectName);
+ AsmManager.attemptIncrementalModelRepairs=true;
+ IncrementalStateManager.recordIncrementalStates=true;
+
+ Ajde.getDefault().getBuildManager().setBuildModelMode(buildModel);
+
+ // Do the compile
+ Ajde.getDefault().getBuildManager().build(getFile(projectName,configFile));
+
+ // Wait for it to complete
+ while (!MyBuildProgressMonitor.hasFinished()) {
+ lognoln(".");
+ pause(100);
+ }
+ log("");
+
+ // What happened?
+ if (MyTaskListManager.hasErrorMessages()) {
+ System.err.println("Build errors:");
+ for (Iterator iter = MyTaskListManager.getErrorMessages().iterator(); iter.hasNext();) {
+ IMessage element = (IMessage) iter.next();
+ System.err.println(element);
+ }
+ System.err.println("---------");
+ }
+ log("Build finished, time taken = "+MyBuildProgressMonitor.getTimeTaken()+"ms");
+ return true;
+ }
+
+ private static void pause(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException ie) {}
+ }
+
+// public static boolean lastCompileDefaultedToBatch() {
+// return MyTaskListManager.defaultedToBatch();
+// }
+ }
+
+
+ // Methods for querying what happened during a build and accessing information
+ // about the build:
+
+ /**
+ * Helper method for dumping info about which files were compiled and
+ * woven during the last build.
+ */
+ public String printCompiledAndWovenFiles() {
+ StringBuffer sb = new StringBuffer();
+ if (getCompiledFiles().size()==0 && getWovenClasses().size()==0)
+ sb.append("No files were compiled or woven\n");
+ for (Iterator iter = getCompiledFiles().iterator(); iter.hasNext();) {
+ Object element = (Object) iter.next();
+ sb.append("compiled: "+element+"\n");
+ }
+ for (Iterator iter = getWovenClasses().iterator(); iter.hasNext();) {
+ Object element = (Object) iter.next();
+ sb.append("woven: "+element+"\n");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Summary report on what happened in the most recent build
+ */
+ public void printBuildReport() {
+ System.out.println("\n============== BUILD REPORT =================");
+ System.out.println("Build took: "+getTimeTakenForBuild()+"ms");
+ List compiled=getCompiledFiles();
+ System.out.println("Compiled: "+compiled.size()+" files");
+ for (Iterator iter = compiled.iterator(); iter.hasNext();) {
+ System.out.println(" :"+iter.next());
+ }
+ List woven=getWovenClasses();
+ System.out.println("Wove: "+woven.size()+" files");
+ for (Iterator iter = woven.iterator(); iter.hasNext();) {
+ System.out.println(" :"+iter.next());
+ }
+ if (wasFullBuild()) System.out.println("It was a batch (full) build");
+ System.out.println("=============================================");
+ }
+
+
+ public boolean wasFullBuild() {
+ return MyStateListener.wasFullBuild();
+ }
+
+
+ public long getTimeTakenForBuild() {
+ return MyBuildProgressMonitor.getTimeTaken();
+ }
+
+ public List getCompiledFiles() {
+ return MyBuildProgressMonitor.getCompiledFiles();
+ }
+
+ public List getWovenClasses() {
+ return MyBuildProgressMonitor.getWovenClasses();
+ }
+
+ // Infrastructure below here
+
+ private void createEmptySandbox() {
+ String os = System.getProperty("os.name");
+ File tempDir = null;
+ // AMC - I did this rather than use the JDK default as I hate having to go look
+ // in c:\documents and settings\......... for the results of a failed test.
+ if (os.startsWith("Windows")) {
+ tempDir = new File("C:\\temp");
+ if (!tempDir.exists()) {tempDir.mkdir();}
+ } else {
+ tempDir = new File("/tmp");
+ }
+ File sandboxRoot = new File(tempDir,SANDBOX_NAME);
+ if (!sandboxRoot.exists()) {
+ sandboxRoot.mkdir();
+ }
+
+ org.aspectj.util.FileUtil.deleteContents(sandboxRoot);
+
+ try {
+ sandboxDir = File.createTempFile("ajcTest",".tmp",sandboxRoot);
+ sandboxDir.delete();
+ sandboxDir.mkdir();
+ } catch (IOException ioEx) {
+ throw new AssertionFailedError("Unable to create sandbox directory for test");
+ }
+ }
+
+ private static void log(String msg) {
+ if (VERBOSE) System.out.println(msg);
+ }
+
+ private static void lognoln(String msg) {
+ if (VERBOSE) System.out.print(msg);
+ }
+
+ /** Return the *full* path to this file which is taken relative to the project dir*/
+ protected static String getFile(String projectName, String path) {
+ return new File(sandboxDir,projectName+File.separatorChar + path).getAbsolutePath();
+ }
+
+ // Helper classes that communicate with Ajde
+
+ static class MyErrorHandler implements ErrorHandler {
+ static MyErrorHandler _instance = new MyErrorHandler();
+ private MyErrorHandler() {}
+
+ public static ErrorHandler getInstance() {
+ return _instance;
+ }
+
+ public void handleWarning(String message) {
+ log("ErrorHandler.handleWarning("+message+")");
+ }
+
+ public void handleError(String message) {
+ log("ErrorHandler.handleWarning("+message+")");
+
+ }
+
+ public void handleError(String message, Throwable t) {
+ log("ErrorHandler.handleError("+message+","+t+")");
+ if (VERBOSE) t.printStackTrace();
+ }
+
+ }
+
+ // -----------------
+
+ static class MyProjectPropertiesAdapter implements ProjectPropertiesAdapter {
+
+ private final static boolean VERBOSE = false;
+
+ static MyProjectPropertiesAdapter _instance = new MyProjectPropertiesAdapter();
+ private MyProjectPropertiesAdapter() {}
+
+ public static ProjectPropertiesAdapter getInstance() {
+ return _instance;
+ }
+
+ private String projectName = null;
+
+ public static void setActiveProject(String n) {
+ _instance.projectName = n;
+ }
+
+ private static Hashtable dependants = new Hashtable();
+
+ public static void addDependancy(String project, String projectItDependsOn) {
+ List l = (List)dependants.get(project);
+ if (l == null) {
+ List ps = new ArrayList();
+ ps.add(projectItDependsOn);
+ dependants.put(project,ps);
+ } else {
+ l.add(projectItDependsOn);
+ }
+ }
+
+ // interface impl below
+
+ // DOESSOMETHING
+ public String getProjectName() {
+ log("MyProjectProperties.getProjectName() [returning "+projectName+"]");
+ return projectName;
+ }
+
+ // DOESSOMETHING
+ public String getRootProjectDir() {
+ String dir = testdataSrcDir+File.separatorChar+projectName;
+ log("MyProjectProperties.getRootProjectDir() [returning "+dir+"]");
+ return dir;
+ }
+
+ public List getBuildConfigFiles() {
+ log("MyProjectProperties.getBuildConfigFiles()");
+ return null;
+ }
+
+ public String getDefaultBuildConfigFile() {
+ log("MyProjectProperties.getDefaultBuildConfigFile()");
+ return null;
+ }
+
+ public String getLastActiveBuildConfigFile() {
+ log("MyProjectProperties.getLastActiveBuildConfigFile()");
+ return null;
+ }
+
+ public List getProjectSourceFiles() {
+ log("MyProjectProperties.getProjectSourceFiles()");
+ return null;
+ }
+
+ public String getProjectSourcePath() {
+ log("MyProjectProperties.getProjectSourcePath()");
+ return null;
+ }
+
+ // DOESSOMETHING
+ public String getClasspath() {
+ log("MyProjectProperties.getClasspath()");
+ String cp =
+ new File(testdataSrcDir) + File.pathSeparator +
+ System.getProperty("sun.boot.class.path") + File.pathSeparator +
+ "../runtime/bin" + File.pathSeparator +
+ System.getProperty("aspectjrt.path");
+
+ // look at dependant projects
+ List projects = (List)dependants.get(projectName);
+ if (projects!=null) {
+ for (Iterator iter = projects.iterator(); iter.hasNext();) {
+ cp = getFile((String)iter.next(),"bin")+File.pathSeparator+cp;
+ }
+ }
+ //System.err.println("For project "+projectName+" getClasspath() returning "+cp);
+ return cp;
+ }
+
+ public String getOutputPath() {
+ String dir = getFile(projectName,"bin");
+ log("MyProjectProperties.getOutputPath() [returning "+dir+"]");
+ return dir;
+ }
+
+ public String getBootClasspath() {
+ log("MyProjectProperties.getBootClasspath()");
+ return null;
+ }
+
+ public String getClassToExecute() {
+ log("MyProjectProperties.getClassToExecute()");
+ return null;
+ }
+
+ public String getExecutionArgs() {
+ log("MyProjectProperties.getExecutionArgs()");
+ return null;
+ }
+
+ public String getVmArgs() {
+ log("MyProjectProperties.getVmArgs()");
+ return null;
+ }
+
+ public Set getInJars() {
+ log("MyProjectProperties.getInJars()");
+ return null;
+ }
+
+ public Set getInpath() {
+ log("MyProjectProperties.getInPath()");
+ return null;
+ }
+
+ public Map getSourcePathResources() {
+ log("MyProjectProperties.getSourcePathResources()");
+ return null;
+ }
+
+ public String getOutJar() {
+ log("MyProjectProperties.getOutJar()");
+ return null;
+ }
+
+ public Set getSourceRoots() {
+ log("MyProjectProperties.getSourceRoots()");
+ return null;
+ }
+
+ public Set getAspectPath() {
+ log("MyProjectProperties.getAspectPath()");
+ return null;
+ }
+
+ public static void log(String s) {
+ if (VERBOSE) System.out.println(s);
+ }
+
+ }
+
+ // -----------------------
+ static class MyBuildProgressMonitor implements BuildProgressMonitor {
+
+ public static boolean VERBOSE = false;
+ private static MyBuildProgressMonitor _instance = new MyBuildProgressMonitor();
+ private MyBuildProgressMonitor() {}
+
+ private List compiledFiles=new ArrayList();
+ private List wovenClasses=new ArrayList();
+
+
+ public static BuildProgressMonitor getInstance() {
+ return _instance;
+ }
+
+ public static void reset() {
+ _instance.finished = false;
+ _instance.compiledFiles.clear();
+ _instance.wovenClasses.clear();
+ }
+
+ public static boolean hasFinished() {
+ return _instance.finished;
+ }
+
+ public static List getCompiledFiles() { return _instance.compiledFiles;}
+ public static List getWovenClasses() { return _instance.wovenClasses; }
+
+ //---
+
+ private long starttime = 0;
+ private long totaltimetaken = 0;
+ private boolean finished = false;
+
+ public void start(String configFile) {
+ starttime = System.currentTimeMillis();
+ log("BuildProgressMonitor.start("+configFile+")");
+ }
+
+ public void setProgressText(String text) {
+ log("BuildProgressMonitor.setProgressText("+text+")");
+ if (text.startsWith("compiled: ")) {
+ compiledFiles.add(text.substring(10));
+ } else if (text.startsWith("woven class ")) {
+ wovenClasses.add(text.substring(12));
+ } else if (text.startsWith("woven aspect ")) {
+ wovenClasses.add(text.substring(13));
+ }
+ }
+
+ public void setProgressBarVal(int newVal) {
+ log("BuildProgressMonitor.setProgressBarVal("+newVal+")");
+ }
+
+ public void incrementProgressBarVal() {
+ log("BuildProgressMonitor.incrementProgressBarVal()");
+ }
+
+ public void setProgressBarMax(int maxVal) {
+ log("BuildProgressMonitor.setProgressBarMax("+maxVal+")");
+ }
+
+ public int getProgressBarMax() {
+ log("BuildProgressMonitor.getProgressBarMax() [returns 100]");
+ return 100;
+ }
+
+ public void finish() {
+ log("BuildProgressMonitor.finish()");
+ _instance.finished=true;
+ _instance.totaltimetaken=(System.currentTimeMillis()-starttime);
+ }
+
+ public static long getTimeTaken() {
+ return _instance.totaltimetaken;
+ }
+
+ public static void log(String s) {
+ if (VERBOSE) System.out.println(s);
+ }
+
+
+ }
+
+ // ----
+
+ static class MyTaskListManager implements TaskListManager {
+
+ private static final String CANT_BUILD_INCREMENTAL_INDICATION = "Unable to perform incremental build";
+ private static final String DOING_BATCH_BUILD_INDICATION = "Performing batch build for config";
+
+ private final static boolean VERBOSE = false;
+ static MyTaskListManager _instance = new MyTaskListManager();
+ private MyTaskListManager() {}
+
+ private boolean receivedNonIncrementalBuildMessage = false;
+ private boolean receivedBatchBuildMessage = false;
+ private List errorMessages = new ArrayList();
+
+ public static void reset() {
+ _instance.receivedNonIncrementalBuildMessage=false;
+ _instance.receivedBatchBuildMessage=false;
+ _instance.errorMessages.clear();
+ }
+
+// public static boolean defaultedToBatch() {
+// return _instance.receivedNonIncrementalBuildMessage;
+// }
+//
+// public static boolean didBatchBuild() {
+// return _instance.receivedBatchBuildMessage;
+// }
+
+ public static boolean hasErrorMessages() {
+ return !_instance.errorMessages.isEmpty();
+ }
+
+ public static List/*IMessage*/ getErrorMessages() {
+ return _instance.errorMessages;
+ }
+
+ public static TaskListManager getInstance() {
+ return _instance;
+ }
+
+ public void addSourcelineTask(String message, ISourceLocation sourceLocation, Kind kind) {
+ log("TaskListManager.addSourcelineTask("+message+","+sourceLocation+","+kind+")");
+ }
+
+ // DOESSOMETHING
+ public void addSourcelineTask(IMessage message) {
+// if (message.getKind()==IMessage.INFO) {
+// if (message.getMessage().startsWith(CANT_BUILD_INCREMENTAL_INDICATION)) _instance.receivedNonIncrementalBuildMessage=true;
+// if (message.getMessage().startsWith(DOING_BATCH_BUILD_INDICATION)) _instance.receivedBatchBuildMessage=true;
+// }
+ if (message.getKind()==IMessage.ERROR) {
+ errorMessages.add(message);
+ }
+ log("TaskListManager.addSourcelineTask("+message+")");
+ }
+
+ public boolean hasWarning() {
+ log("TaskListManager.hasWarning() [returning false]");
+ return false;
+ }
+
+ public void addProjectTask(String message, Kind kind) {
+ log("TaskListManager.addProjectTask("+message+","+kind+")");
+ }
+
+ public void clearTasks() {
+ log("TaskListManager.clearTasks()");
+ }
+
+ public static void log(String s) {
+ if (VERBOSE) System.out.println(s);
+ }
+ }
+
+ // ----
+
+ static class MyBuildOptionsAdapter implements BuildOptionsAdapter {
+ static MyBuildOptionsAdapter _instance = new MyBuildOptionsAdapter();
+ private MyBuildOptionsAdapter() {}
+
+ public static BuildOptionsAdapter getInstance() {
+ return _instance;
+ }
+
+ public Map getJavaOptionsMap() {
+ return null;
+ }
+
+ public boolean getUseJavacMode() {
+ return false;
+ }
+
+ public String getWorkingOutputPath() {
+ return null;
+ }
+
+ public boolean getPreprocessMode() {
+ return false;
+ }
+
+ public String getCharacterEncoding() {
+ return null;
+ }
+
+ public boolean getSourceOnePointFourMode() {
+ return false;
+ }
+
+ // DOESSOMETHING
+ public boolean getIncrementalMode() {
+ return true;
+ }
+
+ public boolean getLenientSpecMode() {
+ return false;
+ }
+
+ public boolean getStrictSpecMode() {
+ return false;
+ }
+
+ public boolean getPortingMode() {
+ return false;
+ }
+
+ public String getNonStandardOptions() {
+ return null;
+ }
+
+ public String getComplianceLevel() {
+ return null;
+ }
+
+ public String getSourceCompatibilityLevel() {
+ return null;
+ }
+
+ public Set getWarnings() {
+ return null;
+ }
+
+ public Set getDebugLevel() {
+ return null;
+ }
+
+ public boolean getNoImportError() {
+ return false;
+ }
+
+ public boolean getPreserveAllLocals() {
+ return false;
+ }
+ }
+
+ static class MyStateListener implements IStateListener {
+
+ private static MyStateListener _instance = new MyStateListener();
+ private MyStateListener() {reset();}
+
+ public static MyStateListener getInstance() { return _instance;}
+
+ public static boolean informedAboutKindOfBuild;
+ public static boolean fullBuildOccurred;
+
+
+ public static void reset() {
+ informedAboutKindOfBuild=false;
+ fullBuildOccurred=false;
+ }
+
+ public boolean pathChange = false;
+ public void pathChangeDetected() {pathChange = true;}
+ public void aboutToCompareClasspaths(List oldClasspath, List newClasspath) {}
+ public void detectedClassChangeInThisDir(File f) {}
+ public void buildSuccessful(boolean wasFullBuild) {
+ informedAboutKindOfBuild= true;
+ fullBuildOccurred=wasFullBuild;
+ }
+
+ public static boolean wasFullBuild() {
+ if (!informedAboutKindOfBuild) throw new RuntimeException("I never heard about what kind of build it was!!");
+ return fullBuildOccurred;
+ }
+ };
+} \ No newline at end of file
diff --git a/tests/src/org/aspectj/systemtest/incremental/tools/MultiProjectIncrementalTests.java b/tests/src/org/aspectj/systemtest/incremental/tools/MultiProjectIncrementalTests.java
new file mode 100644
index 000000000..0bbe1f7f6
--- /dev/null
+++ b/tests/src/org/aspectj/systemtest/incremental/tools/MultiProjectIncrementalTests.java
@@ -0,0 +1,424 @@
+/* *******************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Andy Clement initial implementation
+* ******************************************************************/
+package org.aspectj.systemtest.incremental.tools;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.aspectj.ajdt.internal.core.builder.AjState;
+import org.aspectj.ajdt.internal.core.builder.IncrementalStateManager;
+import org.aspectj.asm.AsmManager;
+import org.aspectj.asm.IProgramElement;
+import org.aspectj.testing.util.FileUtil;
+
+/**
+ * The superclass knows all about talking through Ajde to the compiler.
+ * The superclass isn't in charge of knowing how to simulate overlays
+ * for incremental builds, that is in here. As is the ability to
+ * generate valid build configs based on a directory structure. To
+ * support this we just need access to a sandbox directory - this
+ * sandbox is managed by the superclass (it only assumes all builds occur
+ * in <sandboxDir>/<projectName>/ )
+ *
+ * The idea is you can initialize multiple projects in the sandbox and
+ * they can all be built independently, hopefully exploiting
+ * incremental compilation. Between builds you can alter the contents
+ * of a project using the alter() method that overlays some set of
+ * new files onto the current set (adding new files/changing existing
+ * ones) - you can then drive a new build and check it behaves as
+ * expected.
+ */
+public class MultiProjectIncrementalTests extends AjdeInteractionTestbed {
+
+ private static boolean VERBOSE = false;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+
+ // Compile a single simple project
+ public void testTheBasics() {
+ initialiseProject("P1");
+ build("P1"); // This first build will be batch
+ build("P1");
+ checkWasntFullBuild();
+ checkCompileWeaveCount(0,0);
+ }
+
+
+ // Make simple changes to a project, adding a class
+ public void testSimpleChanges() {
+ initialiseProject("P1");
+ build("P1"); // This first build will be batch
+ alter("P1","inc1"); // adds a single class
+ build("P1");
+ checkCompileWeaveCount(1,-1);
+ build("P1");
+ checkCompileWeaveCount(0,-1);
+ }
+
+
+ // Make simple changes to a project, adding a class and an aspect
+ public void testAddingAnAspect() {
+ initialiseProject("P1");
+ build("P1");
+ alter("P1","inc1"); // adds a class
+ alter("P1","inc2"); // adds an aspect
+ build("P1");
+ long timeTakenForFullBuildAndWeave = getTimeTakenForBuild();
+ checkWasntFullBuild();
+ checkCompileWeaveCount(2,3);
+ build("P1");
+ long timeTakenForSimpleIncBuild = getTimeTakenForBuild();
+ // I don't think this test will have timing issues as the times should be *RADICALLY* different
+ // On my config, first build time is 2093ms and the second is 30ms
+ assertTrue("Should not take longer for the trivial incremental build! first="+timeTakenForFullBuildAndWeave+
+ "ms second="+timeTakenForSimpleIncBuild+"ms",
+ timeTakenForSimpleIncBuild<timeTakenForFullBuildAndWeave);
+ }
+
+
+ public void testBuildingTwoProjectsInTurns() {
+ configureBuildStructureModel(true);
+ initialiseProject("P1");
+ initialiseProject("P2");
+ build("P1");
+ build("P2");
+ build("P1");
+ checkWasntFullBuild();
+ build("P2");
+ checkWasntFullBuild();
+ }
+
+
+ /**
+ * In order for this next test to run, I had to move the weaver/world pair we keep in the
+ * AjBuildManager instance down into the state object - this makes perfect sense - otherwise
+ * when reusing the state for another project we'd not be switching to the right weaver/world
+ * for that project.
+ */
+ public void testBuildingTwoProjectsMakingSmallChanges() {
+
+ configureBuildStructureModel(true);
+
+ initialiseProject("P1");
+ initialiseProject("P2");
+
+ build("P1");
+ build("P2");
+ build("P1");
+ checkWasntFullBuild();
+
+ build("P2");
+ checkWasntFullBuild();
+
+ alter("P1","inc1"); // adds a class
+ alter("P1","inc2"); // adds an aspect
+ build("P1");
+ checkWasntFullBuild();
+ }
+
+
+ /**
+ * Setup up two simple projects and build them in turn - check the
+ * structure model is right after each build
+ */
+ public void testBuildingTwoProjectsAndVerifyingModel() {
+
+ configureBuildStructureModel(true);
+
+ initialiseProject("P1");
+ initialiseProject("P2");
+
+ build("P1");
+ checkForNode("pkg","C",true);
+
+ build("P2");
+ checkForNode("pkg","C",false);
+
+ build("P1");
+ checkForNode("pkg","C",true);
+
+ build("P2");
+ checkForNode("pkg","C",false);
+ }
+
+
+ // Setup up two simple projects and build them in turn - check the
+ // structure model is right after each build
+ public void testBuildingTwoProjectsAndVerifyingStuff() {
+ configureBuildStructureModel(true);
+
+ initialiseProject("P1");
+ initialiseProject("P2");
+
+ build("P1");
+ checkForNode("pkg","C",true);
+
+ build("P2");
+ checkForNode("pkg","C",false);
+
+ build("P1");
+ checkForNode("pkg","C",true);
+
+ build("P2");
+ checkForNode("pkg","C",false);
+ }
+
+
+ /**
+ * Complex. Here we are testing that a state object records structural changes since
+ * the last full build correctly. We build a simple project from scratch - this will
+ * be a full build and so the structural changes since last build count should be 0.
+ * We then alter a class, adding a new method and check structural changes is 1.
+ */
+ public void testStateManagement1() {
+
+ File binDirectoryForP1 = new File(getFile("P1","bin"));
+
+ initialiseProject("P1");
+ build("P1"); // full build
+ AjState ajs = IncrementalStateManager.findStateManagingOutputLocation(binDirectoryForP1);
+ assertTrue("There should be a state object for project P1",ajs!=null);
+ assertTrue("Should be no structural changes as it was a full build but found: "+
+ ajs.getNumberOfStructuralChangesSinceLastFullBuild(),
+ ajs.getNumberOfStructuralChangesSinceLastFullBuild()==0);
+
+
+ alter("P1","inc3"); // adds a method to the class C.java
+ build("P1");
+ checkWasntFullBuild();
+ ajs = IncrementalStateManager.findStateManagingOutputLocation(new File(getFile("P1","bin")));
+ assertTrue("There should be state for project P1",ajs!=null);
+ checkWasntFullBuild();
+ assertTrue("Should be one structural changes as it was a full build but found: "+
+ ajs.getNumberOfStructuralChangesSinceLastFullBuild(),
+ ajs.getNumberOfStructuralChangesSinceLastFullBuild()==1);
+
+ }
+
+
+ /**
+ * Complex. Here we are testing that a state object records structural changes since
+ * the last full build correctly. We build a simple project from scratch - this will
+ * be a full build and so the structural changes since last build count should be 0.
+ * We then alter a class, changing body of a method, not the structure and
+ * check struc changes is still 0.
+ */
+ public void testStateManagement2() {
+ File binDirectoryForP1 = new File(getFile("P1","bin"));
+
+ initialiseProject("P1");
+ alter("P1","inc3"); // need this change in here so 'inc4' can be applied without making
+ // it a structural change
+ build("P1"); // full build
+ AjState ajs = IncrementalStateManager.findStateManagingOutputLocation(binDirectoryForP1);
+ assertTrue("There should be state for project P1",ajs!=null);
+ assertTrue("Should be no struc changes as its a full build: "+
+ ajs.getNumberOfStructuralChangesSinceLastFullBuild(),
+ ajs.getNumberOfStructuralChangesSinceLastFullBuild()==0);
+
+
+ alter("P1","inc4"); // changes body of main() method but does *not* change the structure of C.java
+ build("P1");
+ checkWasntFullBuild();
+ ajs = IncrementalStateManager.findStateManagingOutputLocation(new File(getFile("P1","bin")));
+ assertTrue("There should be state for project P1",ajs!=null);
+ checkWasntFullBuild();
+ assertTrue("Shouldn't be any structural changes but there were "+
+ ajs.getNumberOfStructuralChangesSinceLastFullBuild(),
+ ajs.getNumberOfStructuralChangesSinceLastFullBuild()==0);
+ }
+
+
+ /**
+ * Now the most complex test. Create a dependancy between two projects. Building
+ * one may affect whether the other does an incremental or full build. The
+ * structural information recorded in the state object should be getting used
+ * to control whether a full build is necessary...
+ */
+ public void testBuildingDependantProjects() {
+ initialiseProject("P1");
+ initialiseProject("P2");
+ configureNewProjectDependency("P2","P1");
+
+ build("P1");
+ build("P2"); // now everything is consistent and compiled
+ alter("P1","inc1"); // adds a second class
+ build("P1");
+ build("P2"); // although a second class was added - P2 can't be using it, so we don't full build here :)
+ checkWasntFullBuild();
+ alter("P1","inc3"); // structurally changes one of the classes
+ build("P1");
+ build("P2"); // build notices the structural change
+ checkWasFullBuild();
+ alter("P1","inc4");
+ build("P1");
+ build("P2"); // build sees a change but works out its not structural
+ checkWasntFullBuild();
+ }
+
+ // other possible tests:
+ // - memory usage (freemem calls?)
+ // - relationship map
+
+ // ---------------------------------------------------------------------------------------------------
+
+ /**
+ * Check we compiled/wove the right number of files, passing '-1' indicates you don't care about
+ * that number.
+ */
+ private void checkCompileWeaveCount(int expCompile,int expWoven) {
+ if (expCompile!=-1 && getCompiledFiles().size()!=expCompile)
+ fail("Expected compilation of "+expCompile+" files but compiled "+getCompiledFiles().size()+
+ "\n"+printCompiledAndWovenFiles());
+ if (expWoven!=-1 && getWovenClasses().size()!=expWoven)
+ fail("Expected weaving of "+expWoven+" files but wove "+getWovenClasses().size()+
+ "\n"+printCompiledAndWovenFiles());
+ }
+
+ private void checkWasntFullBuild() {
+ assertTrue("Shouldn't have been a full (batch) build",!wasFullBuild());
+ }
+
+ private void checkWasFullBuild() {
+ assertTrue("Should have been a full (batch) build",wasFullBuild());
+ }
+
+ private void checkForNode(String packageName,String typeName,boolean shouldBeFound) {
+ IProgramElement ipe = AsmManager.getDefault().getHierarchy().findElementForType(packageName,typeName);
+ if (shouldBeFound) {
+ if (ipe==null) printModel();
+ assertTrue("Should have been able to find '"+packageName+"."+typeName+"' in the asm",ipe!=null);
+ } else {
+ if (ipe!=null) printModel();
+ assertTrue("Should have NOT been able to find '"+packageName+"."+typeName+"' in the asm",ipe==null);
+ }
+ }
+
+
+ private void printModel() {
+ try {
+ AsmManager.dumptree(AsmManager.getDefault().getHierarchy().getRoot(),0);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ public void build(String projectName) {
+ constructUpToDateLstFile(projectName,"build.lst");
+ build(projectName,"build.lst");
+ if (AjdeInteractionTestbed.VERBOSE) printBuildReport();
+ }
+
+
+
+ private void constructUpToDateLstFile(String pname,String configname) {
+ File projectBase = new File(sandboxDir,pname);
+ File toConstruct = new File(projectBase,configname);
+ List filesForCompilation = new ArrayList();
+ collectUpFiles(projectBase,projectBase,filesForCompilation);
+
+ try {
+ FileOutputStream fos = new FileOutputStream(toConstruct);
+ DataOutputStream dos = new DataOutputStream(fos);
+ for (Iterator iter = filesForCompilation.iterator(); iter.hasNext();) {
+ String file = (String) iter.next();
+ dos.writeBytes(file+"\n");
+ }
+ dos.close();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+
+ private void collectUpFiles(File location,File base,List collectionPoint) {
+ String contents[] = location.list();
+ if (contents==null) return;
+ for (int i = 0; i < contents.length; i++) {
+ String string = contents[i];
+ File f = new File(location,string);
+ if (f.isDirectory()) {
+ collectUpFiles(f,base,collectionPoint);
+ } else if (f.isFile() && (f.getName().endsWith(".aj") || f.getName().endsWith(".java"))) {
+ String fileFound;
+ try {
+ fileFound = f.getCanonicalPath();
+ String toRemove = base.getCanonicalPath();
+ if (!fileFound.startsWith(toRemove)) throw new RuntimeException("eh? "+fileFound+" "+toRemove);
+ collectionPoint.add(fileFound.substring(toRemove.length()+1));//+1 captures extra separator
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Fill in the working directory with the project base files,
+ * from the 'base' folder.
+ */
+ private void initialiseProject(String p) {
+ File projectSrc=new File(testdataSrcDir+File.separatorChar+p+File.separatorChar+"base");
+ File destination=new File(getWorkingDir(),p);
+ if (!destination.exists()) {destination.mkdir();}
+ copy(projectSrc,destination);//,false);
+ }
+
+ /*
+ * Applies an overlay onto the project being tested - copying
+ * the contents of the specified overlay directory.
+ */
+ private void alter(String projectName,String overlayDirectory) {
+ File projectSrc =new File(testdataSrcDir+File.separatorChar+projectName+
+ File.separatorChar+overlayDirectory);
+ File destination=new File(getWorkingDir(),projectName);
+ copy(projectSrc,destination);
+ }
+
+ /**
+ * Copy the contents of some directory to another location - the
+ * copy is recursive.
+ */
+ private void copy(File from, File to) {
+ String contents[] = from.list();
+ if (contents==null) return;
+ for (int i = 0; i < contents.length; i++) {
+ String string = contents[i];
+ File f = new File(from,string);
+ File t = new File(to,string);
+
+ if (f.isDirectory() && !f.getName().startsWith("inc")) {
+ t.mkdir();
+ copy(f,t);
+ } else if (f.isFile()) {
+ StringBuffer sb = new StringBuffer();
+ //if (VERBOSE) System.err.println("Copying "+f+" to "+t);
+ FileUtil.copyFile(f,t,sb);
+ if (sb.length()!=0) { System.err.println(sb.toString());}
+ }
+ }
+ }
+
+
+ private static void log(String msg) {
+ if (VERBOSE) System.out.println(msg);
+ }
+}