From fd6a48d020dc92e3d3ad70248addbc9ad7b29cfa Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Fri, 5 May 2017 23:13:17 -0700 Subject: [PATCH 01/16] Initial design for Build A/B testing epic story Perhaps this design of the workflow / task mappings is a reasonable introduction to... more design, thinking and maybe implementing :) --- .../internal/gradle/BuildABTestingPlugin.java | 131 ++++++++++++++++++ .../gradle/BuildABTestingPluginTest.groovy | 14 ++ 2 files changed, 145 insertions(+) create mode 100644 src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java create mode 100644 src/test/groovy/org/mockito/release/internal/gradle/BuildABTestingPluginTest.groovy diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java new file mode 100644 index 00000000..06374c82 --- /dev/null +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -0,0 +1,131 @@ +package org.mockito.release.internal.gradle; + +import org.gradle.api.DefaultTask; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.*; + +import java.io.File; + +public class BuildABTestingPlugin implements Plugin { + + public void apply(final Project project) { + //Step 1. Clone the repo for A/B testing + CloneGitHubRepositoryTask clone = project.getTasks().create("cloneGitHubRepository", CloneGitHubRepositoryTask.class); + clone.setRepository("mockito/mockito"); + clone.setTargetDir(new File(project.getBuildDir(), "abTesting/mockito/pristine")); + + //Step 2. Run A test + RunTestTask runA = project.getTasks().create("runA", RunTestTask.class); + runA.dependsOn(clone); + runA.setSourceDir(clone.getTargetDir()); + runA.setWorkDir(new File(project.getBuildDir(), "abTesting/mockito/testA-" + System.currentTimeMillis())); + runA.commandLine("./gradlew", "build"); + runA.setOutputDir(new File(runA.getWorkDir(), "build")); + + //Step 3. Run B test + RunTestTask runB = project.getTasks().create("runB", RunTestTask.class); + runA.dependsOn(clone); + runA.setSourceDir(clone.getTargetDir()); + runA.setWorkDir(new File(project.getBuildDir(), "abTesting/mockito/testB-" + System.currentTimeMillis())); + runA.commandLine("./gradlew", "build"); + runA.setOutputDir(new File(runA.getWorkDir(), "build")); + + //Step 4. Compare test outcomes + CompareDirectoriesTask compare = project.getTasks().create("compareAB", CompareDirectoriesTask.class); + compare.dependsOn(runA, runB); + compare.setDirA(runA.getOutputDir()); + compare.setDirB(runB.getOutputDir()); + compare.setResultsFile(new File(project.getBuildDir(), "abTesting/mockito/results.ser")); //or JSON :) + + //Step 5. Analyze comparison results + AnalyzeComparisonResultsTask analyze = project.getTasks().create("analyzeAB", AnalyzeComparisonResultsTask.class); + analyze.dependsOn(compare); + analyze.setComparisonResultsFile(compare.getResultsFile()); + } + + public static class AnalyzeComparisonResultsTask extends DefaultTask { + + @InputFile + public void setComparisonResultsFile(File comparisonResultsFile) { + } + } + + public static class CompareDirectoriesTask extends DefaultTask { + + @InputDirectory + public void setDirA(File dir) { + + } + + @InputDirectory + public void setDirB(File dir) { + + } + + public void setResultsFile(File file) { + + } + + @OutputFile + public File getResultsFile() { + return null; + } + } + + public static class RunTestTask extends DefaultTask { + + @InputDirectory + public void setSourceDir(File sourceDir) { + + } + + public void setWorkDir(File workDir) { + + } + + @Input + public void commandLine(String ... arg) { + } + + public File getWorkDir() { + return null; + } + + @OutputDirectory + public void setOutputDir(File outputDir) { + + } + + public File getOutputDir() { + return null; + } + } + + public static class CompareABTask extends DefaultTask { + } + + public static class CloneGitHubRepositoryTask extends DefaultTask { + + @Input + public void setRepository(String s) { + } + + public void setTargetDir(File targetDir) { + + } + + @OutputDirectory + public File getTargetDir() { + return null; + } + } + + public static class PrepareABTestingTask extends DefaultTask { + + @InputDirectory + public void setSourceDir(File sourceDir) { + + } + } +} diff --git a/src/test/groovy/org/mockito/release/internal/gradle/BuildABTestingPluginTest.groovy b/src/test/groovy/org/mockito/release/internal/gradle/BuildABTestingPluginTest.groovy new file mode 100644 index 00000000..80bf8c02 --- /dev/null +++ b/src/test/groovy/org/mockito/release/internal/gradle/BuildABTestingPluginTest.groovy @@ -0,0 +1,14 @@ +package org.mockito.release.internal.gradle + +import org.gradle.testfixtures.ProjectBuilder +import spock.lang.Specification + +class BuildABTestingPluginTest extends Specification { + + def project = new ProjectBuilder().build() + + def "applies"() { + expect: + project.plugins.apply(BuildABTestingPlugin.class); + } +} From e49aac1453f23ce6af60c5bf6681be5cd035051c Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Fri, 5 May 2017 23:18:30 -0700 Subject: [PATCH 02/16] Added documentation! --- .../release/internal/gradle/BuildABTestingPlugin.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index 06374c82..52b00e99 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -7,6 +7,9 @@ import java.io.File; +/** + * See rationale and design at: https://github.com/mockito/mockito-release-tools/issues/113 + */ public class BuildABTestingPlugin implements Plugin { public void apply(final Project project) { @@ -44,6 +47,11 @@ public void apply(final Project project) { analyze.setComparisonResultsFile(compare.getResultsFile()); } + /** + * BELOW task types are only empty shells. + * They act as suggestions for workflow / API design. + */ + public static class AnalyzeComparisonResultsTask extends DefaultTask { @InputFile From abded4f5d83ae8a4a4914f8aebe3a57c7134d046 Mon Sep 17 00:00:00 2001 From: epeee Date: Sun, 14 May 2017 20:45:23 +0200 Subject: [PATCH 03/16] step 1: git clone --- .../internal/gradle/BuildABTestingPlugin.java | 60 +++++++++++++++---- ...-release-tools.build-ab-testing.properties | 1 + 2 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 src/main/resources/META-INF/gradle-plugins/org.mockito.mockito-release-tools.build-ab-testing.properties diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index 52b00e99..ad8ef3b3 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -4,6 +4,7 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.tasks.*; +import org.mockito.release.exec.DefaultProcessRunner; import java.io.File; @@ -54,42 +55,53 @@ public void apply(final Project project) { public static class AnalyzeComparisonResultsTask extends DefaultTask { + private File comparisonResultsFile; + @InputFile public void setComparisonResultsFile(File comparisonResultsFile) { + this.comparisonResultsFile = comparisonResultsFile; } } public static class CompareDirectoriesTask extends DefaultTask { + private File dirA; + private File dirB; + private File resultsFile; + @InputDirectory public void setDirA(File dir) { - + this.dirA = dir; } @InputDirectory public void setDirB(File dir) { - + this.dirB = dir; } public void setResultsFile(File file) { - + this.resultsFile = file; } @OutputFile public File getResultsFile() { - return null; + return resultsFile; } } public static class RunTestTask extends DefaultTask { + private File sourceDir; + private File workDir; + private File outputDir; + @InputDirectory public void setSourceDir(File sourceDir) { - + this.sourceDir = sourceDir; } public void setWorkDir(File workDir) { - + this.workDir = workDir; } @Input @@ -97,16 +109,16 @@ public void commandLine(String ... arg) { } public File getWorkDir() { - return null; + return workDir; } @OutputDirectory public void setOutputDir(File outputDir) { - + this.outputDir = outputDir; } public File getOutputDir() { - return null; + return outputDir; } } @@ -115,25 +127,47 @@ public static class CompareABTask extends DefaultTask { public static class CloneGitHubRepositoryTask extends DefaultTask { + private String repository; + private File targetDir; + private static final String REPO_BASE_URL = "https://github.com/"; + @Input - public void setRepository(String s) { + public void setRepository(String repository) { + this.repository = repository; } public void setTargetDir(File targetDir) { - + this.targetDir = targetDir; } @OutputDirectory public File getTargetDir() { - return null; + return targetDir; + } + + @TaskAction + public void cloneGit() { + if(!targetDir.exists()) { + targetDir.mkdirs(); + } + + if (repository == null || repository.isEmpty()) { + throw new RuntimeException("Invalid repository '" + repository + "' given!"); + } + + String url = REPO_BASE_URL + repository; + + new DefaultProcessRunner(targetDir).run("git", "clone", url); } } public static class PrepareABTestingTask extends DefaultTask { + private File sourceDir; + @InputDirectory public void setSourceDir(File sourceDir) { - + this.sourceDir = sourceDir; } } } diff --git a/src/main/resources/META-INF/gradle-plugins/org.mockito.mockito-release-tools.build-ab-testing.properties b/src/main/resources/META-INF/gradle-plugins/org.mockito.mockito-release-tools.build-ab-testing.properties new file mode 100644 index 00000000..22c7a9bf --- /dev/null +++ b/src/main/resources/META-INF/gradle-plugins/org.mockito.mockito-release-tools.build-ab-testing.properties @@ -0,0 +1 @@ +implementation-class=org.mockito.release.internal.gradle.BuildABTestingPlugin \ No newline at end of file From 2899e4645d2986f6dfd3f8cad74c96c6eb0cde49 Mon Sep 17 00:00:00 2001 From: epeee Date: Mon, 15 May 2017 06:56:35 +0200 Subject: [PATCH 04/16] step 2. Run A test step 3. Run B test --- .../internal/gradle/BuildABTestingPlugin.java | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index ad8ef3b3..b132c10f 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -1,8 +1,10 @@ package org.mockito.release.internal.gradle; +import org.gradle.api.Action; import org.gradle.api.DefaultTask; import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.file.CopySpec; import org.gradle.api.tasks.*; import org.mockito.release.exec.DefaultProcessRunner; @@ -22,18 +24,18 @@ public void apply(final Project project) { //Step 2. Run A test RunTestTask runA = project.getTasks().create("runA", RunTestTask.class); runA.dependsOn(clone); - runA.setSourceDir(clone.getTargetDir()); + runA.setSourceDir(new File(clone.getTargetDir(), "mockito")); runA.setWorkDir(new File(project.getBuildDir(), "abTesting/mockito/testA-" + System.currentTimeMillis())); runA.commandLine("./gradlew", "build"); runA.setOutputDir(new File(runA.getWorkDir(), "build")); //Step 3. Run B test RunTestTask runB = project.getTasks().create("runB", RunTestTask.class); - runA.dependsOn(clone); - runA.setSourceDir(clone.getTargetDir()); - runA.setWorkDir(new File(project.getBuildDir(), "abTesting/mockito/testB-" + System.currentTimeMillis())); - runA.commandLine("./gradlew", "build"); - runA.setOutputDir(new File(runA.getWorkDir(), "build")); + runB.dependsOn(clone); + runB.setSourceDir(new File(clone.getTargetDir(), "mockito")); + runB.setWorkDir(new File(project.getBuildDir(), "abTesting/mockito/testB-" + System.currentTimeMillis())); + runB.commandLine("./gradlew", "build"); + runB.setOutputDir(new File(runB.getWorkDir(), "build")); //Step 4. Compare test outcomes CompareDirectoriesTask compare = project.getTasks().create("compareAB", CompareDirectoriesTask.class); @@ -94,6 +96,7 @@ public static class RunTestTask extends DefaultTask { private File sourceDir; private File workDir; private File outputDir; + private String[] arg; @InputDirectory public void setSourceDir(File sourceDir) { @@ -106,20 +109,34 @@ public void setWorkDir(File workDir) { @Input public void commandLine(String ... arg) { + this.arg = arg; } public File getWorkDir() { return workDir; } - @OutputDirectory public void setOutputDir(File outputDir) { this.outputDir = outputDir; } + @OutputDirectory public File getOutputDir() { return outputDir; } + + @TaskAction + public void executeTestTask() { + if (outputDir != null && !outputDir.exists()) { + outputDir.mkdirs(); + } + getProject().copy(new Action() { + public void execute(CopySpec copy) { + copy.from(sourceDir).into(workDir); + } + }); + new DefaultProcessRunner(workDir).run(arg); + } } public static class CompareABTask extends DefaultTask { @@ -147,7 +164,7 @@ public File getTargetDir() { @TaskAction public void cloneGit() { - if(!targetDir.exists()) { + if(targetDir != null && !targetDir.exists()) { targetDir.mkdirs(); } From 4919fee8b68ec3f17db7dba692c060cbb4c550a1 Mon Sep 17 00:00:00 2001 From: epeee Date: Wed, 17 May 2017 06:43:09 +0200 Subject: [PATCH 05/16] make use of clonegitrepositorytask --- .../release/internal/gradle/BuildABTestingPlugin.java | 2 +- .../release/internal/gradle/CloneGitRepositoryTask.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index b132c10f..db77278b 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -17,7 +17,7 @@ public class BuildABTestingPlugin implements Plugin { public void apply(final Project project) { //Step 1. Clone the repo for A/B testing - CloneGitHubRepositoryTask clone = project.getTasks().create("cloneGitHubRepository", CloneGitHubRepositoryTask.class); + CloneGitRepositoryTask clone = project.getTasks().create("cloneGitHubRepository", CloneGitRepositoryTask.class); clone.setRepository("mockito/mockito"); clone.setTargetDir(new File(project.getBuildDir(), "abTesting/mockito/pristine")); diff --git a/src/main/groovy/org/mockito/release/internal/gradle/CloneGitRepositoryTask.java b/src/main/groovy/org/mockito/release/internal/gradle/CloneGitRepositoryTask.java index 9bed8d28..49be9b1a 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/CloneGitRepositoryTask.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/CloneGitRepositoryTask.java @@ -29,7 +29,12 @@ public class CloneGitRepositoryTask extends DefaultTask { @TaskAction public void cloneRepository() { + if (repository == null || repository.isEmpty()) { + throw new RuntimeException("Invalid repository '" + repository + "' given!"); + } + LOG.lifecycle(" Cloning repository {}\n into {}", repository, targetDir); + getProject().getBuildDir().mkdirs(); // build dir can be not created yet ProcessRunner processRunner = org.mockito.release.exec.Exec.getProcessRunner(getProject().getBuildDir()); processRunner.run("git", "clone", repository, targetDir.getAbsolutePath()); From 376226c99c0639ba9fe19f24f10c21dccf3995d5 Mon Sep 17 00:00:00 2001 From: epeee Date: Thu, 25 May 2017 20:01:45 +0200 Subject: [PATCH 06/16] clean targetDir if it already exists --- .../release/internal/gradle/CloneGitRepositoryTask.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/groovy/org/mockito/release/internal/gradle/CloneGitRepositoryTask.java b/src/main/groovy/org/mockito/release/internal/gradle/CloneGitRepositoryTask.java index 49be9b1a..1190d339 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/CloneGitRepositoryTask.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/CloneGitRepositoryTask.java @@ -35,6 +35,10 @@ public void cloneRepository() { LOG.lifecycle(" Cloning repository {}\n into {}", repository, targetDir); + if (targetDir.exists()) { + getProject().delete(targetDir); + } + getProject().getBuildDir().mkdirs(); // build dir can be not created yet ProcessRunner processRunner = org.mockito.release.exec.Exec.getProcessRunner(getProject().getBuildDir()); processRunner.run("git", "clone", repository, targetDir.getAbsolutePath()); From 9a5634425190f18f03dd6059d037238e1a08a21c Mon Sep 17 00:00:00 2001 From: epeee Date: Thu, 25 May 2017 20:10:14 +0200 Subject: [PATCH 07/16] configure CloneGitRepositoryTask (repository) fix RunTestTask sourceDir add check if sourceDir (RunTestTask) exists --- .../internal/gradle/BuildABTestingPlugin.java | 45 +++---------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index db77278b..f7c38340 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -18,13 +18,13 @@ public class BuildABTestingPlugin implements Plugin { public void apply(final Project project) { //Step 1. Clone the repo for A/B testing CloneGitRepositoryTask clone = project.getTasks().create("cloneGitHubRepository", CloneGitRepositoryTask.class); - clone.setRepository("mockito/mockito"); + clone.setRepository("https://github.com/mockito/mockito"); clone.setTargetDir(new File(project.getBuildDir(), "abTesting/mockito/pristine")); //Step 2. Run A test RunTestTask runA = project.getTasks().create("runA", RunTestTask.class); runA.dependsOn(clone); - runA.setSourceDir(new File(clone.getTargetDir(), "mockito")); + runA.setSourceDir(clone.getTargetDir()); runA.setWorkDir(new File(project.getBuildDir(), "abTesting/mockito/testA-" + System.currentTimeMillis())); runA.commandLine("./gradlew", "build"); runA.setOutputDir(new File(runA.getWorkDir(), "build")); @@ -32,7 +32,7 @@ public void apply(final Project project) { //Step 3. Run B test RunTestTask runB = project.getTasks().create("runB", RunTestTask.class); runB.dependsOn(clone); - runB.setSourceDir(new File(clone.getTargetDir(), "mockito")); + runB.setSourceDir(clone.getTargetDir()); runB.setWorkDir(new File(project.getBuildDir(), "abTesting/mockito/testB-" + System.currentTimeMillis())); runB.commandLine("./gradlew", "build"); runB.setOutputDir(new File(runB.getWorkDir(), "build")); @@ -127,6 +127,9 @@ public File getOutputDir() { @TaskAction public void executeTestTask() { + if (sourceDir == null || !sourceDir.exists()) { + throw new RuntimeException("Invalid source dir '" + sourceDir + "' given!" ); + } if (outputDir != null && !outputDir.exists()) { outputDir.mkdirs(); } @@ -142,42 +145,6 @@ public void execute(CopySpec copy) { public static class CompareABTask extends DefaultTask { } - public static class CloneGitHubRepositoryTask extends DefaultTask { - - private String repository; - private File targetDir; - private static final String REPO_BASE_URL = "https://github.com/"; - - @Input - public void setRepository(String repository) { - this.repository = repository; - } - - public void setTargetDir(File targetDir) { - this.targetDir = targetDir; - } - - @OutputDirectory - public File getTargetDir() { - return targetDir; - } - - @TaskAction - public void cloneGit() { - if(targetDir != null && !targetDir.exists()) { - targetDir.mkdirs(); - } - - if (repository == null || repository.isEmpty()) { - throw new RuntimeException("Invalid repository '" + repository + "' given!"); - } - - String url = REPO_BASE_URL + repository; - - new DefaultProcessRunner(targetDir).run("git", "clone", url); - } - } - public static class PrepareABTestingTask extends DefaultTask { private File sourceDir; From 059429fc87075122f5e3910d0e5213e0d2be21d9 Mon Sep 17 00:00:00 2001 From: epeee Date: Fri, 26 May 2017 21:42:05 +0200 Subject: [PATCH 08/16] step 3: compare directories and serialize result to json file --- .../internal/gradle/BuildABTestingPlugin.java | 165 ++++++++++++++++-- 1 file changed, 153 insertions(+), 12 deletions(-) diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index f7c38340..7f189683 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -5,10 +5,20 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.file.CopySpec; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; import org.gradle.api.tasks.*; +import org.json.simple.Jsonable; +import org.json.simple.Jsoner; import org.mockito.release.exec.DefaultProcessRunner; +import org.mockito.release.notes.util.IOUtil; import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * See rationale and design at: https://github.com/mockito/mockito-release-tools/issues/113 @@ -17,7 +27,7 @@ public class BuildABTestingPlugin implements Plugin { public void apply(final Project project) { //Step 1. Clone the repo for A/B testing - CloneGitRepositoryTask clone = project.getTasks().create("cloneGitHubRepository", CloneGitRepositoryTask.class); + CloneGitRepositoryTask clone = project.getTasks().create("cloneGitRepository", CloneGitRepositoryTask.class); clone.setRepository("https://github.com/mockito/mockito"); clone.setTargetDir(new File(project.getBuildDir(), "abTesting/mockito/pristine")); @@ -26,7 +36,8 @@ public void apply(final Project project) { runA.dependsOn(clone); runA.setSourceDir(clone.getTargetDir()); runA.setWorkDir(new File(project.getBuildDir(), "abTesting/mockito/testA-" + System.currentTimeMillis())); - runA.commandLine("./gradlew", "build"); + // using assemble task for now in order to get different results + runA.commandLine("./gradlew", "assemble"); runA.setOutputDir(new File(runA.getWorkDir(), "build")); //Step 3. Run B test @@ -42,7 +53,7 @@ public void apply(final Project project) { compare.dependsOn(runA, runB); compare.setDirA(runA.getOutputDir()); compare.setDirB(runB.getOutputDir()); - compare.setResultsFile(new File(project.getBuildDir(), "abTesting/mockito/results.ser")); //or JSON :) + compare.setResultsFile(new File(project.getBuildDir(), "abTesting/mockito/results.json")); //or JSON :) //Step 5. Analyze comparison results AnalyzeComparisonResultsTask analyze = project.getTasks().create("analyzeAB", AnalyzeComparisonResultsTask.class); @@ -63,8 +74,16 @@ public static class AnalyzeComparisonResultsTask extends DefaultTask { public void setComparisonResultsFile(File comparisonResultsFile) { this.comparisonResultsFile = comparisonResultsFile; } + + @TaskAction + public void analyze() { + + } } + /** + * A Task which compares two given directories. The result will be serialized to a result file. + */ public static class CompareDirectoriesTask extends DefaultTask { private File dirA; @@ -89,6 +108,137 @@ public void setResultsFile(File file) { public File getResultsFile() { return resultsFile; } + + @TaskAction + public void compareDirectories() { + if (resultsFile.exists()) { + getProject().delete(resultsFile); + } + + CompareResult compareResult = new FileDifferenceProvider().getDifference(dirA, dirB); + IOUtil.writeFile(resultsFile, new CompareResultSerializer().serialize(compareResult)); + } + } + + public static class FileDifferenceProvider { + + public CompareResult getDifference(File dirA, File dirB) { + List dirAFiles = getFilesResursive(dirA.listFiles()); + Collections.sort(dirAFiles); + List dirBFiles = getFilesResursive(dirB.listFiles()); + Collections.sort(dirBFiles); + + List onlyA = new ArrayList(); + List onlyB = new ArrayList(); + List bothButDifferent = new ArrayList(); + + int i = 0; + int j = 0; + + while (dirAFiles.size() > i && dirBFiles.size() > j) { + String dirASubPath = dirAFiles.get(i).getPath().substring(dirA.getPath().length()); + String dirBSubPath = dirBFiles.get(j).getPath().substring(dirB.getPath().length()); + int compareResult = dirASubPath.compareTo(dirBSubPath); + + if (compareResult < 0) { + onlyA.add(dirAFiles.get(i)); + i++; + } else if (compareResult > 0) { + onlyB.add(dirBFiles.get(j)); + j++; + } else { + boolean sameMd5 = true; + // TODO md5 check + + if (dirAFiles.get(i).length() == dirBFiles.get(j).length() && sameMd5) { + // nothing to do, both files are available + } else { + bothButDifferent.add(dirAFiles.get(i)); + bothButDifferent.add(dirBFiles.get(j)); + } + i++; + j++; + } + } + + CompareResult result = new CompareResult(); + result.setOnlyA(onlyA); + result.setOnlyB(onlyB); + result.setBothButDifferent(bothButDifferent); + + return result; + } + + + private List getFilesResursive(File[] files) { + List filesRecursive = new ArrayList(); + for (File file : files) { + filesRecursive.add(file); + if (file.isDirectory()) { + filesRecursive.addAll(getFilesResursive(file.listFiles())); + } + } + return filesRecursive; + } + } + + public static class CompareResult implements Jsonable { + + private static final String JSON_FORMAT = "{ \"onlyA\": \"%s\", \"onlyB\": \"%s\", " + + "\"both\": \"%s\" }"; + + private List onlyA; + private List onlyB; + private List bothButDifferent; + + public void setOnlyA(List file) { + this.onlyA = file; + } + + public void setOnlyB(List file) { + this.onlyB = file; + } + + public void setBothButDifferent(List file) { + this.bothButDifferent = file; + } + + @Override + public String toJson() { + return String.format(JSON_FORMAT, + Jsoner.serialize(toStringList(onlyA)), + Jsoner.serialize(toStringList(onlyB)), + Jsoner.serialize(toStringList(bothButDifferent))); + } + + private List toStringList(List files) { + List ret = new ArrayList(); + for (File file : files) { + ret.add(file.getPath()); + } + return ret; + } + + @Override + public void toJson(Writer writable) throws IOException { + writable.append(toJson()); + } + } + + public static class CompareResultSerializer { + + private static final Logger LOG = Logging.getLogger(CompareResultSerializer.class); + + public String serialize(CompareResult compareResult) { + String json = Jsoner.serialize(compareResult); + LOG.info("Serialize compare result to: {}", json); + return json; + } + + public CompareResult deserialize(String json) { + return null; + } + } public static class RunTestTask extends DefaultTask { @@ -145,13 +295,4 @@ public void execute(CopySpec copy) { public static class CompareABTask extends DefaultTask { } - public static class PrepareABTestingTask extends DefaultTask { - - private File sourceDir; - - @InputDirectory - public void setSourceDir(File sourceDir) { - this.sourceDir = sourceDir; - } - } } From 175a1c01c127f8e1aee42298509f8f8cce3fe5fa Mon Sep 17 00:00:00 2001 From: epeee Date: Sat, 27 May 2017 06:15:25 +0200 Subject: [PATCH 09/16] extract filedifferenceprovider added some tests fixed bug found by tests --- .../file/FileDifferenceProvider.java | 78 +++++++++++++++++++ .../internal/gradle/BuildABTestingPlugin.java | 66 +--------------- .../file/FileDifferenceProviderTest.groovy | 74 ++++++++++++++++++ 3 files changed, 154 insertions(+), 64 deletions(-) create mode 100644 src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java create mode 100644 src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy diff --git a/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java new file mode 100644 index 00000000..1cf4be43 --- /dev/null +++ b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java @@ -0,0 +1,78 @@ +package org.mockito.release.internal.comparison.file; + +import org.mockito.release.internal.gradle.BuildABTestingPlugin; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +public class FileDifferenceProvider { + + public BuildABTestingPlugin.CompareResult getDifference(File dirA, File dirB) { + List dirAFiles = getFilesResursive(dirA.listFiles()); + Collections.sort(dirAFiles); + List dirBFiles = getFilesResursive(dirB.listFiles()); + Collections.sort(dirBFiles); + + List onlyA = new ArrayList(); + List onlyB = new ArrayList(); + List bothButDifferent = new ArrayList(); + + int i = 0; + int j = 0; + + while (dirAFiles.size() > i && dirBFiles.size() > j) { + String dirASubPath = dirAFiles.get(i).getPath().substring(dirA.getPath().length()); + String dirBSubPath = dirBFiles.get(j).getPath().substring(dirB.getPath().length()); + int compareResult = dirASubPath.compareTo(dirBSubPath); + + if (compareResult < 0) { + onlyA.add(dirAFiles.get(i)); + i++; + } else if (compareResult > 0) { + onlyB.add(dirBFiles.get(j)); + j++; + } else { + boolean sameMd5 = true; + // TODO md5 check + + if (dirAFiles.get(i).length() == dirBFiles.get(j).length() && sameMd5) { + // nothing to do, both files are available + } else { + bothButDifferent.add(dirAFiles.get(i)); + bothButDifferent.add(dirBFiles.get(j)); + } + i++; + j++; + } + } + + if (dirAFiles.size() == i && dirBFiles.size() > j) { + while (j < dirBFiles.size()) { + onlyB.add(dirBFiles.get(j)); + j++; + } + } + + BuildABTestingPlugin.CompareResult result = new BuildABTestingPlugin.CompareResult(); + result.setOnlyA(onlyA); + result.setOnlyB(onlyB); + result.setBothButDifferent(bothButDifferent); + + return result; + } + + + private List getFilesResursive(File[] files) { + List filesRecursive = new ArrayList(); + for (File file : files) { + filesRecursive.add(file); + if (file.isDirectory()) { + filesRecursive.addAll(getFilesResursive(file.listFiles())); + } + } + return filesRecursive; + } +} diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index 7f189683..7a837819 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -11,13 +11,13 @@ import org.json.simple.Jsonable; import org.json.simple.Jsoner; import org.mockito.release.exec.DefaultProcessRunner; +import org.mockito.release.internal.comparison.file.FileDifferenceProvider; import org.mockito.release.notes.util.IOUtil; import java.io.File; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** @@ -120,68 +120,6 @@ public void compareDirectories() { } } - public static class FileDifferenceProvider { - - public CompareResult getDifference(File dirA, File dirB) { - List dirAFiles = getFilesResursive(dirA.listFiles()); - Collections.sort(dirAFiles); - List dirBFiles = getFilesResursive(dirB.listFiles()); - Collections.sort(dirBFiles); - - List onlyA = new ArrayList(); - List onlyB = new ArrayList(); - List bothButDifferent = new ArrayList(); - - int i = 0; - int j = 0; - - while (dirAFiles.size() > i && dirBFiles.size() > j) { - String dirASubPath = dirAFiles.get(i).getPath().substring(dirA.getPath().length()); - String dirBSubPath = dirBFiles.get(j).getPath().substring(dirB.getPath().length()); - int compareResult = dirASubPath.compareTo(dirBSubPath); - - if (compareResult < 0) { - onlyA.add(dirAFiles.get(i)); - i++; - } else if (compareResult > 0) { - onlyB.add(dirBFiles.get(j)); - j++; - } else { - boolean sameMd5 = true; - // TODO md5 check - - if (dirAFiles.get(i).length() == dirBFiles.get(j).length() && sameMd5) { - // nothing to do, both files are available - } else { - bothButDifferent.add(dirAFiles.get(i)); - bothButDifferent.add(dirBFiles.get(j)); - } - i++; - j++; - } - } - - CompareResult result = new CompareResult(); - result.setOnlyA(onlyA); - result.setOnlyB(onlyB); - result.setBothButDifferent(bothButDifferent); - - return result; - } - - - private List getFilesResursive(File[] files) { - List filesRecursive = new ArrayList(); - for (File file : files) { - filesRecursive.add(file); - if (file.isDirectory()) { - filesRecursive.addAll(getFilesResursive(file.listFiles())); - } - } - return filesRecursive; - } - } - public static class CompareResult implements Jsonable { private static final String JSON_FORMAT = "{ \"onlyA\": \"%s\", \"onlyB\": \"%s\", " + @@ -212,7 +150,7 @@ public String toJson() { } private List toStringList(List files) { - List ret = new ArrayList(); + List ret = new ArrayList(files.size()); for (File file : files) { ret.add(file.getPath()); } diff --git a/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy new file mode 100644 index 00000000..5280bbd1 --- /dev/null +++ b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy @@ -0,0 +1,74 @@ +package org.mockito.release.internal.comparison.file + +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import org.mockito.release.internal.gradle.BuildABTestingPlugin +import spock.lang.Specification + + +class FileDifferenceProviderTest extends Specification { + + @Rule + TemporaryFolder tmp = new TemporaryFolder() + + File dirA, dirB + + def setup() { + dirA = tmp.newFolder('dirA') + dirB = tmp.newFolder('dirB') + } + + def "sameContent" () { + given: + createSomeSameContent() + + when: + BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + + then: + result.onlyA.isEmpty() + result.onlyB.isEmpty() + result.bothButDifferent.isEmpty() + } + + def "onlyA" () { + given: + createSomeSameContent() + File dirC = new File(dirA, 'b/c') + dirC.mkdirs() + File fileD = new File(dirC, 'd') + fileD << 'content of d' + + when: + BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + + then: + result.onlyA == [dirC.parentFile, dirC, fileD] + result.onlyB.isEmpty() + result.bothButDifferent.isEmpty() + } + + def "onlyB" () { + given: + createSomeSameContent() + File dirZ = new File(dirB, 'x/y/z') + dirZ.mkdirs() + File fileW = new File(dirZ, 'w') + fileW << 'content of d' + + when: + BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + + then: + result.onlyA.isEmpty() + result.onlyB == [dirZ.parentFile.parentFile, dirZ.parentFile, dirZ, fileW] + result.bothButDifferent.isEmpty() + } + + private void createSomeSameContent() { + File dirAFile = new File(dirA, 'newFile') + dirAFile << "someContent" + File dirBFile = new File(dirB,'newFile') + dirBFile << 'someContent' + } +} From a6fa4e95deb8653df2312858fdd6ee8703f3c388 Mon Sep 17 00:00:00 2001 From: epeee Date: Sat, 27 May 2017 06:23:55 +0200 Subject: [PATCH 10/16] some more tests --- .../file/FileDifferenceProvider.java | 7 +++++++ .../file/FileDifferenceProviderTest.groovy | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java index 1cf4be43..4b77f7ed 100644 --- a/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java +++ b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java @@ -56,6 +56,13 @@ public BuildABTestingPlugin.CompareResult getDifference(File dirA, File dirB) { } } + if (dirBFiles.size() == j && dirAFiles.size() > i) { + while (i < dirAFiles.size()) { + onlyA.add(dirAFiles.get(i)); + i++; + } + } + BuildABTestingPlugin.CompareResult result = new BuildABTestingPlugin.CompareResult(); result.setOnlyA(onlyA); result.setOnlyB(onlyB); diff --git a/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy index 5280bbd1..a4f91e53 100644 --- a/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy +++ b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy @@ -48,6 +48,27 @@ class FileDifferenceProviderTest extends Specification { result.bothButDifferent.isEmpty() } + def "another onlyA" () { + given: + createSomeSameContent() + File dirC = new File(dirA, 'b/c') + dirC.mkdirs() + File fileD = new File(dirC, 'd') + fileD << 'content of d' + File dirT = new File(dirA, 't') + dirT.mkdirs() + File fileU = new File(dirT, 'u') + fileU << 'content of u' + + when: + BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + + then: + result.onlyA == [dirC.parentFile, dirC, fileD, dirT, fileU] + result.onlyB.isEmpty() + result.bothButDifferent.isEmpty() + } + def "onlyB" () { given: createSomeSameContent() From aab526408f4cc71019784fa9063f7a9454e56956 Mon Sep 17 00:00:00 2001 From: epeee Date: Sat, 27 May 2017 06:32:51 +0200 Subject: [PATCH 11/16] even more tests --- .../file/FileDifferenceProviderTest.groovy | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy index a4f91e53..3012eaa1 100644 --- a/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy +++ b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy @@ -3,6 +3,7 @@ package org.mockito.release.internal.comparison.file import org.junit.Rule import org.junit.rules.TemporaryFolder import org.mockito.release.internal.gradle.BuildABTestingPlugin +import spock.lang.Ignore import spock.lang.Specification @@ -86,10 +87,48 @@ class FileDifferenceProviderTest extends Specification { result.bothButDifferent.isEmpty() } + def "both but different"() { + given: + createSomeSameContent() + + File dirADifferentFile = new File(dirA, 'different') + dirADifferentFile << "someContent" + File dirBDifferentFile = new File(dirB,'different') + dirBDifferentFile << 'differentContent' + + when: + BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + + then: + result.onlyA.isEmpty() + result.onlyB.isEmpty() + result.bothButDifferent == [dirADifferentFile, dirBDifferentFile] + } + + @Ignore + def "both but different (same length)"() { + given: + createSomeSameContent() + + File dirADifferentFile = new File(dirA, 'different') + dirADifferentFile << "content A" + File dirBDifferentFile = new File(dirB,'different') + dirBDifferentFile << 'content B' + + when: + BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + + then: + result.onlyA.isEmpty() + result.onlyB.isEmpty() + result.bothButDifferent == [dirADifferentFile, dirBDifferentFile] + } + private void createSomeSameContent() { File dirAFile = new File(dirA, 'newFile') dirAFile << "someContent" File dirBFile = new File(dirB,'newFile') dirBFile << 'someContent' } + } From 7ae953b65f74cec27769de2369762b6079d0a5c8 Mon Sep 17 00:00:00 2001 From: epeee Date: Sat, 27 May 2017 21:38:20 +0200 Subject: [PATCH 12/16] move CompareResult and CompareResultSerializer added tests fixed serialization --- .../comparison/file/CompareResult.java | 55 +++++++++ .../file/CompareResultSerializer.java | 50 +++++++++ .../file/FileDifferenceProvider.java | 6 +- .../internal/gradle/BuildABTestingPlugin.java | 69 +----------- .../file/CompareResultSerializerTest.groovy | 106 ++++++++++++++++++ .../file/FileDifferenceProviderTest.groovy | 13 +-- 6 files changed, 221 insertions(+), 78 deletions(-) create mode 100644 src/main/groovy/org/mockito/release/internal/comparison/file/CompareResult.java create mode 100644 src/main/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializer.java create mode 100644 src/test/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializerTest.groovy diff --git a/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResult.java b/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResult.java new file mode 100644 index 00000000..d82a8204 --- /dev/null +++ b/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResult.java @@ -0,0 +1,55 @@ +package org.mockito.release.internal.comparison.file; + +import org.json.simple.Jsonable; +import org.json.simple.Jsoner; + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; + + +public class CompareResult implements Jsonable { + + private static final String JSON_FORMAT = "{ \"onlyA\": %s, \"onlyB\": %s, " + + "\"both\": %s }"; + + private List onlyA; + private List onlyB; + private List bothButDifferent; + + public void setOnlyA(List file) { + this.onlyA = file; + } + + public void setOnlyB(List file) { + this.onlyB = file; + } + + public void setBothButDifferent(List file) { + this.bothButDifferent = file; + } + + @Override + public String toJson() { + return String.format(JSON_FORMAT, + Jsoner.serialize(toStringList(onlyA)), + Jsoner.serialize(toStringList(onlyB)), + Jsoner.serialize(toStringList(bothButDifferent))); + } + + private List toStringList(List files) { + List ret = new ArrayList(files.size()); + for (File file : files) { + ret.add(file.getPath()); + } + return ret; + } + + @Override + public void toJson(Writer writable) throws IOException { + writable.append(toJson()); + } + +} diff --git a/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializer.java b/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializer.java new file mode 100644 index 00000000..d5783954 --- /dev/null +++ b/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializer.java @@ -0,0 +1,50 @@ +package org.mockito.release.internal.comparison.file; + +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.json.simple.DeserializationException; +import org.json.simple.JsonObject; +import org.json.simple.Jsoner; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + + +public class CompareResultSerializer { + + private static final Logger LOG = Logging.getLogger(CompareResultSerializer.class); + + public String serialize(CompareResult compareResult) { + String json = Jsoner.serialize(compareResult); + LOG.info("Serialize compare result to: {}", json); + return json; + } + + public CompareResult deserialize(String json) { + CompareResult compareResult = new CompareResult(); + try { + LOG.info("Deserialize compare result from: {}", json); + JsonObject jsonObject = (JsonObject) Jsoner.deserialize(json); + Collection onlyA = jsonObject.getCollection("onlyA"); + Collection onlyB = jsonObject.getCollection("onlyB"); + Collection both = jsonObject.getCollection("both"); + compareResult.setOnlyA(toFileList(onlyA)); + compareResult.setOnlyB(toFileList(onlyB)); + compareResult.setBothButDifferent(toFileList(both)); + } catch (DeserializationException e) { + throw new RuntimeException("Can't deserialize JSON: " + json, e); + } + return compareResult; + } + + private List toFileList(Collection onlyA) { + List fileList = new ArrayList(onlyA.size()); + for (String filePath : onlyA) { + fileList.add(new File(filePath)); + } + return fileList; + } + +} diff --git a/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java index 4b77f7ed..d97f32c0 100644 --- a/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java +++ b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java @@ -1,7 +1,5 @@ package org.mockito.release.internal.comparison.file; -import org.mockito.release.internal.gradle.BuildABTestingPlugin; - import java.io.File; import java.util.ArrayList; import java.util.Collections; @@ -10,7 +8,7 @@ public class FileDifferenceProvider { - public BuildABTestingPlugin.CompareResult getDifference(File dirA, File dirB) { + public CompareResult getDifference(File dirA, File dirB) { List dirAFiles = getFilesResursive(dirA.listFiles()); Collections.sort(dirAFiles); List dirBFiles = getFilesResursive(dirB.listFiles()); @@ -63,7 +61,7 @@ public BuildABTestingPlugin.CompareResult getDifference(File dirA, File dirB) { } } - BuildABTestingPlugin.CompareResult result = new BuildABTestingPlugin.CompareResult(); + CompareResult result = new CompareResult(); result.setOnlyA(onlyA); result.setOnlyB(onlyB); result.setBothButDifferent(bothButDifferent); diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index 7a837819..629831a0 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -5,20 +5,14 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.file.CopySpec; -import org.gradle.api.logging.Logger; -import org.gradle.api.logging.Logging; import org.gradle.api.tasks.*; -import org.json.simple.Jsonable; -import org.json.simple.Jsoner; import org.mockito.release.exec.DefaultProcessRunner; +import org.mockito.release.internal.comparison.file.CompareResult; +import org.mockito.release.internal.comparison.file.CompareResultSerializer; import org.mockito.release.internal.comparison.file.FileDifferenceProvider; import org.mockito.release.notes.util.IOUtil; import java.io.File; -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import java.util.List; /** * See rationale and design at: https://github.com/mockito/mockito-release-tools/issues/113 @@ -120,65 +114,6 @@ public void compareDirectories() { } } - public static class CompareResult implements Jsonable { - - private static final String JSON_FORMAT = "{ \"onlyA\": \"%s\", \"onlyB\": \"%s\", " + - "\"both\": \"%s\" }"; - - private List onlyA; - private List onlyB; - private List bothButDifferent; - - public void setOnlyA(List file) { - this.onlyA = file; - } - - public void setOnlyB(List file) { - this.onlyB = file; - } - - public void setBothButDifferent(List file) { - this.bothButDifferent = file; - } - - @Override - public String toJson() { - return String.format(JSON_FORMAT, - Jsoner.serialize(toStringList(onlyA)), - Jsoner.serialize(toStringList(onlyB)), - Jsoner.serialize(toStringList(bothButDifferent))); - } - - private List toStringList(List files) { - List ret = new ArrayList(files.size()); - for (File file : files) { - ret.add(file.getPath()); - } - return ret; - } - - @Override - public void toJson(Writer writable) throws IOException { - writable.append(toJson()); - } - } - - public static class CompareResultSerializer { - - private static final Logger LOG = Logging.getLogger(CompareResultSerializer.class); - - public String serialize(CompareResult compareResult) { - String json = Jsoner.serialize(compareResult); - LOG.info("Serialize compare result to: {}", json); - return json; - } - - public CompareResult deserialize(String json) { - return null; - } - - } - public static class RunTestTask extends DefaultTask { private File sourceDir; diff --git a/src/test/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializerTest.groovy b/src/test/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializerTest.groovy new file mode 100644 index 00000000..228ba63b --- /dev/null +++ b/src/test/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializerTest.groovy @@ -0,0 +1,106 @@ +package org.mockito.release.internal.comparison.file + +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import spock.lang.Specification +import spock.lang.Subject + + +class CompareResultSerializerTest extends Specification { + + @Subject serializer = new CompareResultSerializer() + + @Rule + TemporaryFolder tmp = new TemporaryFolder() + + File dirA + + def setup() { + dirA = tmp.newFolder('dirA') + } + + def "serialization and deserialization of compareResult (emty)"() { + given: + def compareResult = new CompareResult() + compareResult.setOnlyA([]) + compareResult.setOnlyB([]) + compareResult.setBothButDifferent([]) + + when: + def json = serializer.serialize(compareResult) + def actual = serializer.deserialize(json) + + then: + actual.onlyA == [] + actual.onlyB == [] + actual.bothButDifferent == [] + } + + def "serialization and deserialization of compareResult (onlyA)"() { + given: + def compareResult = new CompareResult() + compareResult.setOnlyA([new File(dirA, "a"), new File(dirA,"b")]) + compareResult.setOnlyB([]) + compareResult.setBothButDifferent([]) + + when: + def json = serializer.serialize(compareResult) + def actual = serializer.deserialize(json) + + then: + actual.onlyA == [new File(dirA, "a"), new File(dirA,"b")] + actual.onlyB == [] + actual.bothButDifferent == [] + } + + def "serialization and deserialization of compareResult (onlyB)"() { + given: + def compareResult = new CompareResult() + compareResult.setOnlyA([]) + compareResult.setOnlyB([new File(dirA, "a"), new File(dirA,"b")]) + compareResult.setBothButDifferent([]) + + when: + def json = serializer.serialize(compareResult) + def actual = serializer.deserialize(json) + + then: + actual.onlyA == [] + actual.onlyB == [new File(dirA, "a"), new File(dirA,"b")] + actual.bothButDifferent == [] + } + + def "serialization and deserialization of compareResult (both)"() { + given: + def compareResult = new CompareResult() + compareResult.setOnlyA([]) + compareResult.setOnlyB([]) + compareResult.setBothButDifferent([new File(dirA, "a"), new File(dirA,"b")]) + + when: + def json = serializer.serialize(compareResult) + def actual = serializer.deserialize(json) + + then: + actual.onlyA == [] + actual.onlyB == [] + actual.bothButDifferent == [new File(dirA, "a"), new File(dirA,"b")] + } + + def "serialization and deserialization of compareResult (full sample)"() { + given: + def compareResult = new CompareResult() + compareResult.setOnlyA([new File(dirA, "x"), new File(dirA,"y")]) + compareResult.setOnlyB([new File(dirA, "u"), new File(dirA,"v"), new File(dirA,"w")]) + compareResult.setBothButDifferent([new File(dirA, "a"), new File(dirA,"b")]) + + when: + def json = serializer.serialize(compareResult) + def actual = serializer.deserialize(json) + + then: + actual.onlyA == [new File(dirA, "x"), new File(dirA,"y")] + actual.onlyB == [new File(dirA, "u"), new File(dirA,"v"), new File(dirA,"w")] + actual.bothButDifferent == [new File(dirA, "a"), new File(dirA,"b")] + } +} diff --git a/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy index 3012eaa1..73b889a3 100644 --- a/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy +++ b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy @@ -2,7 +2,6 @@ package org.mockito.release.internal.comparison.file import org.junit.Rule import org.junit.rules.TemporaryFolder -import org.mockito.release.internal.gradle.BuildABTestingPlugin import spock.lang.Ignore import spock.lang.Specification @@ -24,7 +23,7 @@ class FileDifferenceProviderTest extends Specification { createSomeSameContent() when: - BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); then: result.onlyA.isEmpty() @@ -41,7 +40,7 @@ class FileDifferenceProviderTest extends Specification { fileD << 'content of d' when: - BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); then: result.onlyA == [dirC.parentFile, dirC, fileD] @@ -62,7 +61,7 @@ class FileDifferenceProviderTest extends Specification { fileU << 'content of u' when: - BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); then: result.onlyA == [dirC.parentFile, dirC, fileD, dirT, fileU] @@ -79,7 +78,7 @@ class FileDifferenceProviderTest extends Specification { fileW << 'content of d' when: - BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); then: result.onlyA.isEmpty() @@ -97,7 +96,7 @@ class FileDifferenceProviderTest extends Specification { dirBDifferentFile << 'differentContent' when: - BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); then: result.onlyA.isEmpty() @@ -116,7 +115,7 @@ class FileDifferenceProviderTest extends Specification { dirBDifferentFile << 'content B' when: - BuildABTestingPlugin.CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); then: result.onlyA.isEmpty() From 14a85b0db19ce054d33b36b5e19ddc292c431fce Mon Sep 17 00:00:00 2001 From: epeee Date: Sun, 28 May 2017 11:23:43 +0200 Subject: [PATCH 13/16] extract CompareDirectoriesTask from BuildABTestingPlugin --- .../internal/gradle/BuildABTestingPlugin.java | 43 ---------------- .../gradle/CompareDirectoriesTask.java | 51 +++++++++++++++++++ 2 files changed, 51 insertions(+), 43 deletions(-) create mode 100644 src/main/groovy/org/mockito/release/internal/gradle/CompareDirectoriesTask.java diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index 629831a0..9e745b69 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -7,10 +7,6 @@ import org.gradle.api.file.CopySpec; import org.gradle.api.tasks.*; import org.mockito.release.exec.DefaultProcessRunner; -import org.mockito.release.internal.comparison.file.CompareResult; -import org.mockito.release.internal.comparison.file.CompareResultSerializer; -import org.mockito.release.internal.comparison.file.FileDifferenceProvider; -import org.mockito.release.notes.util.IOUtil; import java.io.File; @@ -75,45 +71,6 @@ public void analyze() { } } - /** - * A Task which compares two given directories. The result will be serialized to a result file. - */ - public static class CompareDirectoriesTask extends DefaultTask { - - private File dirA; - private File dirB; - private File resultsFile; - - @InputDirectory - public void setDirA(File dir) { - this.dirA = dir; - } - - @InputDirectory - public void setDirB(File dir) { - this.dirB = dir; - } - - public void setResultsFile(File file) { - this.resultsFile = file; - } - - @OutputFile - public File getResultsFile() { - return resultsFile; - } - - @TaskAction - public void compareDirectories() { - if (resultsFile.exists()) { - getProject().delete(resultsFile); - } - - CompareResult compareResult = new FileDifferenceProvider().getDifference(dirA, dirB); - IOUtil.writeFile(resultsFile, new CompareResultSerializer().serialize(compareResult)); - } - } - public static class RunTestTask extends DefaultTask { private File sourceDir; diff --git a/src/main/groovy/org/mockito/release/internal/gradle/CompareDirectoriesTask.java b/src/main/groovy/org/mockito/release/internal/gradle/CompareDirectoriesTask.java new file mode 100644 index 00000000..64433819 --- /dev/null +++ b/src/main/groovy/org/mockito/release/internal/gradle/CompareDirectoriesTask.java @@ -0,0 +1,51 @@ +package org.mockito.release.internal.gradle; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; +import org.mockito.release.internal.comparison.file.CompareResult; +import org.mockito.release.internal.comparison.file.CompareResultSerializer; +import org.mockito.release.internal.comparison.file.FileDifferenceProvider; +import org.mockito.release.notes.util.IOUtil; + +import java.io.File; + +/** + * A Task which compares two given directories. The result will be serialized to a result file. + */ +public class CompareDirectoriesTask extends DefaultTask { + + private File dirA; + private File dirB; + private File resultsFile; + + @InputDirectory + public void setDirA(File dir) { + this.dirA = dir; + } + + @InputDirectory + public void setDirB(File dir) { + this.dirB = dir; + } + + public void setResultsFile(File file) { + this.resultsFile = file; + } + + @OutputFile + public File getResultsFile() { + return resultsFile; + } + + @TaskAction + public void compareDirectories() { + if (resultsFile.exists()) { + getProject().delete(resultsFile); + } + + CompareResult compareResult = new FileDifferenceProvider().getDifference(dirA, dirB); + IOUtil.writeFile(resultsFile, new CompareResultSerializer().serialize(compareResult)); + } +} From 64a59b98ed1d5966aac5bf5d11edeffa1d7463a6 Mon Sep 17 00:00:00 2001 From: epeee Date: Sun, 28 May 2017 12:52:14 +0200 Subject: [PATCH 14/16] javadoc --- .../release/internal/comparison/file/CompareResult.java | 5 ++++- .../internal/comparison/file/CompareResultSerializer.java | 4 +++- .../release/internal/gradle/BuildABTestingPlugin.java | 5 +++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResult.java b/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResult.java index d82a8204..42c1c721 100644 --- a/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResult.java +++ b/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResult.java @@ -9,7 +9,10 @@ import java.util.ArrayList; import java.util.List; - +/** + * Holds the compare result of two directories. The result consists of files which are only available in dirA, files + * which are only available in dirB and files which are available in both directories and their content is different. + */ public class CompareResult implements Jsonable { private static final String JSON_FORMAT = "{ \"onlyA\": %s, \"onlyB\": %s, " + diff --git a/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializer.java b/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializer.java index d5783954..46c9d37f 100644 --- a/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializer.java +++ b/src/main/groovy/org/mockito/release/internal/comparison/file/CompareResultSerializer.java @@ -11,7 +11,9 @@ import java.util.Collection; import java.util.List; - +/** + * Json serializer for {@link CompareResult}s. + */ public class CompareResultSerializer { private static final Logger LOG = Logging.getLogger(CompareResultSerializer.class); diff --git a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java index 9e745b69..841a4c17 100644 --- a/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java +++ b/src/main/groovy/org/mockito/release/internal/gradle/BuildABTestingPlugin.java @@ -71,6 +71,11 @@ public void analyze() { } } + /** + * Gradle task to run a given command line. + * Note: not yet migrated to RunTestReleaseTask because RunTestReleaseTask requires two separate checkout tasks + * which might take some time (e.g. if we are using a repo like mockito and the internet connection is not that fast. + */ public static class RunTestTask extends DefaultTask { private File sourceDir; From 616ae0161b6c3db4d0a6a2e30a6a40ee81d257d6 Mon Sep 17 00:00:00 2001 From: epeee Date: Sun, 28 May 2017 13:45:26 +0200 Subject: [PATCH 15/16] FileDifferenceProvider: calculate md5 + enable test case which was failing previously due to missing md5 check --- .../file/FileDifferenceProvider.java | 6 ++- .../mockito/release/internal/util/Md5.java | 48 +++++++++++++++++++ .../file/FileDifferenceProviderTest.groovy | 15 +++++- 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 src/main/groovy/org/mockito/release/internal/util/Md5.java diff --git a/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java index d97f32c0..cc6d24e3 100644 --- a/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java +++ b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java @@ -1,5 +1,7 @@ package org.mockito.release.internal.comparison.file; +import org.mockito.release.internal.util.Md5; + import java.io.File; import java.util.ArrayList; import java.util.Collections; @@ -33,7 +35,9 @@ public CompareResult getDifference(File dirA, File dirB) { onlyB.add(dirBFiles.get(j)); j++; } else { - boolean sameMd5 = true; + String md5A = Md5.calculate(dirAFiles.get(i)); + String md5B = Md5.calculate(dirBFiles.get(j)); + boolean sameMd5 = md5A.equals(md5B); // TODO md5 check if (dirAFiles.get(i).length() == dirBFiles.get(j).length() && sameMd5) { diff --git a/src/main/groovy/org/mockito/release/internal/util/Md5.java b/src/main/groovy/org/mockito/release/internal/util/Md5.java new file mode 100644 index 00000000..aacf6d94 --- /dev/null +++ b/src/main/groovy/org/mockito/release/internal/util/Md5.java @@ -0,0 +1,48 @@ +package org.mockito.release.internal.util; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Md5 utilities + */ +public class Md5 { + + /** + * Calculates the md5 of a given file. + * @param file the file to calculate the md5 for + * @return the resulting md5 + */ + public static String calculate(File file) { + InputStream is = null; + try { + MessageDigest md = MessageDigest.getInstance("md5"); + is = new FileInputStream(file); + + int bytesRead = 0; + byte[] data = new byte[1024]; + while ((bytesRead = is.read(data)) != -1) { + md.update(data, 0, bytesRead); + } + return new BigInteger(1, md.digest()).toString(16); + } catch (java.io.IOException e) { + throw new RuntimeException("error while generating md5 for file " + file, e); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("error while generating md5 for file " + file, e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + } +} diff --git a/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy index 73b889a3..901790a3 100644 --- a/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy +++ b/src/test/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProviderTest.groovy @@ -2,7 +2,6 @@ package org.mockito.release.internal.comparison.file import org.junit.Rule import org.junit.rules.TemporaryFolder -import spock.lang.Ignore import spock.lang.Specification @@ -104,7 +103,6 @@ class FileDifferenceProviderTest extends Specification { result.bothButDifferent == [dirADifferentFile, dirBDifferentFile] } - @Ignore def "both but different (same length)"() { given: createSomeSameContent() @@ -123,6 +121,19 @@ class FileDifferenceProviderTest extends Specification { result.bothButDifferent == [dirADifferentFile, dirBDifferentFile] } + def "same files and same content"() { + given: + createSomeSameContent() + + when: + CompareResult result = new FileDifferenceProvider().getDifference(dirA, dirB); + + then: + result.onlyA.isEmpty() + result.onlyB.isEmpty() + result.bothButDifferent.isEmpty() + } + private void createSomeSameContent() { File dirAFile = new File(dirA, 'newFile') dirAFile << "someContent" From 04154f6026e0601ff0739e86ff81f1d2dfa101e0 Mon Sep 17 00:00:00 2001 From: epeee Date: Sun, 28 May 2017 13:46:47 +0200 Subject: [PATCH 16/16] remove outdated todo --- .../release/internal/comparison/file/FileDifferenceProvider.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java index cc6d24e3..7a0324ee 100644 --- a/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java +++ b/src/main/groovy/org/mockito/release/internal/comparison/file/FileDifferenceProvider.java @@ -38,7 +38,6 @@ public CompareResult getDifference(File dirA, File dirB) { String md5A = Md5.calculate(dirAFiles.get(i)); String md5B = Md5.calculate(dirBFiles.get(j)); boolean sameMd5 = md5A.equals(md5B); - // TODO md5 check if (dirAFiles.get(i).length() == dirBFiles.get(j).length() && sameMd5) { // nothing to do, both files are available