From f83a0191a89b7acebcf3edcc6f8793049c0c5a7f Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Thu, 18 Jul 2019 15:48:27 +0100 Subject: [PATCH] Test helper for refaster checks (#697) --- .gitignore | 2 +- baseline-refaster-rules/build.gradle | 3 + .../refaster/CollectionsIsEmptyTest.java | 40 ++++++ .../refaster/OptionalOrElseSupplierTest.java | 44 +++++++ .../baseline/refaster/SortedFirstTest.java | 45 +++++++ .../baseline/refaster/Utf8LengthTest.java | 41 ++++++ baseline-refaster-testing/build.gradle | 27 ++++ .../baseline/refaster/CompilerUtility.java | 88 +++++++++++++ .../baseline/refaster/RefasterTestHelper.java | 118 ++++++++++++++++++ changelog/@unreleased/pr-697.v2.yml | 5 + settings.gradle | 1 + 11 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/CollectionsIsEmptyTest.java create mode 100644 baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/OptionalOrElseSupplierTest.java create mode 100644 baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/SortedFirstTest.java create mode 100644 baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/Utf8LengthTest.java create mode 100644 baseline-refaster-testing/build.gradle create mode 100644 baseline-refaster-testing/src/main/java/com/palantir/baseline/refaster/CompilerUtility.java create mode 100644 baseline-refaster-testing/src/main/java/com/palantir/baseline/refaster/RefasterTestHelper.java create mode 100644 changelog/@unreleased/pr-697.v2.yml diff --git a/.gitignore b/.gitignore index c67c09b73..16e3a6ed6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ .project .settings build -/out/ +out/ gradle.properties docs/node_modules/ generated_src diff --git a/baseline-refaster-rules/build.gradle b/baseline-refaster-rules/build.gradle index 4d003b5ca..988115122 100644 --- a/baseline-refaster-rules/build.gradle +++ b/baseline-refaster-rules/build.gradle @@ -5,4 +5,7 @@ apply from: "${rootDir}/gradle/publish-jar.gradle" dependencies { compile 'com.google.errorprone:error_prone_refaster' + + testCompile 'junit:junit' + testCompile project(':baseline-refaster-testing') } diff --git a/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/CollectionsIsEmptyTest.java b/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/CollectionsIsEmptyTest.java new file mode 100644 index 000000000..fd5115509 --- /dev/null +++ b/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/CollectionsIsEmptyTest.java @@ -0,0 +1,40 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.baseline.refaster; + +import org.junit.Test; + +public class CollectionsIsEmptyTest { + + @Test + public void test() { + RefasterTestHelper + .forRefactoring(CollectionsIsEmpty.class) + .withInputLines( + "Test", + "import java.util.ArrayList;", + "public class Test {", + " boolean empty = new ArrayList<>().size() == 0;", + "}") + .hasOutputLines( + "import java.util.ArrayList;", + "public class Test {", + " boolean empty = new ArrayList<>().isEmpty();", + "}"); + } + +} diff --git a/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/OptionalOrElseSupplierTest.java b/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/OptionalOrElseSupplierTest.java new file mode 100644 index 000000000..0c0dec7c0 --- /dev/null +++ b/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/OptionalOrElseSupplierTest.java @@ -0,0 +1,44 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.baseline.refaster; + +import org.junit.Test; + +public class OptionalOrElseSupplierTest { + + @Test + public void test() { + RefasterTestHelper + .forRefactoring(OptionalOrElseSupplier.class) + .withInputLines( + "Test", + "import java.util.*;", + "import java.util.function.Supplier;", + "public class Test {", + " Supplier supplier = () -> \"hello\";", + " String s = Optional.ofNullable(\"world\").orElse(supplier.get());", + "}") + .hasOutputLines( + "import java.util.*;", + "import java.util.function.Supplier;", + "public class Test {", + " Supplier supplier = () -> \"hello\";", + " String s = Optional.ofNullable(\"world\").orElseGet(supplier);", + "}"); + } + +} diff --git a/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/SortedFirstTest.java b/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/SortedFirstTest.java new file mode 100644 index 000000000..bce9debdb --- /dev/null +++ b/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/SortedFirstTest.java @@ -0,0 +1,45 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.baseline.refaster; + +import org.junit.Test; + +public class SortedFirstTest { + + @Test + public void test() { + RefasterTestHelper + .forRefactoring(SortedFirst.class) + .withInputLines( + "Test", + "import java.util.*;", + "import java.util.stream.Stream;", + "public class Test {", + " Optional i = Arrays.asList(5, -10, 7, -18, 23).stream()", + " .sorted(Comparator.reverseOrder())", + " .findFirst();", + "}") + .hasOutputLines( + "import java.util.*;", + "import java.util.stream.Stream;", + "public class Test {", + " Optional i = Arrays.asList(5, -10, 7, -18, 23).stream()" + + ".min(Comparator.reverseOrder());", + "}"); + } + +} diff --git a/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/Utf8LengthTest.java b/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/Utf8LengthTest.java new file mode 100644 index 000000000..31e31edd9 --- /dev/null +++ b/baseline-refaster-rules/src/test/java/com/palantir/baseline/refaster/Utf8LengthTest.java @@ -0,0 +1,41 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.baseline.refaster; + +import org.junit.Test; + +public class Utf8LengthTest { + + @Test + public void test() { + RefasterTestHelper + .forRefactoring(Utf8Length.class) + .withInputLines( + "Test", + "import java.nio.charset.StandardCharsets;", + "public class Test {", + " int i = \"hello world\".getBytes(StandardCharsets.UTF_8).length;", + "}") + .hasOutputLines( + "import com.google.common.base.Utf8;", + "import java.nio.charset.StandardCharsets;", + "public class Test {", + " int i = Utf8.encodedLength(\"hello world\");", + "}"); + } + +} diff --git a/baseline-refaster-testing/build.gradle b/baseline-refaster-testing/build.gradle new file mode 100644 index 000000000..56999c590 --- /dev/null +++ b/baseline-refaster-testing/build.gradle @@ -0,0 +1,27 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'java-library' +apply from: "${rootDir}/gradle/publish-jar.gradle" + +dependencies { + compile 'com.google.errorprone:error_prone_refaster' + compile 'com.google.errorprone:error_prone_test_helpers' + + compile 'com.google.guava:guava' + compile 'junit:junit' + compile 'org.assertj:assertj-core' +} diff --git a/baseline-refaster-testing/src/main/java/com/palantir/baseline/refaster/CompilerUtility.java b/baseline-refaster-testing/src/main/java/com/palantir/baseline/refaster/CompilerUtility.java new file mode 100644 index 000000000..9d3c829ea --- /dev/null +++ b/baseline-refaster-testing/src/main/java/com/palantir/baseline/refaster/CompilerUtility.java @@ -0,0 +1,88 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.baseline.refaster; + +import com.google.common.collect.ImmutableList; +import com.google.common.io.CharStreams; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.api.JavacTool; +import com.sun.tools.javac.util.Context; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Locale; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; + +final class CompilerUtility { + + private CompilerUtility() {} + + static CompilerResult compile(JavaFileObject javaFileObject) { + JavaCompiler compiler = JavacTool.create(); + DiagnosticCollector diagnosticsCollector = new DiagnosticCollector<>(); + StandardJavaFileManager fileManager = + compiler.getStandardFileManager(diagnosticsCollector, Locale.ENGLISH, StandardCharsets.UTF_8); + + JavacTaskImpl task = (JavacTaskImpl) compiler.getTask( + CharStreams.nullWriter(), + fileManager, + diagnosticsCollector, + ImmutableList.of(), + null, + ImmutableList.of(javaFileObject)); + + Iterable trees; + try { + trees = task.parse(); + task.analyze(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return new CompilerResult() { + @Override + public Context context() { + return task.getContext(); + } + + @Override + public List compilationUnits() { + return ImmutableList.copyOf(trees); + } + + @Override + public List> diagnostics() { + return diagnosticsCollector.getDiagnostics(); + } + }; + } + + interface CompilerResult { + + Context context(); + + List compilationUnits(); + + List> diagnostics(); + + } + +} diff --git a/baseline-refaster-testing/src/main/java/com/palantir/baseline/refaster/RefasterTestHelper.java b/baseline-refaster-testing/src/main/java/com/palantir/baseline/refaster/RefasterTestHelper.java new file mode 100644 index 000000000..39fedd1d7 --- /dev/null +++ b/baseline-refaster-testing/src/main/java/com/palantir/baseline/refaster/RefasterTestHelper.java @@ -0,0 +1,118 @@ +/* + * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.baseline.refaster; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.errorprone.CodeTransformer; +import com.google.errorprone.apply.DescriptionBasedDiff; +import com.google.errorprone.apply.ImportOrganizer; +import com.google.errorprone.apply.SourceFile; +import com.google.errorprone.refaster.RefasterRuleBuilderScanner; +import com.google.testing.compile.JavaFileObjects; +import com.sun.source.tree.ClassTree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.tree.JCTree; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import javax.tools.JavaFileObject; +import org.assertj.core.api.Assertions; + +public final class RefasterTestHelper { + + private final List transformers; + + /** + * The source code of the given refaster rule should exist in {@code src/main/java}. + */ + public static RefasterTestHelper forRefactoring(Class refasterRuleClass) { + return new RefasterTestHelper(refasterRuleClass); + } + + private RefasterTestHelper(Class refasterRuleClass) { + Path sourceFile = Paths + .get("src/main/java") + .resolve(refasterRuleClass.getName().replaceAll("\\.", File.separator) + ".java"); + try { + Iterable sourceLines = Files.readAllLines(sourceFile, StandardCharsets.UTF_8); + this.transformers = extractRefasterRules( + JavaFileObjects.forSourceLines(refasterRuleClass.getName(), sourceLines)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public RefactoringTestInput withInputLines(String fullyQualifiedName, String... lines) { + return new RefactoringTestInput(transformers, JavaFileObjects.forSourceLines(fullyQualifiedName, lines)); + } + + public final class RefactoringTestInput { + + private List transformers; + private JavaFileObject input; + + RefactoringTestInput(List transformers, JavaFileObject input) { + this.transformers = transformers; + this.input = input; + } + + public void hasOutputLines(String... lines) { + CompilerUtility.CompilerResult result = CompilerUtility.compile(input); + Assertions.assertThat(result.diagnostics()).isEmpty(); + + JCTree.JCCompilationUnit tree = result.compilationUnits().stream() + .filter(compilationUnitTree -> compilationUnitTree instanceof JCTree.JCCompilationUnit) + .map(compilationUnitTree -> (JCTree.JCCompilationUnit) compilationUnitTree) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Failed to compile input lines")); + + DescriptionBasedDiff diff = DescriptionBasedDiff.create(tree, ImportOrganizer.STATIC_FIRST_ORGANIZER); + transformers.forEach(transformer -> transformer.apply(new TreePath(tree), result.context(), diff)); + + SourceFile inputSourceFile = sourceFile(input); + diff.applyDifferences(inputSourceFile); + + Assertions.assertThat(inputSourceFile.getSourceText()).isEqualTo(Joiner.on('\n').join(lines)); + } + } + + private static SourceFile sourceFile(JavaFileObject javaFileObject) { + try { + return SourceFile.create(javaFileObject); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private List extractRefasterRules(JavaFileObject object) { + CompilerUtility.CompilerResult result = CompilerUtility.compile(object); + ClassTree classTree = result.compilationUnits().stream() + .flatMap(compilationUnitTree -> compilationUnitTree.getTypeDecls().stream()) + .filter(tree -> tree instanceof ClassTree) + .map(tree -> (ClassTree) tree) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("No class found in Refaster rule")); + + return ImmutableList.copyOf(RefasterRuleBuilderScanner.extractRules(classTree, result.context())); + } + +} diff --git a/changelog/@unreleased/pr-697.v2.yml b/changelog/@unreleased/pr-697.v2.yml new file mode 100644 index 000000000..9585e6c74 --- /dev/null +++ b/changelog/@unreleased/pr-697.v2.yml @@ -0,0 +1,5 @@ +type: feature +feature: + description: Test helper for refaster checks. + links: + - https://github.com/palantir/gradle-baseline/pull/697 diff --git a/settings.gradle b/settings.gradle index 5751b326d..e65ad3a72 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,6 +4,7 @@ enableFeaturePreview("STABLE_PUBLISHING") include "baseline-error-prone" include "baseline-refaster-javac-plugin" include "baseline-refaster-rules" +include "baseline-refaster-testing" include "gradle-baseline-java" include "gradle-baseline-java-config" include "gradle-junit-reports"