IncrementalPerformanceTests.java

/********************************************************************
 * Copyright (c) 2008 Contributors. All rights reserved.
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Public License v 2.0
 * which accompanies this distribution and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
 *
 * Contributors:
 *     Andy Clement          initial implementation
 *******************************************************************/
package org.aspectj.systemtest.incremental.tools;

import java.io.IOException;

import org.aspectj.ajde.core.ICompilerConfiguration;

/**
 * Testing the performance of incremental compilation as it would be in AJDT.
 *
 * @author AndyClement
 */
public class IncrementalPerformanceTests extends AbstractMultiProjectIncrementalAjdeInteractionTestbed {

	/**
	 * Build a project of 64 source files and no aspects.<br>
	 * <ul>
	 * <li>First build is a full build.
	 * <li>Second build is an incremental build with no changes at all.
	 * <li>Third build is an incremental build with just a source file touched (not changed).
	 * </ul>
	 *
	 * <p>
	 * 162-dev, 28Aug08 times: Thinkpad T61p: 3203/3140/3234/3156 173/172/172/172 313/297/297/312
	 */
	public void testBuildingProject64Files() {
		String proj = "Proj64";

		// A full build:
		initialiseProject(proj);
		build(proj);
		checkWasFullBuild();
		long fullbuildtime = getTimeTakenForBuild(proj);
		System.out.println("Full build time: " + fullbuildtime + "ms");

		// An incremental build with no source file changes at all. What should happen?
		// We need to determine that nothing has to be done as fast as possible, this is all about
		// determining from the configuration that nothing has changed and returning as fast as possible. Any
		// delays here are unnecessary burden that will hurt every other kind of compilation.
		build(proj);
		checkWasntFullBuild();
		checkCompileWeaveCount(proj, 0, 0);
		long nochangebuild = getTimeTakenForBuild(proj);
		System.out.println("Incr build time for no changes at all: " + nochangebuild + "ms");

		// An incremental build with no source file changes at all *and* we tell the compiler there are
		// no source changes (so it doesn't need to check timestamps). super fast
		addProjectSourceFileChanged(proj, null);
		build(proj);
		checkWasntFullBuild();
		checkCompileWeaveCount(proj, 0, 0);
		long nochangebuildandDoTellCompiler = getTimeTakenForBuild(proj);
		System.out.println("Incr build time for no changes at all and telling the compiler that: " + nochangebuildandDoTellCompiler
				+ "ms");

		// Now we touch a file (C0.java) and call build. What should happen?
		// We need to determine what has changed, we'll do that by walking over the set of input files and
		// checking their last modified stamps. So although we won't rebuild a buildConfig object, we will
		// call lastModifiedTime() a lot to determine which file has changed.
		alter(proj, "inc1");
		build(proj);
		checkWasntFullBuild();
		checkCompileWeaveCount(proj, 1, 1);
		long whitespacechangeDontTellCompiler = getTimeTakenForBuild(proj);
		System.out.println("Incr build time for whitespace change: " + whitespacechangeDontTellCompiler + "ms");

		// Similar to previous test, touch that file, but this time tell the compiler which file has changed. What should happen?
		// As we are telling the compiler what has changed, it will not jump through hoops checking the last mod time of
		// every source file in the project configuration.
		alter(proj, "inc1");
		addProjectSourceFileChanged(proj, getProjectRelativePath(proj, "src/out/C0.java"));
		build(proj);
		checkWasntFullBuild();
		checkCompileWeaveCount(proj, 1, 1);
		long whitespacechangeDoTellCompiler = getTimeTakenForBuild(proj);
		System.out.println("Incr build time for whitespace change (where we tell the compiler what changed): "
				+ whitespacechangeDoTellCompiler + "ms");

		// Lets assert what really ought to be true
		assertTrue(nochangebuild < fullbuildtime);
		assertTrue(whitespacechangeDontTellCompiler < fullbuildtime);
		assertTrue(whitespacechangeDoTellCompiler < fullbuildtime);

		assertTrue(nochangebuild < whitespacechangeDontTellCompiler);
		// assertTrue(nochangebuild < whitespacechangeDoTellCompiler);

		// assertTrue(whitespacechangeDoTellCompiler < whitespacechangeDontTellCompiler);
	}

	/**
	 * Project dependencies are captured by using classpath. The dependee project has the bin folder for the project upon which it
	 * depends on its classpath. This can make it expensive when determining whether to build the dependee project as we may need to
	 * analyse all the classpath entries, we don't know which are project related. However, a new API in ICompilerConfiguration
	 * called getClasspathElementsWithModifiedContents() can be returned by an implementor to tell us which parts of the classpath
	 * to check.
	 */
	public void testBuildingTwoProjects() {

		String projA = "Proj64";
		String projB = "Dependee";

		// A full build:
		initialiseProject(projA);
		initialiseProject(projB);
		configureNewProjectDependency(projB, projA);
		build(projA);
		checkWasFullBuild();
		build(projB);
		checkWasFullBuild();

		alter(projA, "C43changeOne"); // C43 made package private
		build(projA);
		setNextChangeResponse(projB, ICompilerConfiguration.EVERYTHING);
		build(projB);
		long timeTakenWhenFullyAnalysingClasspath = getTimeTakenForBuild(projB);
		checkWasntFullBuild();

		alter(projA, "C43changeOne"); // C43 made package private
		build(projA);
		addClasspathEntryChanged(projB, getProjectRelativePath(projA, "bin").getPath());
		// waitForReturn();
		build(projB);
		long timeTakenWhenFullyToldSpecifically = getTimeTakenForBuild(projB);
		// waitFor10();
		checkWasntFullBuild();

		System.out.println("Without: " + timeTakenWhenFullyAnalysingClasspath + "ms   With: " + timeTakenWhenFullyToldSpecifically
				+ "ms");

	}

	// --- helper code ---

	@SuppressWarnings("unused")
	private void waitFor10() {
		try {
			Thread.sleep(10000);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unused")
	private void waitForReturn() {
		try {
			System.in.read();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	protected void setUp() throws Exception {
		super.setUp();
		testdataSrcDir = "../tests/incrementalPerformance";
	}

	@Override
	protected void tearDown() throws Exception {
		super.tearDown();
		testdataSrcDir = "../tests/multiIncremental";
	}

}