From ee045f4d6e09b27f4c2e6947a7c3f29c96b23e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= Date: Wed, 11 Aug 2021 10:04:16 +0200 Subject: [PATCH] Replace normal file usage with memory-mapped file (#116) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Replace normal file usage with memory-mapped file Signed-off-by: André Silva * Refactor load method Signed-off-by: André Silva --- .../stamp_project/testrunner/EntryPoint.java | 8 ++- .../testrunner/listener/Coverage.java | 4 +- .../listener/CoveragePerTestMethod.java | 4 +- .../CoveredTestResultPerTestMethod.java | 4 +- .../testrunner/listener/TestResult.java | 4 +- .../listener/impl/CoverageDetailed.java | 33 ++------- .../listener/impl/CoverageImpl.java | 40 ++++------- .../impl/CoveragePerTestMethodImpl.java | 34 +++------ .../CoveredTestResultPerTestMethodImpl.java | 33 +++------ .../listener/impl/TestResultImpl.java | 36 ++++------ .../listener/utils/ListenerUtils.java | 68 ++++++++++++++++++ .../testrunner/reader/TestResultReader.java | 56 --------------- .../testrunner/runner/Loader.java | 42 ----------- .../testrunner/listener/Coverage.class | Bin 884 -> 854 bytes .../listener/CoveragePerTestMethod.class | Bin 1001 -> 977 bytes .../testrunner/listener/TestResult.class | Bin 1225 -> 1195 bytes .../listener/impl/CoverageImpl.class | Bin 7343 -> 5934 bytes .../impl/CoveragePerTestMethodImpl.class | Bin 8378 -> 6952 bytes .../listener/impl/TestResultImpl.class | Bin 7281 -> 5928 bytes .../testrunner/runner/Loader.class | Bin 3060 -> 0 bytes 20 files changed, 130 insertions(+), 236 deletions(-) delete mode 100644 src/main/java/eu/stamp_project/testrunner/reader/TestResultReader.java delete mode 100644 src/main/java/eu/stamp_project/testrunner/runner/Loader.java delete mode 100644 src/main/resources/runner-classes/eu/stamp_project/testrunner/runner/Loader.class diff --git a/src/main/java/eu/stamp_project/testrunner/EntryPoint.java b/src/main/java/eu/stamp_project/testrunner/EntryPoint.java index 5ed54042..7498eadf 100644 --- a/src/main/java/eu/stamp_project/testrunner/EntryPoint.java +++ b/src/main/java/eu/stamp_project/testrunner/EntryPoint.java @@ -1,7 +1,10 @@ package eu.stamp_project.testrunner; import eu.stamp_project.mutationtest.descartes.DescartesMutationEngine; -import eu.stamp_project.testrunner.listener.*; +import eu.stamp_project.testrunner.listener.Coverage; +import eu.stamp_project.testrunner.listener.CoveragePerTestMethod; +import eu.stamp_project.testrunner.listener.CoveredTestResultPerTestMethod; +import eu.stamp_project.testrunner.listener.TestResult; import eu.stamp_project.testrunner.listener.impl.CoverageImpl; import eu.stamp_project.testrunner.listener.impl.CoveragePerTestMethodImpl; import eu.stamp_project.testrunner.listener.impl.CoveredTestResultPerTestMethodImpl; @@ -33,7 +36,8 @@ import java.lang.reflect.Method; import java.net.URL; import java.util.*; -import java.util.concurrent.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/src/main/java/eu/stamp_project/testrunner/listener/Coverage.java b/src/main/java/eu/stamp_project/testrunner/listener/Coverage.java index 1ae1b3d3..ee0b3284 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/Coverage.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/Coverage.java @@ -9,12 +9,10 @@ */ public interface Coverage { - public static final String SERIALIZE_NAME = "Coverage"; + public static final String SHARED_MEMORY_FILE = "Coverage.dat"; public static final String OUTPUT_DIR = "target" + ConstantsHelper.FILE_SEPARATOR; - public static final String EXTENSION = ".ser"; - public void setExecutionPath(String executionPath); public int getInstructionsCovered(); diff --git a/src/main/java/eu/stamp_project/testrunner/listener/CoveragePerTestMethod.java b/src/main/java/eu/stamp_project/testrunner/listener/CoveragePerTestMethod.java index 43d83f82..2398415a 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/CoveragePerTestMethod.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/CoveragePerTestMethod.java @@ -12,12 +12,10 @@ */ public interface CoveragePerTestMethod extends Serializable { - public static final String SERIALIZE_NAME = "CoveragePerTest"; + public static final String SHARED_MEMORY_FILE = "CoveragePerTestMethod.dat"; public static final String OUTPUT_DIR = "target" + ConstantsHelper.FILE_SEPARATOR; - public static final String EXTENSION = ".ser"; - public Map getCoverageResultsMap(); public Coverage getCoverageOf(String testMethodName); diff --git a/src/main/java/eu/stamp_project/testrunner/listener/CoveredTestResultPerTestMethod.java b/src/main/java/eu/stamp_project/testrunner/listener/CoveredTestResultPerTestMethod.java index 2eadc65b..c0f1853a 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/CoveredTestResultPerTestMethod.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/CoveredTestResultPerTestMethod.java @@ -13,12 +13,10 @@ */ public interface CoveredTestResultPerTestMethod extends TestResult, Serializable { - public static final String SERIALIZE_NAME = "CoveredTestResultPerTest"; + public static final String SHARED_MEMORY_FILE = "CoveredTestResultPerTest.dat"; public static final String OUTPUT_DIR = "target" + ConstantsHelper.FILE_SEPARATOR; - public static final String EXTENSION = ".ser"; - public Map getCoverageResultsMap(); public Coverage getCoverageOf(String testMethodName); diff --git a/src/main/java/eu/stamp_project/testrunner/listener/TestResult.java b/src/main/java/eu/stamp_project/testrunner/listener/TestResult.java index bf463ba9..2bb392d9 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/TestResult.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/TestResult.java @@ -12,12 +12,10 @@ */ public interface TestResult extends Serializable { - public static final String SERIALIZE_NAME = "TestResult"; + public static final String SHARED_MEMORY_FILE = "TestResult.dat"; public static final String OUTPUT_DIR = "target" + ConstantsHelper.FILE_SEPARATOR; - public static final String EXTENSION = ".ser"; - /** * Aggregate result of this instance to the given instance * @param that the other instance of TestResult of which we need to add the values to this instance diff --git a/src/main/java/eu/stamp_project/testrunner/listener/impl/CoverageDetailed.java b/src/main/java/eu/stamp_project/testrunner/listener/impl/CoverageDetailed.java index 16b4826a..97e527db 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/impl/CoverageDetailed.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/impl/CoverageDetailed.java @@ -1,12 +1,10 @@ package eu.stamp_project.testrunner.listener.impl; +import eu.stamp_project.testrunner.listener.Coverage; +import eu.stamp_project.testrunner.listener.utils.ListenerUtils; + import java.io.File; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; import java.io.Serializable; -import eu.stamp_project.testrunner.listener.Coverage; -import eu.stamp_project.testrunner.listener.TestResult; -import eu.stamp_project.testrunner.runner.Loader; /** * created by Benjamin DANGLOT benjamin.danglot@inria.fr on 14/11/18 @@ -54,33 +52,16 @@ public String toString() { @Override public void save() { - File outputDir = new File(TestResult.OUTPUT_DIR); - if (!outputDir.exists()) { - if (!outputDir.mkdirs()) { - System.err.println("Error while creating output dir"); - } - } - File f = new File(outputDir, SERIALIZE_NAME + EXTENSION); - try (FileOutputStream fout = new FileOutputStream(f)) { - try (ObjectOutputStream oos = new ObjectOutputStream(fout)) { - oos.writeObject(this); - } catch (Exception e) { - throw new RuntimeException(e); - } - } catch (Exception e) { - System.err.println("Error while writing serialized file."); - throw new RuntimeException(e); - } - System.out.println("File saved to the following path: " + f.getAbsolutePath()); + ListenerUtils.saveToMemoryMappedFile(new File(OUTPUT_DIR, SHARED_MEMORY_FILE), this); } /** - * Load from serialized object + * Loads and deserializes the file from a memory mapped file * - * @return an Instance of JUnit4Coverage loaded from a serialized file. + * @return loaded CoverageDetailed from the memory mapped file */ public static Coverage load() { - return new Loader().load(SERIALIZE_NAME); + return ListenerUtils.loadFromMemoryMappedFile(ListenerUtils.computeTargetFilePath(OUTPUT_DIR, SHARED_MEMORY_FILE)); } @Override diff --git a/src/main/java/eu/stamp_project/testrunner/listener/impl/CoverageImpl.java b/src/main/java/eu/stamp_project/testrunner/listener/impl/CoverageImpl.java index 7a85b150..4d59dd36 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/impl/CoverageImpl.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/impl/CoverageImpl.java @@ -1,10 +1,13 @@ package eu.stamp_project.testrunner.listener.impl; import eu.stamp_project.testrunner.listener.Coverage; -import eu.stamp_project.testrunner.listener.TestResult; -import eu.stamp_project.testrunner.runner.Loader; -import org.jacoco.core.analysis.*; -import java.io.*; +import eu.stamp_project.testrunner.listener.utils.ListenerUtils; +import org.jacoco.core.analysis.IClassCoverage; +import org.jacoco.core.analysis.ICounter; +import org.jacoco.core.analysis.ILine; + +import java.io.File; +import java.io.Serializable; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; @@ -89,35 +92,22 @@ public String toString() { return this.instructionsCovered + " / " + this.instructionsTotal; } + /** + * Writes the serialized object to a memory mapped file. + * The location depends on the workspace set for the test runner process. + */ @Override public void save() { - File outputDir = new File(TestResult.OUTPUT_DIR); - if (!outputDir.exists()) { - if (!outputDir.mkdirs()) { - System.err.println("Error while creating output dir"); - } - } - File f = new File(outputDir, SERIALIZE_NAME + EXTENSION); - try (FileOutputStream fout = new FileOutputStream(f)) { - try (ObjectOutputStream oos = new ObjectOutputStream(fout)) { - oos.writeObject(this); - } catch (Exception e) { - throw new RuntimeException(e); - } - } catch (Exception e) { - System.err.println("Error while writing serialized file."); - throw new RuntimeException(e); - } - System.out.println("File saved to the following path: " + f.getAbsolutePath()); + ListenerUtils.saveToMemoryMappedFile(new File(OUTPUT_DIR, SHARED_MEMORY_FILE), this); } /** - * Load from serialized object + * Loads and deserializes the file from a memory mapped file * - * @return an Instance of JUnit4Coverage loaded from a serialized file. + * @return loaded Coverage from the memory mapped file */ public static Coverage load() { - return new Loader().load(SERIALIZE_NAME); + return ListenerUtils.loadFromMemoryMappedFile(ListenerUtils.computeTargetFilePath(OUTPUT_DIR, SHARED_MEMORY_FILE)); } } diff --git a/src/main/java/eu/stamp_project/testrunner/listener/impl/CoveragePerTestMethodImpl.java b/src/main/java/eu/stamp_project/testrunner/listener/impl/CoveragePerTestMethodImpl.java index c1eda232..1b7e049c 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/impl/CoveragePerTestMethodImpl.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/impl/CoveragePerTestMethodImpl.java @@ -3,16 +3,13 @@ import eu.stamp_project.testrunner.listener.Coverage; import eu.stamp_project.testrunner.listener.CoveragePerTestMethod; import eu.stamp_project.testrunner.listener.CoverageTransformer; -import eu.stamp_project.testrunner.listener.TestResult; -import eu.stamp_project.testrunner.runner.Loader; +import eu.stamp_project.testrunner.listener.utils.ListenerUtils; import eu.stamp_project.testrunner.utils.ConstantsHelper; import org.jacoco.core.data.ExecutionDataStore; import org.jacoco.core.data.SessionInfoStore; import org.jacoco.core.runtime.RuntimeData; import java.io.File; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -109,35 +106,22 @@ public Coverage getCoverageOf(String testMethodName) { return this.getCoverageResultsMap().get(testMethodName); } + /** + * Writes the serialized object to a memory mapped file. + * The location depends on the workspace set for the test runner process. + */ @Override public void save() { - File outputDir = new File(TestResult.OUTPUT_DIR); - if (!outputDir.exists()) { - if (!outputDir.mkdirs()) { - System.err.println("Error while creating output dir"); - } - } - File f = new File(outputDir, SERIALIZE_NAME + EXTENSION); - try (FileOutputStream fout = new FileOutputStream(f)) { - try (ObjectOutputStream oos = new ObjectOutputStream(fout)) { - oos.writeObject(this); - } catch (Exception e) { - throw new RuntimeException(e); - } - } catch (Exception e) { - System.err.println("Error while writing serialized file."); - throw new RuntimeException(e); - } - System.out.println("File saved to the following path: " + f.getAbsolutePath()); + ListenerUtils.saveToMemoryMappedFile(new File(OUTPUT_DIR, SHARED_MEMORY_FILE), this); } /** - * Load from serialized object + * Loads and deserializes the file from a memory mapped file * - * @return an Instance of CoveragePerTestMethod loaded from a serialized file. + * @return loaded CoveragePerTestMethodImpl from the memory mapped file */ public static CoveragePerTestMethodImpl load() { - return new Loader().load(SERIALIZE_NAME); + return ListenerUtils.loadFromMemoryMappedFile(ListenerUtils.computeTargetFilePath(OUTPUT_DIR, SHARED_MEMORY_FILE)); } @Override diff --git a/src/main/java/eu/stamp_project/testrunner/listener/impl/CoveredTestResultPerTestMethodImpl.java b/src/main/java/eu/stamp_project/testrunner/listener/impl/CoveredTestResultPerTestMethodImpl.java index 5f117d4f..7723c371 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/impl/CoveredTestResultPerTestMethodImpl.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/impl/CoveredTestResultPerTestMethodImpl.java @@ -4,16 +4,14 @@ import eu.stamp_project.testrunner.listener.CoverageTransformer; import eu.stamp_project.testrunner.listener.CoveredTestResultPerTestMethod; import eu.stamp_project.testrunner.listener.TestResult; +import eu.stamp_project.testrunner.listener.utils.ListenerUtils; import eu.stamp_project.testrunner.runner.Failure; -import eu.stamp_project.testrunner.runner.Loader; import eu.stamp_project.testrunner.utils.ConstantsHelper; import org.jacoco.core.data.ExecutionDataStore; import org.jacoco.core.data.SessionInfoStore; import org.jacoco.core.runtime.RuntimeData; import java.io.File; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -165,35 +163,22 @@ public Failure getFailureOf(String testMethodName) { .orElseThrow(() -> new IllegalArgumentException(String.format("Could not find %s in failing test", testMethodName))); } + /** + * Writes the serialized object to a memory mapped file. + * The location depends on the workspace set for the test runner process. + */ @Override public void save() { - File outputDir = new File(TestResult.OUTPUT_DIR); - if (!outputDir.exists()) { - if (!outputDir.mkdirs()) { - System.err.println("Error while creating output dir"); - } - } - File f = new File(outputDir, SERIALIZE_NAME + EXTENSION); - try (FileOutputStream fout = new FileOutputStream(f)) { - try (ObjectOutputStream oos = new ObjectOutputStream(fout)) { - oos.writeObject(this); - } catch (Exception e) { - throw new RuntimeException(e); - } - } catch (Exception e) { - System.err.println("Error while writing serialized file."); - throw new RuntimeException(e); - } - System.out.println("File saved to the following path: " + f.getAbsolutePath()); + ListenerUtils.saveToMemoryMappedFile(new File(OUTPUT_DIR, SHARED_MEMORY_FILE), this); } /** - * Load from serialized object + * Loads and deserializes the file from a memory mapped file * - * @return an Instance of CoveragePerTestMethod loaded from a serialized file. + * @return loaded CoveredTestResultPerTestMethodImpl from the memory mapped file */ public static CoveredTestResultPerTestMethodImpl load() { - return new Loader().load(SERIALIZE_NAME); + return ListenerUtils.loadFromMemoryMappedFile(ListenerUtils.computeTargetFilePath(OUTPUT_DIR, SHARED_MEMORY_FILE)); } @Override diff --git a/src/main/java/eu/stamp_project/testrunner/listener/impl/TestResultImpl.java b/src/main/java/eu/stamp_project/testrunner/listener/impl/TestResultImpl.java index e67a996f..6a9f50ea 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/impl/TestResultImpl.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/impl/TestResultImpl.java @@ -1,16 +1,12 @@ package eu.stamp_project.testrunner.listener.impl; import eu.stamp_project.testrunner.listener.TestResult; +import eu.stamp_project.testrunner.listener.utils.ListenerUtils; import eu.stamp_project.testrunner.runner.Failure; -import eu.stamp_project.testrunner.runner.Loader; import java.io.File; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -83,30 +79,22 @@ public Failure getFailureOf(String testMethodName) { .orElseThrow(() -> new IllegalArgumentException(String.format("Could not find %s in failing test", testMethodName))); } + /** + * Writes the serialized object to a memory mapped file. + * The location depends on the workspace set for the test runner process. + */ @Override public synchronized void save() { - File outputDir = new File(OUTPUT_DIR); - if (!outputDir.exists()) { - if (!outputDir.mkdirs()) { - System.err.println("Error while creating output dir"); - } - } - File f = new File(outputDir, SERIALIZE_NAME + EXTENSION); - try (FileOutputStream fout = new FileOutputStream(f)) { - try (ObjectOutputStream oos = new ObjectOutputStream(fout)) { - oos.writeObject(this); - } catch (Exception e) { - throw new RuntimeException(e); - } - } catch (Exception e) { - System.err.println("Error while writing serialized file."); - throw new RuntimeException(e); - } - System.out.println("File saved to the following path: " + f.getAbsolutePath()); + ListenerUtils.saveToMemoryMappedFile(new File(OUTPUT_DIR, SHARED_MEMORY_FILE), this); } + /** + * Loads and deserializes the file from a memory mapped file + * + * @return loaded TestResult from the memory mapped file + */ public static TestResult load() { - return new Loader().load(SERIALIZE_NAME); + return ListenerUtils.loadFromMemoryMappedFile(ListenerUtils.computeTargetFilePath(OUTPUT_DIR, SHARED_MEMORY_FILE)); } public String toString() { diff --git a/src/main/java/eu/stamp_project/testrunner/listener/utils/ListenerUtils.java b/src/main/java/eu/stamp_project/testrunner/listener/utils/ListenerUtils.java index ce9002fd..7f2ff582 100644 --- a/src/main/java/eu/stamp_project/testrunner/listener/utils/ListenerUtils.java +++ b/src/main/java/eu/stamp_project/testrunner/listener/utils/ListenerUtils.java @@ -1,8 +1,15 @@ package eu.stamp_project.testrunner.listener.utils; +import eu.stamp_project.testrunner.EntryPoint; +import eu.stamp_project.testrunner.listener.CoveredTestResultPerTestMethod; import org.jacoco.core.data.ExecutionData; import org.jacoco.core.data.ExecutionDataStore; +import java.io.*; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.StandardOpenOption; + public class ListenerUtils { /** @@ -21,4 +28,65 @@ public static ExecutionDataStore cloneExecutionDataStore(ExecutionDataStore orig return cloned; } + public static void saveToMemoryMappedFile(File file, Object object) { + try { + // Serialize the object + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bos); + out.writeObject(object); + out.flush(); + byte[] bytes = bos.toByteArray(); + + // Create output dir if it does not exist + File outputDir = new File(CoveredTestResultPerTestMethod.OUTPUT_DIR); + if (!outputDir.exists()) { + if (!outputDir.mkdirs()) { + System.err.println("Error while creating output dir"); + } + } + + // Write to shared memory file + FileChannel channel = FileChannel.open(file.toPath(), + StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE); + MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, bytes.length); + buffer.put(bytes); + + out.close(); + channel.close(); + System.out.println("File saved to the following path: " + file.getAbsolutePath()); + } catch (Exception e) { + System.err.println("Error while writing memory-mapped serialized file."); + throw new RuntimeException(e); + } + } + + public static T loadFromMemoryMappedFile(File file) { + try { + // Access the shared memory file + FileChannel channel = FileChannel.open(file.toPath(), StandardOpenOption.READ); + MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); + + // Get content + byte[] buffer = new byte[(int) channel.size()]; + mappedByteBuffer.get(buffer); + + // Read and deserialize the file + ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(buffer)); + final T load = (T) is.readObject(); + + is.close(); + channel.close(); + file.delete(); + return load; + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + public static File computeTargetFilePath(String outputDir, String outputFile) { + return new File( + new File(EntryPoint.workingDirectory != null && EntryPoint.workingDirectory.exists() ? EntryPoint.workingDirectory.getAbsolutePath() : "./", + outputDir).getAbsolutePath(), + outputFile); + } } \ No newline at end of file diff --git a/src/main/java/eu/stamp_project/testrunner/reader/TestResultReader.java b/src/main/java/eu/stamp_project/testrunner/reader/TestResultReader.java deleted file mode 100644 index 2408bdcd..00000000 --- a/src/main/java/eu/stamp_project/testrunner/reader/TestResultReader.java +++ /dev/null @@ -1,56 +0,0 @@ -package eu.stamp_project.testrunner.reader; - -import eu.stamp_project.testrunner.listener.junit4.JUnit4TestResult; -import eu.stamp_project.testrunner.runner.Failure; -import eu.stamp_project.testrunner.runner.Loader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; - -/** - * created by Benjamin DANGLOT - * benjamin.danglot@inria.fr - * on 11/09/18 - */ -public class TestResultReader { - - private static final Logger LOGGER = LoggerFactory.getLogger(TestResultReader.class); - - /** - * @param args should contain only on argument: the path to the testResult.ser to be read - */ - public static void main(String[] args) { - if (!new File("target/dspot/JUnit4TestResult.ser").exists()) { - usage(); - } - boolean verbose = false; - if (args.length >= 1 && "--verbose".equals(args[1])) { - verbose = true; - } - JUnit4TestResult JUnit4TestResult = new Loader().load("JUnit4TestResult"); - LOGGER.info("Test that has been run:"); - JUnit4TestResult.getRunningTests().forEach(running -> LOGGER.info("\t{}", running)); - LOGGER.info("Passing tests:"); - JUnit4TestResult.getPassingTests().forEach(passing -> LOGGER.info("\t{}", passing)); - LOGGER.info("Failing tests:"); - if (verbose) { - JUnit4TestResult.getFailingTests().stream() - .map(failure -> failure.toString() + ":" + failure.stackTrace) - .forEach(failure -> LOGGER.info("\t{}", failure)); - } else { - JUnit4TestResult.getFailingTests().stream().map(Failure::toString).forEach(failure -> LOGGER.info("\t{}", failure)); - } - LOGGER.info("Assumption failing tests:"); - JUnit4TestResult.getAssumptionFailingTests().stream().map(Failure::toString).forEach(assumptionFailure -> LOGGER.info("\t{}", assumptionFailure)); - LOGGER.info("Ignored tests:"); - JUnit4TestResult.getIgnoredTests().forEach(ignored -> LOGGER.info("\t{}", ignored)); - } - - private static void usage() { - LOGGER.error("Usage: java -cp test-runner--jar-with-dependencies.jar eu.stamp_project.testrunner.reader.TestResultReader"); - LOGGER.error("The target/dspot/JUnit4TestResult.ser must exist and created by the EntryPoint"); - System.exit(1); - } - -} diff --git a/src/main/java/eu/stamp_project/testrunner/runner/Loader.java b/src/main/java/eu/stamp_project/testrunner/runner/Loader.java deleted file mode 100644 index 91af153e..00000000 --- a/src/main/java/eu/stamp_project/testrunner/runner/Loader.java +++ /dev/null @@ -1,42 +0,0 @@ -package eu.stamp_project.testrunner.runner; - -import eu.stamp_project.testrunner.EntryPoint; -import eu.stamp_project.testrunner.listener.impl.TestResultImpl; -import eu.stamp_project.testrunner.utils.ConstantsHelper; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.ObjectInputStream; - -/** - * Created by Benjamin DANGLOT - * benjamin.danglot@inria.fr - * on 21/12/17 - */ -public class Loader { - - public T load(String name) { - T object; - String outputDirectoryPath = EntryPoint.workingDirectory != null ? - EntryPoint.workingDirectory.getAbsolutePath() + ConstantsHelper.FILE_SEPARATOR : ""; - File f = new File(outputDirectoryPath + TestResultImpl.OUTPUT_DIR + name + TestResultImpl.EXTENSION); - if (!f.exists()) { - throw new RuntimeException(new FileNotFoundException(f.getAbsolutePath() + " does not exist.")); - } else { - System.out.println("Loading " + f.getAbsolutePath()); - } - try (FileInputStream fin = new FileInputStream(f);) { - try (ObjectInputStream ois = new ObjectInputStream(fin)) { - object = (T) ois.readObject(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } catch (Exception ex) { - throw new RuntimeException(ex); - } - f.delete(); - return object; - } - -} diff --git a/src/main/resources/runner-classes/eu/stamp_project/testrunner/listener/Coverage.class b/src/main/resources/runner-classes/eu/stamp_project/testrunner/listener/Coverage.class index e1fa36be3798a6bf066c481e15e6126764aa9687..f8a453d7edd33dc71fe0ed9c19452c5bc7dc2fe2 100644 GIT binary patch delta 239 zcmeyuc8$&X)W2Q(7#J8#7!24M8W}ti6xUwnPxLGO}@w^Y0b{U z$iUAaz`y{c1sR0Ev@nzwVGsrL#TdlFv;>1B(ES_?Qb3xKL3*+&vzDj~P=pQ0V_@I| LlCnUWi9rqk5y>MG delta 289 zcmYk0yH3Jz6o4#w<=uu}(DU1Y$^PrpxvCscQt0DhJc`B+4Gs;C z9FEC3oZu#(>Z)M2?jNIgIEob6#zPb*RqZ9pu!K!cIyll%V@f6zt&`+0(t6dCX4=`+zugHMx@fy+NMBp@^--o-O$V|zZMnihjNBLiDW zVo`c(2@iudgAO}`E)Rnq0}l^_K7#=d13v=~BZJjsJ4W5fo=ldEl9L^oL|ODw5=$n} zWZJ^WG}(_?(wLQnkwJh#5ac0M;u5)+-ADsaYaM delta 291 zcmYk1OHRUI6otPZkiW$Wq9URKzC~1gu>sVYG*KW>5@TWrgNB(TrE%mYuq!cfJc+1Gi{q8wAH#zrP|LDuF-;YnAPF=H+La}ME6>-~Shuvt~GcnlLR5S;gsv_OB zJ5J-)d9eG<#@*zmQan_oT0OVjbNkm$M`7CcuHEc9t){`z+sjCmjwvXT!N4DSfkllI z&8fv1mc=<2p&5J1XRb!iolG4BP}{38sYpu vKg&!)vB07vm!`>_=w+0(t6dCX4=`+zugHNB4fy+NMBp@^--o-O$W4k<)ngN3xBLiDW zVo`c(2@iuIgAqG}F%Qr#9v%i$1~VQ8eg+;!2K~v7j53p7Fxm6-g`^gj1f>?2=9K8A zB$iCJWZuHaH2EE~WIZPfBZB~gAOi!C7Ge+vI!XlSXeI_xAYTlPFOI~Q0Ln`;NP+c9 zqlwF)@nsq0fM#+q$OCCc1_dD75NHDkNKT%{A}6W{WU&Ed7=Z3zWKaUqObp5Z{&*;5 delta 331 zcmZ9G%Syvw5QWbqp@07HD_X4@wQaoDR_pz`s~90*Q%O@0H$^14>8go4!Do;>hAY91 zF5K%=_$1=AE6M82H;0*XUgVcN`~Lm-1h9`&fmI8HH4~MAu1i!=8%!G#CN>4?0$T#x zgweJec#TW%#_jrz=JQ3RxI?g7o$#s?cF(WG_1j*{H?jNrSk_8=a0t2NwtpWb zisKFh4i%1|6pnF{zH4`RD~#eKh~kG{LNMxa)K6>r8|mqfZa>)=gW@tHTvo$4q(R7I z0$`m)ft{(LGtE4cVf!D?vOkCU;axGaFJ-t4hku$_fMamLV%jsRg(Y6)n7}0n%d8q! F{s4LvDIov= diff --git a/src/main/resources/runner-classes/eu/stamp_project/testrunner/listener/impl/CoverageImpl.class b/src/main/resources/runner-classes/eu/stamp_project/testrunner/listener/impl/CoverageImpl.class index 02a4c0fb0b3ed696a4e3f8cf0340162537a34c55..5a5a4fd9883c83bb9cdae94ef7c04276cf74cc4d 100644 GIT binary patch delta 2096 zcmZvdd0bRg6vw}B26!;sVjKZeTtH+IP*O=0T*wq7F*ndOQ%4zbj4c=_Z6RB&Y<*>= z*xZo_pn%#d!xCJ z`*nm%JUDnzUmx<|C0rbV%Zt%_&aX0lze zgPn@!c;4Xjx!wkEc74EKkzMSs3n)fVs2HiqxfGeapm>qB1|4j@3U5_;_JlyKzp8v# zy}zQ&SF3o5l$Q;hiP?#oe?>8sA_HaX`>V4H{S^|_tK?dzMGf+odBw#5UNhOHc%3&C zZ}OI6H+xL>D&E$9-=WatUB!F6ulRru-Fy@i#m8>;=`21`e9C?ieCFnJEjXa~f^7zo zgJxCutNei>iZ3a$_PUNH@fBYi#HAOX2!71WxxQI}{GL->G9m}9gV7tDnS9f zBCR7a5jn?D4bG^j_LddaR#*N%gzhJYU>%OxAI7(QXYFzCh~#^IFc^_;EpR4S>)TCD z;zz|HepZy}A)0~L@zv4_>JGKY)_7ZKbw4>`G$kPtOML_gxPq@%zG3Y@QDdHuSSki3cL|He83mI#!KqAs!<2YM5 zhYdJPXr9$IE~W7VUQfZx5WGym%aYaaW#eUt6hRLLqq$>vu^~Jy)7d%&^Bjh2WU&|` zo^)Cf8JQaiKQ-Atl0jdMKV6cs)s8!eu)%bpWIID(y41nKZNRZoP=kewu!a5GEShY! zHnx)Dp%d-b|N_X1bo*W}lQ~b}O)UA+RW>N#}!u zk|rVQUt^4o)J(T#5_giYR%C{~AHkri+%CPTA*%)%H*ME9vxOuBq1_F_W;BzWIe8O3 zCT)?mpoLV2Jep~mOLvfAQ;i!OpWqr5(OH_BO5-T$O|k59QLDc7wYw!2GOaV2g&{%6 zn-L14NPu)5Vm&|q}H58CW$v;Y7A literal 7343 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 diff --git a/src/main/resources/runner-classes/eu/stamp_project/testrunner/listener/impl/CoveragePerTestMethodImpl.class b/src/main/resources/runner-classes/eu/stamp_project/testrunner/listener/impl/CoveragePerTestMethodImpl.class index a5d755c73ce768533013e1efc5787a44fe4b2f0d..70c9890fc70d7a8b36937825839becfdc2515dc1 100644 GIT binary patch delta 2466 zcmZ`*d013c6#v~pc+8vo4a~3;;)W~(I+|oDC2ol#1Sx20VxzoZ5Ez;nOwF1?D$Q13 zmX>L@TDGX@uvjLQEw-4p+Gb^zEtc)8*17Ku1M-c3%zgLVbI$MV=bMgkn@9Tgo$A;P zAeTYL*=RB%1d zjje**CSkjT78!%l6p0<;yE77gp}0;s-zr{OO<0UJ3GF6yV3#1C5dzOj*v$~_QQS^P z?F_~3ak}cur%YrmbJ;;DvQMqcQOQuCZjH!PD`NVo>2foBPTeARW)-5EV>~8s9qe>Nty9z@x%B-a@^) zk>|4{^+2Dv)S;CQFGnE`NI1yxHs0ZQ7w>Vrj}I6o=$b|fCUqpoA$%m^V~$U-HyZoN z?5ELa#N2$I%_Kx zHv=>)a=4=ao;P??IIDW!$j-T<1niI8l0Di@93~`x7-H;{o zYm^1vysR0bU4B=OMjeZiOUlbi%gg3XoHC6eBU9~UG3wHISxvH-)FZL!IU@5Ody&)Q zRq7RYxu6*0J&r|6nX6c-bGesLNg9;ONzPhDU1jMzIB0`xFL2d0G_R{3jS!xiCM!`n{`x6XKXgJ z;7^Xf@Gr-eqRL)0swd-e4LGHah(Bb&f2uj5$d^K$OCcZ<$ryoLx}QU1WWY$=DLRYj z(Hfmcb3@G)q{k3VEg7W8p;`1P$u^7D)4tSw+L!u~zGAKlF_o~3h+2;@8gHnF5{CPV ziB6>{0c`Y^4I^@3#tNHSU zW2vx76skyt-Z{yER4}nng&pGb>=K^;9iF291x`;wIR6;XYE0XaF6wvk7Y#B)($9DkdkmAjYi4nM_)fuZ5`+b zw4=WP+cBUG1KTl31e(?rDA|vvDg)Qi=4c|DCNCGPCJt&7wniK!lA_>W6ssBd5z`*KjWv7JPx8@41c^iR6+o+P48?jnp8b zMKTCireY0Y6vI#=!rEaEL$PMA9m5!M{1`s7JLP(8vnj$kl;_z}5W z1k0L%Ww@FwCQur#AcxXn@sd#`a85F!btycBz#CK_HUfTKh(6h6JJd4 R9VD|FDy@BS+l`t;f&DD!7M^gwiNtNKEnP@0ZNXd++8QGj|3I} zAnBI4wa2uL9p+{;lS^hZ4hQYNJ>pg>p0jN;m5osl?{lq@8Fg9PHAT>Be44o!6*HE; zGHqKs&3HDJH8WW|mr9v-ESbn;P4&_1@#k>c6i*tNjG0-TunBM3`>Ayw0|P{&Ol@UT zRrXe**DAdT&aTiGS;!{F%-Cl4t*k@vX(i?obX4F?q-TLno|RMdr$(%dV19Mf!6K03 z4cv5k-aJFLk;;r%_Lyl4x(7~5bv|5@3@uNj64@03yS;*qYLb^Jv*(%b-LaKQ@3n^M z#f<}rl(``{w%xRcjO|Izv?b)nEtDK|#3Ry0!|<<(+C`CJvh zsBnXc*jI_NDQ=CWb6GQQ5%M2sSB%Z{naN*2hNi;7Ntw~f{?mNhZ5$EwdUeWQ{7%w6 zC|!f9I&PZSV2nAL$kC{uD)Uyx*u%AerSkV_uFexyY~yxSxHjgp)DO$AX^fGg5OAgo z;t=WE7dO+Y&U3AH#L8s_a|#sJB$8$semy}*k428$l>jQ@6nxrX)`;(70d~?P`z!V{ zY@660v1wv=#BGw6F)X-bs^RiqQ)gY)a@z~RjJ$PAHPoCm#Y%!s|B%>OD!};c+kq(aZ}k)a7La|Md?|j97>0Q13I?hc{;Y@xq{|m;ClU( zj*ZwvtrZt0>%MdhVo1l8c%hEx;{`e{#}yi$((yENzKAY05@7JIvaBrIDkJUsc&0HV zwM`Oi)Fjk|+jP;SPLq0F!V=MOHD0U}bDE&*q~en{ahPUEC}n8Upi3jJr_9o%OS81- zGF@6UnW4)}nI)LpXWN$Dx_6Yot#QUpmYr*>>-Vk030srdx|||S>~&6xj}q0hoR*nn z5nFV{QOg4bjg78P;gwfPma*G@S4aSs@YtIXln5h`wxF0D$Lvy0oiF}5m~NDiw;@l0im zR+YMjTeDVccGPSgv64w^uhMqf$c}cm>M~Ex)a5MPqT_A2g>`x|g|k~wP5A(`i=y9H zre7Uzkv1mg$w(AB`^j^TE@#VpP1<$okU*16qrY}G;Z|K1$U?Ndu~lN+bU-*t@SQgKz?)cG)z;{k;Nxh`4B zEHe`iF?~jSRM6^A+m%FA`VO$-msOFxWWK`UO)-H_$?w#$!0lF;z&CCg+O%cp%GLdw z%QWall2b%N<~|-Q-LB_()aBSNW+)E*uC|}YRoW?F+6)V?_%j)9;)%T27`S<&bebAT zE1+abaF(YLFN^m0Wc0DqTn42S6)n#BbD6__M5PZi@2|ml(#o(}O*4jv2XpDP%|oZj zRv}+ROS70mAi%t=TqvD3Q-tjD_VQQo%A)S&Ao_wj3as3ov65^dH!+?_L1akC^_l#`&CTHK{4 zSnL~0zNRzb;iUG^om7{Au;5WHPz-=?v8mZx=nQ3z&t7I}C z%O!|Iyp{2TPkqtda&lb1T0F|VEVP46kUg68^ER+TA=Ttzvp+c;=J5u+pBKxgp$>Df z4wv$KfcN7ySdTZVHlO3?IOEN{rDh{v7;oX2=YzudGF+z)4TQZ;wfWAQoxbyC@6Wfo zz&5a*wBsDziU9BJfzHk&kj@2%QPa8bFan*&P`m92f`<{}r^e5^!>I4QLjx8giX~{nx#-}k`9I3J5PMj~rca8I3@JX9*nT242EWW5}DRaN8+vQ?17<;ZKQBImHjLta5mDRqY&Gb=N?nhQI# z>ky>dDU;1|p$aAoc_tI=#lC8^BIUF;kk-ZtXep;u2HcOUs?pNRX>BI0!3k(7$5ymn zgcp%Qnt1oA7)Q4Q^yVsDNYwSEnH7Jf*bPU2BtAYbwHLL!;Ec*F!Km+z88#R_AO}P>y%p%IfQ7- zoN=7002Z{IegtQ%KZd#679K(C2I;OnhI!k%j^NDhpmc|#L9%sLGg)q;LFr&Mk2=xvES4kvVcl;n4yMxEjzAYL& zf{x=|C$K46djhk%#?g5YtGk2IS_R5Imbp_)I2G*Xq38Obr*}sP9dV#F1sx5O z{>%+ZSjtUd+m%!u#}8rQF?2BnE^3L5WAVaqEa7V$=Mv~nYHkY?+U5L9V;|#ZxA(q^ z_PGJC#%np=P4lbqGTesSF&}s19(;!L%jiZAdGANK{y3h%lN>*UN1?_Gc_kAhyqj#V zz--Rhh#Gv@TF6BV8r^AT_v_ zR1Zi9S4#t~k=b~eoJX%vj=Po#eHByj%kc_E-9d!$O1z3+=i@=jaw7u7YIf%6^s?0R zs)%&pHI!7yaUEXAuNu6b)DC0jzc5e38=giFGz~ZL)A_ks!y7fciBAY0kxTek%ipIl z9re}W3b3_LlX|cW2w0`z&5@0Y*qQ>dW<)CB$X0<=K#>X*KwKittYmM&TfI!l5|D8oC01!|GAu=Y~UQWAWF1Wmtvw z3Af|@%0%=;RjWRL_wYT3<9G3Y1>PO1=bz&>IS2K!S}u`3M#K@WtLpVh!W?Cs9do{Z JiC^K@{{gd7{NVrq diff --git a/src/main/resources/runner-classes/eu/stamp_project/testrunner/listener/impl/TestResultImpl.class b/src/main/resources/runner-classes/eu/stamp_project/testrunner/listener/impl/TestResultImpl.class index a7dfd0ed9e52a7762a1e51a2138a5925bbbbe07e..9196bc3d6659e57f7e236bd8a2c0e2d5176412bd 100644 GIT binary patch delta 2303 zcmZ`)d0bRg6#m`-^M=P$W=0q{5F}w>Kt>b|GX%|03)B=*GO1BsI0!Q|GgxXm*=e;Ye zkg#0Fm26z4P~6p4io1?|u4i$B6)_MBZpJM#ZVk$~jlpl1afbb?!w1A3JQLLhr#dsE{ zv6z6(GTKzMqeJYMZJAG-u_e)nPBRv&cm`W#Y*X|4Et4l zjxQwi75SFF`4Unh{*xP<8kYK+Js!8Gj#vFu@R#^X#hEB)?Q3yN8D_6WxrD^Lba$P{ z>(gqx9M2B<98fV2^Ce`6sm2W9O(?SApo(wsEhqAwitkaQ;t+O;j)aA=_yIo(yP9Xf z;ntaIp=7`jv8vY)1CEN8UPq1iNkUe(s4&{ap5AK=_H0paO%=z@rr4?oi}87UYkp_B z+aJ(8nr|js5^R3gBCXO}p*4Daiz{4BOqp$&a2nv2gc+OO7eVfV^ecL zt91G5v;ccda|ISiDC|0`cct6wmXm&qQh+a+XETX);z z$fgeemrc#yZI>XCbQ%+qWrX0RL6+!S;An^B=!BuV9Wk3B_VvjgGm+K@z({f+Vkw(M zq>?=dnMlJhq+=58sG(>~#u;Qu++CDGnHZO3b8@^<-yag?8yEHsQA0q3ac*~WmC!{pAC^uG zsNRTy*3@zVSQAg@PdItt?M+#Rf zMe)q;^lF8JmQxU?P}+$MYVDJ(L)wt(XhT*T?AGj%-iP&^kWO26Yi=8Y?371_d088V zwjn=4YG@=h^rHFgk2+#x0X38xCD5WuC=D@VR(Qa}$&^sraWcuBa8_G~#coAGb&O+1 z2Zpy}MC1Uz`XGqw2qDQjyC5`rGs0-aR2q{PpSuZ#lC;$Ke literal 7281 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