]> source.dussan.org Git - aspectj.git/commitdiff
MultiIncrementalSupport: the test harness for managing Ajde interactions and several...
authoraclement <aclement>
Tue, 26 Apr 2005 18:05:55 +0000 (18:05 +0000)
committeraclement <aclement>
Tue, 26 Apr 2005 18:05:55 +0000 (18:05 +0000)
tests/src/org/aspectj/systemtest/AllTests.java
tests/src/org/aspectj/systemtest/incremental/tools/AjdeInteractionTestbed.java [new file with mode: 0644]
tests/src/org/aspectj/systemtest/incremental/tools/MultiProjectIncrementalTests.java [new file with mode: 0644]

index d8b88d0d7c9469741ac3fdab5fcc5728e1c6b75c..ca01cd05c876059832b1c7ce91bf332a8bbce437 100644 (file)
@@ -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 (file)
index 0000000..a7005d6
--- /dev/null
@@ -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 (file)
index 0000000..0bbe1f7
--- /dev/null
@@ -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);
+       }
+}