diff --git a/example/Abnormals/BuildFailure/kgenprog.toml b/example/Abnormals/BuildFailure/kgenprog.toml new file mode 100644 index 000000000..2df10afbb --- /dev/null +++ b/example/Abnormals/BuildFailure/kgenprog.toml @@ -0,0 +1,4 @@ +root-dir = "./" +src = ["src/example/CloseToZero.java"] +test = ["src/example/CloseToZeroTest.java"] + diff --git a/example/Abnormals/BuildFailure/src/example/CloseToZero.java b/example/Abnormals/BuildFailure/src/example/CloseToZero.java new file mode 100644 index 000000000..b9aafe274 --- /dev/null +++ b/example/Abnormals/BuildFailure/src/example/CloseToZero.java @@ -0,0 +1,15 @@ +package example; + +public class CloseToZero { + + public int close_to_zero(int n) { + if (n == 0) { + k++; // build failure + } else if (n > 0) { + n--; + } else { + n++; + } + return n; + } +} diff --git a/example/Abnormals/BuildFailure/src/example/CloseToZeroTest.java b/example/Abnormals/BuildFailure/src/example/CloseToZeroTest.java new file mode 100644 index 000000000..583670e2a --- /dev/null +++ b/example/Abnormals/BuildFailure/src/example/CloseToZeroTest.java @@ -0,0 +1,26 @@ +package example; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class CloseToZeroTest { + @Test + public void test01() { + assertEquals(9, new CloseToZero().close_to_zero(10)); + } + + @Test + public void test02() { + assertEquals(99, new CloseToZero().close_to_zero(100)); + } + + @Test + public void test03() { + assertEquals(0, new CloseToZero().close_to_zero(0)); + } + + @Test + public void test04() { + assertEquals(-9, new CloseToZero().close_to_zero(-10)); + } +} diff --git a/example/Abnormals/NoBugs/kgenprog.toml b/example/Abnormals/NoBugs/kgenprog.toml new file mode 100644 index 000000000..2df10afbb --- /dev/null +++ b/example/Abnormals/NoBugs/kgenprog.toml @@ -0,0 +1,4 @@ +root-dir = "./" +src = ["src/example/CloseToZero.java"] +test = ["src/example/CloseToZeroTest.java"] + diff --git a/example/Abnormals/NoBugs/src/example/CloseToZero.java b/example/Abnormals/NoBugs/src/example/CloseToZero.java new file mode 100644 index 000000000..340e19aeb --- /dev/null +++ b/example/Abnormals/NoBugs/src/example/CloseToZero.java @@ -0,0 +1,15 @@ +package example; + +public class CloseToZero { + + public int close_to_zero(int n) { + if (n == 0) { + ; // do nothing, means no bugs + } else if (n > 0) { + n--; + } else { + n++; + } + return n; + } +} diff --git a/example/Abnormals/NoBugs/src/example/CloseToZeroTest.java b/example/Abnormals/NoBugs/src/example/CloseToZeroTest.java new file mode 100644 index 000000000..583670e2a --- /dev/null +++ b/example/Abnormals/NoBugs/src/example/CloseToZeroTest.java @@ -0,0 +1,26 @@ +package example; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class CloseToZeroTest { + @Test + public void test01() { + assertEquals(9, new CloseToZero().close_to_zero(10)); + } + + @Test + public void test02() { + assertEquals(99, new CloseToZero().close_to_zero(100)); + } + + @Test + public void test03() { + assertEquals(0, new CloseToZero().close_to_zero(0)); + } + + @Test + public void test04() { + assertEquals(-9, new CloseToZero().close_to_zero(-10)); + } +} diff --git a/src/main/java/jp/kusumotolab/kgenprog/CUILauncher.java b/src/main/java/jp/kusumotolab/kgenprog/CUILauncher.java index 18b54b78b..171109014 100644 --- a/src/main/java/jp/kusumotolab/kgenprog/CUILauncher.java +++ b/src/main/java/jp/kusumotolab/kgenprog/CUILauncher.java @@ -1,10 +1,10 @@ package jp.kusumotolab.kgenprog; -import java.util.List; import java.util.Random; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Level; +import jp.kusumotolab.kgenprog.KGenProgMain.ExitStatus; import jp.kusumotolab.kgenprog.fl.FaultLocalization; import jp.kusumotolab.kgenprog.ga.codegeneration.DefaultSourceCodeGeneration; import jp.kusumotolab.kgenprog.ga.codegeneration.SourceCodeGeneration; @@ -19,7 +19,6 @@ import jp.kusumotolab.kgenprog.ga.selection.VariantSelection; import jp.kusumotolab.kgenprog.ga.validation.DefaultCodeValidation; import jp.kusumotolab.kgenprog.ga.validation.SourceCodeValidation; -import jp.kusumotolab.kgenprog.ga.variant.Variant; import jp.kusumotolab.kgenprog.output.Exporters; import jp.kusumotolab.kgenprog.project.test.LocalTestExecutor; import jp.kusumotolab.kgenprog.project.test.TestExecutor; @@ -38,7 +37,7 @@ public static void main(final String[] args) { } } - public List launch(final Configuration config) { + public ExitStatus launch(final Configuration config) { setLogLevel(config.getLogLevel()); final FaultLocalization faultLocalization = config.getFaultLocalization() diff --git a/src/main/java/jp/kusumotolab/kgenprog/Configuration.java b/src/main/java/jp/kusumotolab/kgenprog/Configuration.java index 2aad944b9..a2412f9dd 100644 --- a/src/main/java/jp/kusumotolab/kgenprog/Configuration.java +++ b/src/main/java/jp/kusumotolab/kgenprog/Configuration.java @@ -22,6 +22,7 @@ import org.kohsuke.args4j.spi.StringArrayOptionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.electronwill.nightconfig.core.UnmodifiableConfig.Entry; import com.electronwill.nightconfig.core.conversion.Conversion; import com.electronwill.nightconfig.core.conversion.Converter; import com.electronwill.nightconfig.core.conversion.InvalidValueException; @@ -334,14 +335,12 @@ private Builder() { testPaths = new ArrayList<>(); } - public static Configuration buildFromCmdLineArgs(final String[] args) - throws IllegalArgumentException { + public static Configuration buildFromCmdLineArgs(final String[] args) { final Builder builder = createFromCmdLineArgs(args); return builder.build(); } - public static Builder createFromCmdLineArgs(final String[] args) - throws IllegalArgumentException { + public static Builder createFromCmdLineArgs(final String[] args) { final Builder builder = new Builder(); final CmdLineParser parser = new CmdLineParser(builder); @@ -503,19 +502,19 @@ public Builder setHistoryRecord(final boolean historyRecord) { return this; } - private static void validateArgument(final Builder builder) throws IllegalArgumentException { + private static void validateArgument(final Builder builder) { validateExistences(builder); validateCurrentDir(builder); } - private static void validateExistences(final Builder builder) throws IllegalArgumentException { + private static void validateExistences(final Builder builder) { validateExistence(builder.rootDir); builder.productPaths.forEach(Builder::validateExistence); builder.testPaths.forEach(Builder::validateExistence); builder.classPaths.forEach(Builder::validateExistence); } - private static void validateExistence(final Path path) throws IllegalArgumentException { + private static void validateExistence(final Path path) { if (Files.notExists(path)) { log.error(path.toString() + " does not exist."); throw new IllegalArgumentException(path.toString() + " does not exist."); @@ -549,7 +548,7 @@ private static boolean needsParseConfigFile(final String[] args) { .contains("--config"); } - private void parseConfigFile() throws InvalidValueException, NoSuchFileException { + private void parseConfigFile() throws NoSuchFileException { try (final FileConfig config = loadConfig()) { final ObjectConverter converter = new ObjectConverter(); converter.toObject(config, this); @@ -563,7 +562,7 @@ private void findOptionsSetInConfigFile() throws NoSuchFileException { // 設定ファイルに記述されているオプション一覧を取得 final Set optionNames = config.entrySet() .stream() - .map(o -> o.getKey()) + .map(Entry::getKey) .collect(Collectors.toSet()); final Class clazz = this.getClass(); diff --git a/src/main/java/jp/kusumotolab/kgenprog/KGenProgMain.java b/src/main/java/jp/kusumotolab/kgenprog/KGenProgMain.java index 07bed5d7f..46aed7c8d 100644 --- a/src/main/java/jp/kusumotolab/kgenprog/KGenProgMain.java +++ b/src/main/java/jp/kusumotolab/kgenprog/KGenProgMain.java @@ -20,6 +20,7 @@ import jp.kusumotolab.kgenprog.ga.variant.VariantStore; import jp.kusumotolab.kgenprog.output.Exporters; import jp.kusumotolab.kgenprog.project.jdt.JDTASTConstruction; +import jp.kusumotolab.kgenprog.project.test.EmptyTestResults; import jp.kusumotolab.kgenprog.project.test.TestExecutor; import jp.kusumotolab.kgenprog.project.test.TestResult; import jp.kusumotolab.kgenprog.project.test.TestResults; @@ -33,18 +34,15 @@ */ public class KGenProgMain { - private static Logger log = LoggerFactory.getLogger(KGenProgMain.class); + private final static Logger log = LoggerFactory.getLogger(KGenProgMain.class); + private final LogWriter logwriter = new LogWriter(); private final Configuration config; - private final FaultLocalization faultLocalization; private final Mutation mutation; private final Crossover crossover; private final SourceCodeGeneration sourceCodeGeneration; - private final SourceCodeValidation sourceCodeValidation; - private final VariantSelection variantSelection; - private final TestExecutor testExecutor; private final Exporters exporters; - private final JDTASTConstruction astConstruction; + private final Strategies strategies; /** * コンストラクタ.自動プログラム修正に必要な全ての情報を渡す必要あり. @@ -59,55 +57,79 @@ public class KGenProgMain { * @param testExecutor テスト実行を行うインスタンス * @param exporters 出力処理を行うインスタンス */ - public KGenProgMain(final Configuration config, final FaultLocalization faultLocalization, - final Mutation mutation, final Crossover crossover, + public KGenProgMain( + final Configuration config, + final FaultLocalization faultLocalization, + final Mutation mutation, + final Crossover crossover, final SourceCodeGeneration sourceCodeGeneration, - final SourceCodeValidation sourceCodeValidation, final VariantSelection variantSelection, - final TestExecutor testExecutor, final Exporters exporters) { - + final SourceCodeValidation sourceCodeValidation, + final VariantSelection variantSelection, + final TestExecutor testExecutor, + final Exporters exporters) { this.config = config; - this.faultLocalization = faultLocalization; this.mutation = mutation; this.crossover = crossover; this.sourceCodeGeneration = sourceCodeGeneration; - this.sourceCodeValidation = sourceCodeValidation; - this.variantSelection = variantSelection; - this.testExecutor = testExecutor; - this.astConstruction = new JDTASTConstruction(); this.exporters = exporters; + this.strategies = new Strategies(faultLocalization, new JDTASTConstruction(), + sourceCodeGeneration, sourceCodeValidation, testExecutor, variantSelection); } /** * 自動プログラム修正を実行する.
* 得られた解(全てのテストケースを通過するプログラム)を返す.
- * - * @return 得られた解(全てのテストケースを通過するプログラム) */ - public List run() throws RuntimeException { - logConfig(); + public ExitStatus run() { + logwriter.logConfig(); - testExecutor.initialize(); + if (!config.getTargetProject() + .isValid()) { + log.error("No such project directory."); + return ExitStatus.FAILURE_INVALID_PROJECT; + } - final Strategies strategies = new Strategies(faultLocalization, astConstruction, - sourceCodeGeneration, sourceCodeValidation, testExecutor, variantSelection); final VariantStore variantStore = new VariantStore(config, strategies); final Variant initialVariant = variantStore.getInitialVariant(); - logInitialFailedTests(initialVariant.getTestResults()); + if (!initialVariant.isBuildSucceeded()) { + log.error("Failed to build the specified project."); + log.error(System.lineSeparator()); // keep empty line to show build failure causes + log.error(((EmptyTestResults) initialVariant.getTestResults()).getCause()); + return ExitStatus.FAILURE_INITIAL_BUILD; + } + + if (initialVariant.isCompleted()) { + log.error("No bugs to be repaired. All tests passed."); + return ExitStatus.FAILURE_NO_BUGS; + } + + logwriter.logInitialFailedTests(initialVariant.getTestResults()); - mutation.setCandidates(initialVariant.getGeneratedSourceCode() - .getProductAsts()); + mutation.setInitialCandidates(initialVariant); sourceCodeGeneration.initialize(initialVariant); final StopWatch stopwatch = new StopWatch(config.getTimeLimitSeconds()); stopwatch.start(); - ExitStatus exitStatus; + // GAのメインループ + log.info("GA started"); + final ExitStatus exitStatus = execGALoop(variantStore, stopwatch); + log.info("GA stopped"); - while (true) { + exporters.exportAll(variantStore); + stopwatch.unsplit(); + logwriter.logGAStopped(variantStore, stopwatch, exitStatus); + + return exitStatus; + } + + + private ExitStatus execGALoop(final VariantStore variantStore, final StopWatch stopwatch) { + while (true) { // 新しい世代に入ったことをログ出力 - logGeneration(variantStore.getGenerationNumber()); + logwriter.logGeneration(variantStore.getGenerationNumber()); // 変異プログラムを生成 final List variantsByMutation = mutation.exec(variantStore); @@ -116,45 +138,31 @@ public List run() throws RuntimeException { variantStore.addGeneratedVariants(variantsByCrossover); // 世代別サマリの出力 - logGenerationSummary(stopwatch.toString(), variantsByMutation, variantsByCrossover); + logwriter.logGenerationSummary(stopwatch.toString(), variantsByMutation, variantsByCrossover); stopwatch.split(); + variantStore.updateVariantCounts( Stream.concat(variantsByMutation.stream(), variantsByCrossover.stream()) .collect(Collectors.toList())); // しきい値以上の completedVariants が生成された場合は,GA を抜ける if (areEnoughCompletedVariants(variantStore.getFoundSolutions())) { - exitStatus = ExitStatus.SUCCESS; - break; + return ExitStatus.SUCCESS; } // 制限時間に達した場合には GA を抜ける if (stopwatch.isTimeout()) { - exitStatus = ExitStatus.FAILURE_TIME_LIMIT; - break; + return ExitStatus.FAILURE_TIME_LIMIT; } // 最大世代数に到達した場合には GA を抜ける if (reachedMaxGeneration(variantStore.getGenerationNumber())) { - exitStatus = ExitStatus.FAILURE_MAXIMUM_GENERATION; - break; + return ExitStatus.FAILURE_MAXIMUM_GENERATION; } // 次世代に向けての準備 variantStore.proceedNextGeneration(); } - - log.info("GA stopped."); - // 出力処理を行う - exporters.exportAll(variantStore); - - stopwatch.unsplit(); - strategies.finish(); - logGAStopped(variantStore.getGenerationNumber(), variantStore.getVariantCount(), - variantStore.getSyntaxValidVariantCount(), variantStore.getBuildSuccessVariantCount(), - stopwatch.toString(), exitStatus); - - return variantStore.getFoundSolutions(config.getRequiredSolutionsCount()); } private boolean reachedMaxGeneration(final OrdinalNumber generation) { @@ -165,174 +173,174 @@ private boolean areEnoughCompletedVariants(final List completedVariants return config.getRequiredSolutionsCount() <= completedVariants.size(); } - private void logConfig() { - final StringBuilder sb = new StringBuilder(); - sb// - .append(System.lineSeparator()) - .append("==================== kGenProg Configuration ====================") - .append(System.lineSeparator()) - .append(config.toString()) - .append("================================================================"); - log.info(sb.toString()); - } + /** + * kGenProgMainクラスの終了状態 + */ + enum ExitStatus { + SUCCESS("SUCCESS"), + FAILURE_MAXIMUM_GENERATION("FAILURE (maximum generation)"), + FAILURE_TIME_LIMIT("FAILURE (time limit)"), + FAILURE_INITIAL_BUILD("FAILURE (initial build failed)"), + FAILURE_INVALID_PROJECT("FAILURE (invalid project)"), + FAILURE_NO_BUGS("FAILURE (no bugs to be repaired)"); - private void logInitialFailedTests(final TestResults testResults) { - final StringBuilder sb = new StringBuilder(); - final List succeededTestResults = testResults.getSucceededTestResults(); - final List failedTestResults = testResults.getFailedTestResults(); - sb// - .append("initial failed tests (") - .append(failedTestResults.size()) - .append("/") - .append(succeededTestResults.size() + failedTestResults.size()) - .append(")") - .append(System.lineSeparator()); - - for (TestResult testResult : testResults.getFailedTestResults()) { - sb// - .append(testResult.executedTestFQN) - .append(": ") - .append(testResult.getFailedReason()) - .append(System.lineSeparator()); + private final String code; + + ExitStatus(String code) { + this.code = code; } - log.info(sb.toString()); - } - private void logGeneration(final OrdinalNumber generation) { - final StringBuilder sb = new StringBuilder(); - sb// - .append("entered the era of ") - .append(generation.toString()) - .append(" generation."); - log.info(sb.toString()); + String getCode() { + return code; + } } - private void logGenerationSummary(final String timeText, final List variantsByMutation, - final List variantsByCrossover) { - final List variants = new ArrayList<>(); - variants.addAll(variantsByMutation); - variants.addAll(variantsByCrossover); - final StringBuilder sb = new StringBuilder(); - final DecimalFormat df = createDecimalFormat(); - sb// - .append(System.lineSeparator()) - .append("----------------------------------------------------------------") - .append(System.lineSeparator()) - .append("Elapsed time: ") - .append(timeText) - .append(System.lineSeparator()) - .append("Variants: generated ") - .append(variantsByMutation.size() + variantsByCrossover.size()) - .append(", build-succeeded ") - .append(count(variants, Variant::isBuildSucceeded)) - .append(", build-failed ") - .append(count(variants, v -> v.triedBuild() && !v.isBuildSucceeded())) - .append(", syntax-invalid ") - .append(count(variants, v -> !v.isSyntaxValid() && !v.isReproduced())) - .append(", redundant ") - .append(count(variants, Variant::isReproduced)) - .append(System.lineSeparator()) - .append("Fitness: max ") - .append(getMaxText(variants)) - .append(", min ") - .append(getMinText(variants)) - .append(", ave ") - .append(df.format(getAverage(variants))) - .append(System.lineSeparator()) - .append("----------------------------------------------------------------") - .append(System.lineSeparator()); - log.info(sb.toString()); - } + /** + * Log出力周りの管理クラス + */ + private class LogWriter { + + private final DecimalFormat format = new DecimalFormat("#.###"); + + private void logConfig() { + final StringBuilder sb = new StringBuilder() + .append(System.lineSeparator()) + .append("==================== kGenProg Configuration ====================") + .append(System.lineSeparator()) + .append(config.toString()) + .append("================================================================"); + log.info(sb.toString()); + } - private int count(final List variants, final Predicate p) { - return (int) variants.stream() - .filter(p) - .count(); - } + private void logGeneration(final OrdinalNumber generation) { + log.info("entered the era of {} generation.", generation); + } + + private void logInitialFailedTests(final TestResults testResults) { + final List succeededResults = testResults.getSucceededTestResults(); + final List failedResults = testResults.getFailedTestResults(); - private String getMaxText(final List variants) { - final Map frequencies = getFrequencies(variants); - if (frequencies.isEmpty()) { - return "--"; + final StringBuilder sb = new StringBuilder() + .append(String.format("initial failed tests (%d/%d)", + failedResults.size(), + succeededResults.size() + failedResults.size())) + .append(System.lineSeparator()); + testResults.getFailedTestResults() + .forEach(r -> sb.append(String.format("%s: %s", r.executedTestFQN, r.getFailedReason()))); + log.info(sb.toString()); } - final Map.Entry max = - Collections.max(frequencies.entrySet(), Map.Entry.comparingByKey()); - final DecimalFormat df = createDecimalFormat(); - return df.format(max.getKey()) + "(" + max.getValue() + ")"; - } - private String getMinText(final List variants) { - final Map frequencies = getFrequencies(variants); - if (frequencies.isEmpty()) { - return "--"; + private void logGenerationSummary(final String timeText, final List variantsByMutation, + final List variantsByCrossover) { + final List variants = new ArrayList<>(); + variants.addAll(variantsByMutation); + variants.addAll(variantsByCrossover); + final StringBuilder sb = new StringBuilder() + .append(System.lineSeparator()) + .append("----------------------------------------------------------------") + .append(System.lineSeparator()) + .append(String.format("Elapsed time: %s", timeText)) + .append(System.lineSeparator()) + .append(createVariantsSummary(variants)) + .append(System.lineSeparator()) + .append(createFitnessSummary(variants)) + .append(System.lineSeparator()) + .append("----------------------------------------------------------------") + .append(System.lineSeparator()); + log.info(sb.toString()); } - final Map.Entry min = - Collections.min(frequencies.entrySet(), Map.Entry.comparingByKey()); - final DecimalFormat df = createDecimalFormat(); - return df.format(min.getKey()) + "(" + min.getValue() + ")"; - } - private double getAverage(final List variants) { - return variants.stream() - .filter(Variant::isBuildSucceeded) - .mapToDouble(this::getNormalizedFitnessValue) - .average() - .orElse(Double.NaN); - } + private String createFitnessSummary(final List variants) { + return String.format("Fitness: max %s, min %s, ave %s", + getMaxText(variants), + getMinText(variants), + format.format(getAverage(variants))); + } - private Map getFrequencies(final List variants) { - return variants.stream() - .filter(Variant::isBuildSucceeded) - .collect(Collectors.groupingBy(this::getNormalizedFitnessValue, Collectors.counting())); - } + private String createVariantsSummary(final List variants) { + return String.format( + "Variants: generated %d, build-succeeded %d, build-failed %d, syntax-invalid %d, redundant %d", + count(variants, v -> true), + count(variants, Variant::isBuildSucceeded), + count(variants, v -> v.triedBuild() && !v.isBuildSucceeded()), + count(variants, v -> !v.isSyntaxValid() && !v.isReproduced()), + count(variants, Variant::isReproduced)); + } - private double getNormalizedFitnessValue(final Variant variant) { - return variant.getFitness() - .getNormalizedValue(); - } + private int count(final List variants, final Predicate p) { + return (int) variants.stream() + .filter(p) + .count(); + } - private DecimalFormat createDecimalFormat() { - return new DecimalFormat("#.###"); - } + private String getMaxText(final List variants) { + final Map frequencies = getFrequencies(variants); + if (frequencies.isEmpty()) { + return "--"; + } + final Map.Entry max = + Collections.max(frequencies.entrySet(), Map.Entry.comparingByKey()); + return format.format(max.getKey()) + "(" + max.getValue() + ")"; + } - private void logGAStopped(final OrdinalNumber generation, final int variantCount, - final int syntaxValidCount, final int buildSuccessCount, final String time, - final ExitStatus exitStatus) { - final StringBuilder sb = new StringBuilder(); - sb// - .append("Summary") - .append(System.lineSeparator()) - .append("Reached generation = ") - .append(generation.intValue()) - .append(System.lineSeparator()) - .append("Generated variants = ") - .append(variantCount) - .append(System.lineSeparator()) - .append("Syntax valid variants = ") - .append(syntaxValidCount) - .append(System.lineSeparator()) - .append("Build succeeded variants = ") - .append(buildSuccessCount) - .append(System.lineSeparator()) - .append("Time elapsed = ") - .append(time) - .append(System.lineSeparator()) - .append("Exit status = ") - .append(exitStatus.getCode()); - log.info(sb.toString()); - } + private String getMinText(final List variants) { + final Map frequencies = getFrequencies(variants); + if (frequencies.isEmpty()) { + return "--"; + } + final Map.Entry min = + Collections.min(frequencies.entrySet(), Map.Entry.comparingByKey()); + return format.format(min.getKey()) + "(" + min.getValue() + ")"; + } - private enum ExitStatus { - SUCCESS("SUCCESS"), FAILURE_MAXIMUM_GENERATION( - "FAILURE (maximum generation)"), FAILURE_TIME_LIMIT("FAILURE (time limit)"); - private final String code; + private double getAverage(final List variants) { + return variants.stream() + .filter(Variant::isBuildSucceeded) + .mapToDouble(this::getNormalizedFitnessValue) + .average() + .orElse(Double.NaN); + } - ExitStatus(String code) { - this.code = code; + private Map getFrequencies(final List variants) { + return variants.stream() + .filter(Variant::isBuildSucceeded) + .collect(Collectors.groupingBy(this::getNormalizedFitnessValue, Collectors.counting())); } - String getCode() { - return code; + private double getNormalizedFitnessValue(final Variant variant) { + return variant.getFitness() + .getNormalizedValue(); + } + + private void logGAStopped(final VariantStore variantStore, final StopWatch stopwatch, + final ExitStatus exitStatus) { + logGAStopped(variantStore.getGenerationNumber(), + variantStore.getVariantCount(), + variantStore.getSyntaxValidVariantCount(), + variantStore.getBuildSuccessVariantCount(), + stopwatch.toString(), + exitStatus); + } + + private void logGAStopped(final OrdinalNumber generation, final int variantCount, + final int syntaxValidCount, final int buildSuccessCount, final String time, + final ExitStatus exitStatus) { + final StringBuilder sb = new StringBuilder() + .append("Summary") + .append(System.lineSeparator()) + .append(String.format("Reached generation = %d", generation.intValue())) + .append(System.lineSeparator()) + .append(String.format("Generated variants = %d", variantCount)) + .append(System.lineSeparator()) + .append(String.format("Syntax valid variants = %d", syntaxValidCount)) + .append(System.lineSeparator()) + .append(String.format("Build succeeded variants = %d", buildSuccessCount)) + .append(System.lineSeparator()) + .append(String.format("Time elapsed = %s", time)) + .append(System.lineSeparator()) + .append(String.format("Exit status = %s", exitStatus.getCode())); + log.info(sb.toString()); } } + } diff --git a/src/main/java/jp/kusumotolab/kgenprog/Strategies.java b/src/main/java/jp/kusumotolab/kgenprog/Strategies.java index 48320e410..5a522d012 100644 --- a/src/main/java/jp/kusumotolab/kgenprog/Strategies.java +++ b/src/main/java/jp/kusumotolab/kgenprog/Strategies.java @@ -54,14 +54,6 @@ public Strategies(final FaultLocalization faultLocalization, this.variantSelection = variantSelection; } - /** - * 自動プログラム修正を終える際に呼び出す必要があるメソッド.
- * {@link KGenProgMain#run()}内で呼び出されている.
- */ - public void finish() { - testExecutor.finish(); - } - /** * 自動バグ限局を実行するメソッド.
* 限局対象のソースコードとテストの実行結果を受け取り,自動バグ限局の実行結果を返す.
diff --git a/src/main/java/jp/kusumotolab/kgenprog/ga/mutation/Mutation.java b/src/main/java/jp/kusumotolab/kgenprog/ga/mutation/Mutation.java index 85ffd77f2..a4bf5a46f 100644 --- a/src/main/java/jp/kusumotolab/kgenprog/ga/mutation/Mutation.java +++ b/src/main/java/jp/kusumotolab/kgenprog/ga/mutation/Mutation.java @@ -49,6 +49,14 @@ public void setCandidates(final List> candidates candidateSelection.setCandidates(candidates); } + /** + * @param variant 再利用するソースコード群を含初期バリアント + */ + public void setInitialCandidates(final Variant variant) { + candidateSelection.setCandidates(variant.getGeneratedSourceCode() + .getProductAsts()); + } + /** * 変異処理された Variant を mutationGeneratingCount 分だけ返す * diff --git a/src/main/java/jp/kusumotolab/kgenprog/project/factory/TargetProject.java b/src/main/java/jp/kusumotolab/kgenprog/project/factory/TargetProject.java index 18d7b1f0a..f8055a008 100644 --- a/src/main/java/jp/kusumotolab/kgenprog/project/factory/TargetProject.java +++ b/src/main/java/jp/kusumotolab/kgenprog/project/factory/TargetProject.java @@ -1,5 +1,6 @@ package jp.kusumotolab.kgenprog.project.factory; +import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Objects; @@ -39,6 +40,11 @@ public void setTestSourcePaths(final List testSourcePaths) { this.testSourcePaths = testSourcePaths; } + public boolean isValid() { + // TODO: other validations + return Files.isDirectory(rootPath); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/jp/kusumotolab/kgenprog/project/test/EmptyTestResults.java b/src/main/java/jp/kusumotolab/kgenprog/project/test/EmptyTestResults.java index 88805fc0b..155d1e8ed 100644 --- a/src/main/java/jp/kusumotolab/kgenprog/project/test/EmptyTestResults.java +++ b/src/main/java/jp/kusumotolab/kgenprog/project/test/EmptyTestResults.java @@ -2,6 +2,7 @@ import jp.kusumotolab.kgenprog.project.ASTLocation; import jp.kusumotolab.kgenprog.project.ProductSourcePath; +import jp.kusumotolab.kgenprog.project.build.BuildResults; /** * テスト失敗時のテスト結果.
@@ -13,12 +14,12 @@ public class EmptyTestResults extends TestResults { private final String cause; - /** - * @deprecated - */ - @Deprecated - public EmptyTestResults() { - this(""); + public EmptyTestResults(final BuildResults buildResults) { + super(buildResults); + this.cause = buildResults.diagnostics.getDiagnostics() + .stream() + .map(d -> d.getMessage(null)) + .reduce("", String::concat); } public EmptyTestResults(final String cause) { diff --git a/src/main/java/jp/kusumotolab/kgenprog/project/test/TestExecutor.java b/src/main/java/jp/kusumotolab/kgenprog/project/test/TestExecutor.java index 1d46d4d08..e2ff47697 100644 --- a/src/main/java/jp/kusumotolab/kgenprog/project/test/TestExecutor.java +++ b/src/main/java/jp/kusumotolab/kgenprog/project/test/TestExecutor.java @@ -22,9 +22,4 @@ default Single execAsync(final Single variantSingle) { return variantSingle.map(this::exec); } - default void initialize() { - } - - default void finish() { - } } diff --git a/src/main/java/jp/kusumotolab/kgenprog/project/test/TestThread.java b/src/main/java/jp/kusumotolab/kgenprog/project/test/TestThread.java index 6e4704d04..f1708009e 100644 --- a/src/main/java/jp/kusumotolab/kgenprog/project/test/TestThread.java +++ b/src/main/java/jp/kusumotolab/kgenprog/project/test/TestThread.java @@ -102,7 +102,7 @@ public TestResults getTestResults() { public void run() { // ビルド失敗時は即座に諦める if (buildResults.isBuildFailed) { - testResults = new EmptyTestResults("build failed."); + testResults = new EmptyTestResults(buildResults); return; } diff --git a/src/test/java/jp/kusumotolab/kgenprog/KGenProgMainTest.java b/src/test/java/jp/kusumotolab/kgenprog/KGenProgMainTest.java index 5ba7136e8..4fe13cf49 100644 --- a/src/test/java/jp/kusumotolab/kgenprog/KGenProgMainTest.java +++ b/src/test/java/jp/kusumotolab/kgenprog/KGenProgMainTest.java @@ -1,14 +1,15 @@ package jp.kusumotolab.kgenprog; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import jp.kusumotolab.kgenprog.ga.variant.Variant; +import jp.kusumotolab.kgenprog.KGenProgMain.ExitStatus; public class KGenProgMainTest { @@ -21,11 +22,11 @@ public class KGenProgMainTest { /* * 引数で与えられた情報を利用して,CUILauncher経由でkGenProgMainを実行するメソッド */ - private List runKGenProgMain(final Path rootPath, final Path productPath, + private ExitStatus runKGenProgMain(final Path rootPath, final Path productPath, final Path testPath) { - final List productPaths = Arrays.asList(productPath); - final List testPaths = Arrays.asList(testPath); + final List productPaths = Collections.singletonList(productPath); + final List testPaths = Collections.singletonList(testPath); final Path outDir = tempFolder.getRoot() .toPath(); @@ -115,4 +116,37 @@ public void testQuickSort01() { assertThatCode(() -> runKGenProgMain(rootPath, productPath, testPath)). doesNotThrowAnyException(); } + + + @Test + public void testBuildFailure() { + final Path rootPath = Paths.get("example/Abnormals/BuildFailure"); + final Path productPath = rootPath.resolve(PRODUCT_NAME); + final Path testPath = rootPath.resolve(TEST_NAME); + + final ExitStatus status = runKGenProgMain(rootPath, productPath, testPath); + assertThat(status).isEqualTo(ExitStatus.FAILURE_INITIAL_BUILD); + + } + + @Test + public void testNoBugs() { + final Path rootPath = Paths.get("example/Abnormals/NoBugs"); + final Path productPath = rootPath.resolve(PRODUCT_NAME); + final Path testPath = rootPath.resolve(TEST_NAME); + + final ExitStatus status = runKGenProgMain(rootPath, productPath, testPath); + assertThat(status).isEqualTo(ExitStatus.FAILURE_NO_BUGS); + } + + @Test + public void testMissingRootDir() { + final Path rootPath = Paths.get("no-such-project-dir"); + final Path productPath = rootPath.resolve(PRODUCT_NAME); + final Path testPath = rootPath.resolve(TEST_NAME); + + final ExitStatus status = runKGenProgMain(rootPath, productPath, testPath); + assertThat(status).isEqualTo(ExitStatus.FAILURE_INVALID_PROJECT); + } + }