Package tests.integrationtests

Source Code of tests.integrationtests.ITCompilationUnitOutput

package tests.integrationtests;

import joust.utils.logging.LogUtils;
import lombok.experimental.ExtensionMethod;
import lombok.extern.java.Log;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import testutils.BaseIntegrationTestCase;

import javax.tools.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;

import static org.junit.Assert.*;

/**
* Parameterised JUnit test to ensure the output of the test programs in the resources directory
* are unchanged after optimisation.
* The parameter function gets the list of all input files from the resource directory, causing
* JUnit to produce an instance of the test for each such file, tidily giving us one test per
* compilation unit without the need for lots of boring boilerplate.
*/
@Log
@ExtensionMethod({Logger.class, LogUtils.LogExtensions.class})
@RunWith(Parameterized.class)
public class ITCompilationUnitOutput {
    public static final String TEST_INPUTS_DIR = "/testinputs/";
    public static final String TEST_SHARED_CLASSES_DIR = "/testutils/";
    public static final String OPT_DIR = "/compilationResults/opt/";
    public static final String UN_OPT_DIR = "/compilationResults/noOpt/";

    public static final String TEST_INPUT_PACKAGE = "testinputs";

    private static final URL sOptOutDir = ITCompilationUnitOutput.class.getResource(OPT_DIR);
    private static final URL sUnOptOutDir = ITCompilationUnitOutput.class.getResource(UN_OPT_DIR);

    private static final String JAVAP = System.getProperty("java.home")+"/../bin/javap";

    // The directory in which classes shared between all unit tests should reside.
    private static File[] mSharedClasses;

    @Parameters(name = "{index}: {0}")
    public static Collection<Object[]> data() {
        // Create a File array of all *.java files in the shared classes directory.
        URL sharedClasses = ITCompilationUnitOutput.class.getResource(TEST_SHARED_CLASSES_DIR);
        File[] files = new File(sharedClasses.getFile()).listFiles();
        ArrayList<File> desiredFiles = new ArrayList<File>();
        for (File f : files) {
            if (f.getName().endsWith(".java")) {
                desiredFiles.add(f);
            }
        }
        mSharedClasses = desiredFiles.toArray(new File[desiredFiles.size()]);

        URL testInputsUrl = ITCompilationUnitOutput.class.getResource(TEST_INPUTS_DIR);

        File optOutDir = new File(sOptOutDir.getFile());
        File noOptOutdir = new File(sUnOptOutDir.getFile());

        File testInputs = new File(testInputsUrl.getFile());
        File[] testCases = testInputs.listFiles();
        System.out.println(Arrays.toString(testCases));

        LinkedList<Object[]> ret = new LinkedList<Object[]>();
        for (int i = 0; i < testCases.length; i++) {
            ret.add(new Object[] {testCases[i], optOutDir, noOptOutdir});
        }

        return ret;
    }

    // A pair of classloaders - one for optimised classes, one for unoptimised classes.
    public static URLClassLoader sOptClassLoader =  new URLClassLoader(new URL[] {sOptOutDir});
    public static URLClassLoader sUnOptClassLoader =  new URLClassLoader(new URL[] {sUnOptOutDir});

    private File mTargetSource;
    private File mOptOutDir;
    private File mNoOptOutDir;

    private String mTestClassName;
    private String mFullyQualifiedTestClassName;

    public ITCompilationUnitOutput(File elementName, File optOutDir, File noOptOutDir) {
        mTargetSource = elementName;
        mOptOutDir = optOutDir;
        mNoOptOutDir = noOptOutDir;

        String n = elementName.getName();
        mTestClassName = n.substring(0, n.lastIndexOf('.'));
        mFullyQualifiedTestClassName = TEST_INPUT_PACKAGE + '.' + mTestClassName;
    }

    /**
     * Compile the given .java with and without optimisation, run both, and ensure the outputs
     * are the same.
     */
    @Test
    public void runTest() {
        log.info("-------------------------------------------------------------------------");
        log.info("- Running compilation output comparism test for {}. -", mTargetSource.getName());
        log.info("-------------------------------------------------------------------------");

        // Create the compiled programs..
        assertTrue(compileTarget(false));
        assertTrue(compileTarget(true));

        // Execute them both and collect their output...
        Class<? extends BaseIntegrationTestCase> optClass = null;
        Class<? extends BaseIntegrationTestCase> noOptClass = null;
        try {
            optClass = (Class<? extends BaseIntegrationTestCase>) sOptClassLoader.loadClass(mFullyQualifiedTestClassName);
            noOptClass = (Class<? extends BaseIntegrationTestCase>) sUnOptClassLoader.loadClass(mFullyQualifiedTestClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // Verify we loaded the classes successfully...
        assertNotNull(optClass);
        assertNotNull(noOptClass);

        log.debug("Running no-opt...");
        String noOptOutput = executeCompiledTest(noOptClass);
        log.debug("Returns:\n{}", noOptOutput);

        log.debug("Running opt...");
        String optOutput = executeCompiledTest(optClass);
        log.debug("Returns:\n{}", optOutput);

        // Not using an assertion directly so we can print extra debug information.
        if (!optOutput.equals(noOptOutput)) {
            // Print a bytecode diff for debugging...
            printBytecodeOfTest();

            // ...And fail the test.
            assertTrue(false);
        }

        log.info("---------------------------------------------");
        log.info("- End of compilation output comparism test. -");
        log.info("---------------------------------------------");
    }

    /**
     * Print the bytecode of the failing programs, to aid in debugging.
     */
    private void printBytecodeOfTest() {
        try {
            log.debug("-------------- BYTECODE OF OPTIMISED TESTCASE --------------");
            printBytecodeForDir(sOptOutDir);
            log.debug("-------------- BYTECODE OF UNOPTIMISED TESTCASE --------------");
            printBytecodeForDir(sUnOptOutDir);
        } catch (IOException e) {
            log.debug("IOException dumping bytecode!");
            e.printStackTrace();
        }
    }

    /**
     * Helper method to print the bytecode of the test class for this test at the directory given.
     * Typically called once for optimised builds, and again for the unoptimised build.
     *
     * @param dirName Top-level output directory to look in for the target class.
     * @throws IOException If an IOException is thrown by ProcessBuilder.start trying to call javap.
     */
    private void printBytecodeForDir(URL dirName) throws IOException {
        Runtime rt = Runtime.getRuntime();
        String[] cmd = new String[]{JAVAP, "-v", "-classpath", ".", mFullyQualifiedTestClassName};

        Process proc = rt.exec(cmd, null, new File(dirName.getFile()));

        BufferedReader stdInput = new BufferedReader(new
                InputStreamReader(proc.getInputStream()));

        String s;

        while ((s = stdInput.readLine()) != null) {
            System.out.println(s);
        }
    }

    /**
     * Given a class that extends BaseIntegrationTestCase, run the test and collect the output.
     *
     * @param targetClass
     * @return The output of the test referred to by this class.
     */
    private String executeCompiledTest(Class<? extends BaseIntegrationTestCase> targetClass) {
        BaseIntegrationTestCase t = null;

        try {
            t = targetClass.getConstructor().newInstance();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        assertNotNull(t);

        return t.runTest();
    }

    /**
     * Helper method to compile the target for this test with or without optimisation.
     *
     * @param optimise If true, apply the optimiser. Otherwise, do not apply the optimiser.
     */
    private boolean compileTarget(boolean optimise) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);

        // Create a list of target files including both the shared files and the target for this test.
        File[] targetFiles = new File[mSharedClasses.length + 1];
        targetFiles[0] = mTargetSource;
        System.arraycopy(mSharedClasses, 0, targetFiles, 1, mSharedClasses.length);
        for (File f: targetFiles) {
            log.warn("File: {}", f);
        }

        Iterable<? extends JavaFileObject> compilationTarget = fileManager.getJavaFileObjects(targetFiles);

        List<String> optionList = new ArrayList<String>();
        if (!optimise) {
            optionList.add("-proc:none");
        }
        optionList.add("-source");
        optionList.add("1.7");
        optionList.add("-target");
        optionList.add("1.7");
        optionList.add("-AJOUSTLogLevel=FINEST");

        // The compilation targets...
        optionList.add("-d");
        if (optimise) {
            optionList.add(mOptOutDir.getPath());
        } else {
            optionList.add(mNoOptOutDir.getPath());
        }

        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, optionList, null, compilationTarget);

        log.trace("Calling task.");
        boolean ret = task.call();
        log.trace("Task complete with {} diagnostics to report.", diagnostics.getDiagnostics().size());
        for (Diagnostic<? extends JavaFileObject> d : diagnostics.getDiagnostics()) {
            switch (d.getKind()) {
                case ERROR:
                    ret = false;
                    log.error(d);
                    break;
                case WARNING:
                case MANDATORY_WARNING:
                    log.warn(d);
                    break;
                case NOTE:
                case OTHER:
                    log.info(d);
                    break;
            }
        }

        log.trace("Done.");
        // Perform the compilation task.
        return ret;
    }
}
TOP

Related Classes of tests.integrationtests.ITCompilationUnitOutput

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.