diff options
author | aclement <aclement> | 2005-04-26 18:05:55 +0000 |
---|---|---|
committer | aclement <aclement> | 2005-04-26 18:05:55 +0000 |
commit | 175d006555a39d12c2a05d8e519fb534f1d87d33 (patch) | |
tree | 02b7b40b2c61d46264717748342c9c3de903f080 /tests | |
parent | 4b602c7b6c1b269c74bf46be603dc2a9f8210771 (diff) | |
download | aspectj-175d006555a39d12c2a05d8e519fb534f1d87d33.tar.gz aspectj-175d006555a39d12c2a05d8e519fb534f1d87d33.zip |
MultiIncrementalSupport: the test harness for managing Ajde interactions and several multi incremental tests.
Diffstat (limited to 'tests')
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); + } +} |