BuildModuleTest.java

/* *******************************************************************
 * Copyright (c) 1999-2001 Xerox Corporation,
 *               2002 Palo Alto Research Center, Incorporated (PARC),
 *               2005-2006 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:
 *     Xerox/PARC     initial implementation
 * ******************************************************************/

package org.aspectj.internal.build;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import junit.framework.TestCase;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Commandline.Argument;
import org.aspectj.internal.tools.ant.taskdefs.BuildModule;
import org.aspectj.internal.tools.ant.taskdefs.Checklics;
import org.aspectj.internal.tools.build.Util;

/**
 * Test our integrated taskdef build.
 * This responds to two environment variables:
 * (1) run.build.tests must be defined before
 *   tests that build the tree (and hence take minutes)
 *   will run;
 * (2) build.config takes the same form as it does for the
 *   builder task, e.g., "useEclipseCompiles" will avoid
 *   recompiling with Javac and adopt classes in the
 *   {module}/bin directories.
 */
public class BuildModuleTest extends TestCase {

    private static boolean printInfoMessages = false;
    private static boolean printedMessage;
    private static final boolean REMOVE_JARS_AFTER_DEBUGGING = true;
    // to just build one module verbosely
    private static final String[] DEBUGS
        = {};

    // skip those requiring ajdoc, which requires tools.jar
    // also skip those requiring java5 unless manually set up
    // also skip big ones to avoid slowing the build too much
    private static final String[] SKIPS
        //= {};
       = {"aspectjtools", "ajdoc", "aspectj5rt", "run-all-junit-tests",
        "testing", "testing-drivers", "org.aspectj.ajdt.core", "weaver"};

    private static final String SKIP_MESSAGE =
        "BuildModuleTest: Define \"run.build.tests\" as a system "
        + "property to run tests to build ";
    private static final String BUILD_CONFIG;
    static {
        String config = null;
        try {
            config = System.getProperty("build.config");
        } catch (Throwable t) {
            // ignore
        }
        BUILD_CONFIG = config;
        if (printInfoMessages) {
            System.out.println("BuildModuleTest build.config: " + config);
        }
    }

    List<File> tempFiles = new ArrayList<>();
    private File jarDir;
    private boolean deleteJars;
    boolean building;  // must be enabled for tests to run

	public BuildModuleTest(String name) {
		super(name);
        building = Boolean.getBoolean("run.build.tests");
	}
    protected void setUp() {
        // change to view whether prior output is used
        deleteJars = true; // todo
    }

    protected void tearDown() throws Exception {
		super.tearDown();
        if (debugging() && !REMOVE_JARS_AFTER_DEBUGGING) {
            if (0 < tempFiles.size()) {
                System.err.println("debugging files left: " + tempFiles);
            }
            return;
        }
        deleteTempFiles();
	}

    protected void deleteTempFiles() {
		for (File file : tempFiles) {
			if (!Util.delete(file)) {
				File[] list = file.listFiles();
				if (!Util.isEmpty(list)) {
					StringBuilder sb = new StringBuilder();
					sb.append("warning: BuildModuleTest unable to delete ");
					sb.append(file.toString());
					sb.append("\n"); // XXX platform
					for (File value : list) {
						sb.append("  ");
						sb.append(value.toString());
						sb.append("\n"); // XXX platform
					}
					System.err.println(sb.toString());
				}
			}
		}
    }

    public void testAllJunitTests() {
      checkBuild("run-all-junit-tests");
    }

	public void testBuild() {
        checkBuild("build",
            Checklics.class.getName(),
            new String[0], // help message
            true); // ant needed
    }

    public void testUtil() {
        checkBuild("util");
    }

    public void testAsm() {
        checkBuild("asm");
    }

    public void testRuntime() {
        checkBuild("runtime");
    }

    public void testAspectj5rt() {
        checkBuild("aspectj5rt");
    }

//    public void testLocalOutOfDate() {
//        Messager handler = new Messager();
//        File jarDir = new File("c:/home/ws/head/aj-build/jars");
//        File baseDir = new File("c:/home/ws/head");
//        Modules mods = new Modules(baseDir, jarDir, handler);
//        Module module = mods.getModule("ajbrowser");
//        Result r = module.getResult(Result.kind(true, true));
//        r.outOfDate();
//    }
//    public void testAspectj5rtRequired() {
//        File baseDir = new File("..");
//        Modules modules = new Modules(baseDir, getJarDir(), new Messager());
//        Module module = modules.getModule("aspectj5rt");
//        Result result = module.getResult(Result.kind(true, true));
//        Result[] results = result.getRequired();
//        System.out.println(result.toLongString());
//        System.out.println("results: " + Arrays.asList(results));
//        deleteTempFiles();
//    }

    public void xtestNoDuplicates() {
        File weaverAllJar = null;
        try {
            weaverAllJar = doTask("weaver",true, true, true);
        } catch (Throwable t) {
            System.err.println(getClass() + ".testNoDuplicates() incomplete");
            t.printStackTrace(System.err);
            return;
        }
        String dupError = duplicateEntryError(weaverAllJar);
        weaverAllJar.delete();
        if (null != dupError) {
            fail(dupError);
        }
    }
    public void testTestingUtils() {
        checkBuild("testing-util");
    }

    public void testAjdt() {
        checkBuild("org.aspectj.ajdt.core",
           "org.aspectj.tools.ajc.Main",
            new String[] { "-noExit", "-version" });
    }//
    public void testTesting() {
        checkBuild("testing",
            "org.aspectj.testing.util.LangUtilTest",
            new String[] {"ignored"});
    }
    public void testTestingDrivers() {
        checkBuild("testing-drivers",
            "org.aspectj.testing.drivers.Harness",
            new String[] {"-help"});
    }
    public void testWeaver() {
        checkBuild("weaver");
    }

    // ajdoc relies on tools.jar
    public void testAspectjtools() {
        if (!shouldBuild("aspectjtools")) {
            return;
        }
        File baseDir = new File("..");
        File tempBuildDir = new File(baseDir, "aj-build");
        File distDir = new File(tempBuildDir, "dist");
        File jarDir = new File(tempBuildDir, "jars");
        assertTrue(distDir.canWrite() || distDir.mkdirs());
        File productDir = new File(baseDir.getPath() + "/build/products/tools");
        assertTrue(""+productDir, productDir.canRead());
        checkBuildProduct(productDir, baseDir, distDir, jarDir);
    }

    void checkBuildProduct(File productDir, File baseDir, File distDir, File jarDir) {
        if (!shouldBuild(productDir.getPath())) {
            return;
        }
        assertTrue(null != productDir);
        assertTrue(productDir.canRead());

        checkJavac();

        BuildModule task = new BuildModule();
        Project project = new Project();
        task.setProject(project);
        assertTrue(jarDir.canWrite() || jarDir.mkdirs());
        tempFiles.add(jarDir);
        task.setJardir(new Path(project, jarDir.getAbsolutePath()));
        task.setProductdir(new Path(project, productDir.getAbsolutePath()));
        task.setBasedir(new Path(project, baseDir.getAbsolutePath()));
        task.setDistdir(new Path(project, distDir.getAbsolutePath()));
        task.setFailonerror(true);
        if (null != BUILD_CONFIG) {
            task.setBuildConfig(BUILD_CONFIG);
        }
        //task.setVerbose(true);
        task.setCreateinstaller(true);
        task.execute();
        // now run installer and do product tests?
    }

    File getAntJar() {
        return new File("../lib/ant/lib/ant.jar");
    }
    File getJUnitJar() {
        return new File("../lib/junit/junit.jar");
    }

    File getJarDir() {
        if (null == jarDir) {
            File baseDir = new File("../aj-build/");
            if (!baseDir.canWrite()) {
                baseDir = new File(".");
            }
            jarDir = new File(baseDir, "BuildModuleTest-jars");
            tempFiles.add(jarDir);
        }
        if (!jarDir.exists()) {
            assertTrue(jarDir.mkdirs());
        }
        return jarDir;
    }

    BuildModule getTask(String module) {
        BuildModule task = new BuildModule();
        Project project = new Project();
        task.setProject(project);
        File jarDir = getJarDir();
        assertTrue(jarDir.canWrite() || jarDir.mkdirs());
        tempFiles.add(jarDir);
        File moduleDir = new File(Util.path("..", module));
        assertTrue(moduleDir.canRead());
        task.setModuledir(new Path(project, moduleDir.getAbsolutePath()));
        task.setJardir(new Path(project, jarDir.getAbsolutePath()));
        task.setFailonerror(true);
        if (null != BUILD_CONFIG) {
            task.setBuildConfig(BUILD_CONFIG);
        }
        return task;
    }

    void checkBuild(String module) {
        checkBuild(module, null, null, false);
    }

    void checkBuild(String module,
        String classname,
        String[] args) {
        checkBuild(module, classname, args, true);
    }

    boolean shouldBuild(String target) {
        if (null == target) {
            return false;
        }
        if (!building && !printedMessage) {
            System.err.println(SKIP_MESSAGE + target + " (this is the only warning)");
            printedMessage = true;
        }
        if (debugging()) {
			for (String debug : DEBUGS) {
				if (target.equals(debug)) {
					return true;
				}
			}
            return false;
        } else {
			for (String skip : SKIPS) {
				if (skip.equals(target)) {
					if (printInfoMessages) {
						System.err.println(target + " skipped build test [" + getClass().getName() + ".shouldBuild(..)]");
					}
					return false;
				}
			}
        }
        return building;
    }
    private static boolean debugging() {
        return ((null != DEBUGS) && (0 < DEBUGS.length));
    }
    private static String duplicateEntryError(File weaverAllJar) {
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(weaverAllJar);
            Enumeration e = zipFile.entries();
            List<String> entryNames = new ArrayList<>();
            while (e.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) e.nextElement();
                String name = entry.getName();
                if (entryNames.contains(name)) {
                    return "duplicate entry: " + name;
                }
                entryNames.add(name);
            }
        } catch (ZipException e) {
            return "ZipException " + e;
        } catch (IOException e) {
            return "IOException " + e;
        } finally {
            if (null != zipFile) {
                try {
                    zipFile.close();
                } catch (IOException e) {
                    return "IOException closing " + zipFile + ": " + e;
                }
            }
        }
        return null;
    }

    private static String name(String module, boolean trimTesting, boolean assemble) {
        return module + (trimTesting?"":"-test") + (assemble?"-all":"");
    }
    private void deleteJar(File jar) {
        if (!deleteJars) {
            return ;
        }
        if (jar.exists()) {
            jar.delete();
        }
        if (jar.exists()) {
            try {
                Thread.sleep(5000);
            } catch (Throwable t) {
            }
        }
        if (jar.exists()) {
            assertTrue("cannot delete " + jar, jar.delete());
        }
    }
    void checkBuild(String module,
        String classname,
        String[] args,
        boolean addAnt) {
        if (!shouldBuild(module)) {
            return;
        }
        assertTrue(null != module);
        checkJavac();
        doTask(module, true, false);
        doTask(module, true, true);
        doTask(module, false, false);
        File jar = doTask(module, false, true, true);

        // verify if possible
        if (null != classname) {
            Java java = new Java();
            Project project = new Project();
            java.setProject(project);
            java.setFailonerror(true);
            Path cp = new Path(project);
            assertTrue(jar.canRead());
            cp.append(new Path(project, jar.getAbsolutePath()));
            if (addAnt) {
                cp.append(new Path(project, getAntJar().getAbsolutePath()));
                cp.append(new Path(project, getJUnitJar().getAbsolutePath()));
            }
            java.setClasspath(cp);
            java.setClassname(classname);
            if (null != args) {
				for (String s : args) {
					Argument arg = java.createArg();
					arg.setValue(s);
				}
            }
            try {
                java.execute();
            } catch (BuildException e) {
                e.printStackTrace(System.err);
                assertTrue("BuildException running " + classname, false);
            }
        }
        deleteJar(jar);
    }
    void doTask(String module, boolean trimTesting, boolean assembleAll) {
        doTask(module, trimTesting, assembleAll, false);
    }

    File doTask(String module, boolean trimTesting, boolean assembleAll, boolean keepJars) {
        BuildModule task = getTask(module);
        String name = name(module, trimTesting, assembleAll);
        File jar = new File(getJarDir(), name+ ".jar");
        task.setAssembleall(assembleAll);
        task.setTrimtesting(trimTesting);
        task.execute();
        if (!jar.canRead()) {
            File[] files = getJarDir().listFiles();
            fail("cannot read " + jar + " in " + Arrays.asList(files));
        }
        if (!keepJars && deleteJars) {
            deleteTempFiles();
        }
        return jar;
    }

    void checkJavac() {
        boolean result = false;
        try {
            result = (null != Class.forName("sun.tools.javac.Main"));
        } catch (Throwable t) {
            // ignore
        }
        if (! result) {
            assertTrue("add tools.jar to the classpath for Ant's use of javac", false);
        }
    }

}