uk.org.cinquin.mutinack.tests.FunctionalTestRerun.java Source code

Java tutorial

Introduction

Here is the source code for uk.org.cinquin.mutinack.tests.FunctionalTestRerun.java

Source

/**
 * Mutinack mutation detection program.
 * Copyright (C) 2014-2016 Olivier Cinquin
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package uk.org.cinquin.mutinack.tests;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringReader;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.nustaq.serialization.FSTConfiguration;

import contrib.uk.org.lidalia.slf4jext.Logger;
import contrib.uk.org.lidalia.slf4jext.LoggerFactory;
import uk.org.cinquin.mutinack.Mutinack;
import uk.org.cinquin.mutinack.MutinackGroup;
import uk.org.cinquin.mutinack.misc_util.Assert;
import uk.org.cinquin.mutinack.misc_util.FileCache;
import uk.org.cinquin.mutinack.misc_util.StaticStuffToAvoidMutating;
import uk.org.cinquin.mutinack.misc_util.exceptions.FunctionalTestFailed;
import uk.org.cinquin.mutinack.output.RunResult;

/**
 * The "recordedFunctionalTestRuns.bin" file must have been created prior to running
 * these tests. The file is created by the run_functional_tests script. The absolute
 * paths recorded in recordedFunctionalTestRuns must be valid at the time tests from
 * this class are run.
 * @author olivier
 *
 */
@RunWith(Parameterized.class)
public class FunctionalTestRerun {

    private static final Logger logger = LoggerFactory.getLogger(FileCache.class);

    public static Map<String, RunResult> testRuns;
    private static final String pathToRecordedTests = "recordedFunctionalTestRuns.bin";
    private static final FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();

    static {
        try (DataInputStream dataIS = new DataInputStream(new FileInputStream(pathToRecordedTests))) {
            byte[] bytes = new byte[(int) new File(pathToRecordedTests).length()];
            dataIS.readFully(bytes);
            @SuppressWarnings("unchecked")
            Map<String, RunResult> testRuns0 = (Map<String, RunResult>) conf.asObject(bytes);
            testRuns = testRuns0;
        } catch (IOException e) {
            logger.error("Problem reading data from " + new File(pathToRecordedTests).getAbsolutePath(), e);
        }
    }

    private static final List<String> listDuplexKeepTypes = //Arrays.asList("DuplexHashMapKeeper",
            //      /*"DuplexITKeeper",*/ "DuplexArrayListKeeper", null);
            Collections.singletonList(null);

    private static final List<String> dontForceDuplexKeepTypes = Arrays.asList(new String[] { null });

    @org.junit.runners.Parameterized.Parameters(name = "{0}-{1}-{2}")
    public static Iterable<Object[]> data() {
        List<String> param2List = false ? dontForceDuplexKeepTypes : listDuplexKeepTypes;
        List<Object[]> result = testRuns.keySet().stream().filter(s -> !s.contains("illegal")).//TODO Deal separately with runs that should fail
        //For now, just skip them
                flatMap(s -> param2List.stream().map(duplex -> new Object[] { s, duplex, true }))
                .collect(Collectors.toList());
        if (!result.isEmpty()) {
            result.get(0)[2] = false;
        }
        return result;
    }

    @Parameter(0)
    public String testName;

    @Parameter(1)
    public String duplexKeeperType;

    @Parameter(2)
    public boolean suppressAlignmentOutput;

    RunResult run;

    private static final int nColumnsToCheck = 14;

    @Test
    public void test() throws InterruptedException, IOException {
        if (run == null) {
            run = testRuns.get(testName);
            //TODO Switch to throwing TestRunFailure
            Assert.isNonNull(run, "Could not find parameters for test %s within %s", testName,
                    testRuns.keySet().stream().collect(Collectors.joining("\t")));
            Assert.isNonNull(run.parameters.referenceOutput, "No reference output specified for test GenericTest");
            run.parameters.terminateImmediatelyUponError = false;
            if (suppressAlignmentOutput) {
                run.parameters.outputAlignmentFile = Collections.emptyList();
            }
        }

        String referenceOutputDirectory = new File(run.parameters.referenceOutput).getParent();

        final boolean useCustomScript;
        if (new File(referenceOutputDirectory + "/custom_check_script").exists()) {
            useCustomScript = true;
        } else {
            useCustomScript = false;
            Assert.isTrue(new File(run.parameters.referenceOutput).exists(), "File %s does not exist",
                    new File(run.parameters.referenceOutput).getAbsolutePath());
        }

        Path referenceOutputPath = Paths.get(run.parameters.referenceOutput);
        String auxOutputFileBaseName = Files.createTempDirectory("functional_test_").toString();
        run.parameters.auxOutputFileBaseName = auxOutputFileBaseName + '/';

        run.parameters.jsonFilePathExtraPrefix = ManagementFactory.getRuntimeMXBean().getName() + this.hashCode();

        try {
            try (ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                    ByteArrayOutputStream errStream = new ByteArrayOutputStream()) {

                StaticStuffToAvoidMutating.instantiateThreadPools(64);

                //Only one test should run at a time, so it's OK to use a static variable
                try (PrintStream outPS = new PrintStream(outStream);
                        PrintStream errPS = new PrintStream(errStream)) {
                    MutinackGroup.forceKeeperType = duplexKeeperType;
                    Mutinack.realMain1(run.parameters, outPS, errPS);
                } finally {
                    MutinackGroup.forceKeeperType = null;
                }

                if (useCustomScript) {
                    ProcessBuilder pb = new ProcessBuilder("./custom_check_script",
                            run.parameters.jsonFilePathExtraPrefix).directory(new File(referenceOutputDirectory))
                                    .redirectErrorStream(true);
                    Process process = pb.start();
                    try (DataInputStream processOutput = new DataInputStream(process.getInputStream())) {
                        process.waitFor();
                        if (process.exitValue() != 0) {
                            byte[] processBytes = new byte[processOutput.available()];
                            processOutput.readFully(processBytes);
                            throw new FunctionalTestFailed(testName + '\n' + new String(processBytes));
                        }
                    }
                    return;
                } else {
                    final String out = outStream.toString();
                    try (Stream<String> referenceOutputLines = Files.lines(referenceOutputPath)) {
                        checkOutput(out, referenceOutputLines);
                    }
                }
            }

            File baseOutputDir = referenceOutputPath.getParent().toFile();
            for (String expectedOutputBaseName : baseOutputDir.list((file, name) -> name.startsWith("expected_")
                    && !name.equals("expected_run.out") && !name.equals("expected_output.txt"))) {
                File actualOutputFile = new File(
                        auxOutputFileBaseName + '/' + expectedOutputBaseName.replace("expected_", ""));
                if (!actualOutputFile.exists()) {
                    throw new FunctionalTestFailed(
                            "Output file " + actualOutputFile.getAbsolutePath() + " was not created");
                }
                //Files.lines needs to be explicitly closed, or else the
                //underlying file does not get closed (apparently even
                //after the stream gets read to the end)
                try (Stream<String> linesToLookFor = Files
                        .lines(Paths.get(baseOutputDir.getAbsolutePath() + '/' + expectedOutputBaseName))) {
                    checkOutput(FileUtils.readFileToString(actualOutputFile), linesToLookFor);
                }
            }
        } finally {
            FileUtils.deleteDirectory(new File(auxOutputFileBaseName));
            String prefix = run.parameters.jsonFilePathExtraPrefix;
            if (!prefix.isEmpty()) {
                for (File f : referenceOutputPath.getParent().toFile()
                        .listFiles((dir, name) -> name.startsWith(run.parameters.jsonFilePathExtraPrefix))) {
                    f.delete();
                }
            }
        }
    }

    private static void checkOutput(String output, Stream<String> linesToLookFor) {
        linesToLookFor.forEach((final String line) -> {
            String[] split = line.split("\t");
            StringBuilder headSB = new StringBuilder();
            for (int i = 0; i < Math.min(nColumnsToCheck, split.length); i++) {
                if (i > 0) {
                    headSB.append('\t');
                }
                headSB.append(split[i]);
            }
            final String head = headSB.toString();
            if (output.indexOf(head) < 0) {
                final String subset;
                if (split.length > 1 && !split[1].equals("")) {
                    subset = split[1];
                } else {
                    split = line.split("\\s+");
                    subset = split[0];
                }
                final StringBuilder suppMessage = new StringBuilder("\n");
                try (final BufferedReader reader = new BufferedReader(new StringReader(output))) {
                    String outLine;
                    while ((outLine = reader.readLine()) != null) {
                        if (outLine.indexOf(subset) > -1) {
                            if (suppMessage.length() == 0) {
                                suppMessage.append("Did find\n");
                            }
                            suppMessage.append(outLine).append('\n');
                        }
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                throw new FunctionalTestFailed("Could not find " + line + suppMessage);
            }
        });
    }
}