/*
* Copyright 2014 Google 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.google.errorprone;
import static com.google.common.truth.Truth.assertThat;
import static com.google.errorprone.BugPattern.Category.JDK;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static org.junit.Assert.fail;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.CompilationUnitTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.ReturnTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ReturnTree;
import com.sun.tools.javac.main.Main.Result;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests for {@link CompilationTestHelper}.
*/
@RunWith(JUnit4.class)
public class CompilationTestHelperTest {
private CompilationTestHelper compilationHelper;
@Before
public void setUp() {
compilationHelper = CompilationTestHelper.newInstance(ReturnTreeChecker.class, getClass());
}
@Test
public void fileWithNoBugMarkersAndNoErrorsShouldPass() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {}")
.doTest();
}
@Test
public void fileWithNoBugMarkersAndErrorFails() {
try {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" public boolean doIt() {",
" return true;",
" }",
"}")
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage()).contains("Saw unexpected error on line 3");
}
}
@Test
public void fileWithBugMarkerAndNoErrorsFails() {
try {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" // BUG: Diagnostic contains:",
" public void doIt() {}",
"}")
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage()).contains("Did not see an error on line 3");
}
}
@Test
public void fileWithBugMatcherAndNoErrorsFails() {
try {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" // BUG: Diagnostic matches: X",
" public void doIt() {}",
"}")
.expectErrorMessage("X", Predicates.containsPattern(""))
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage()).contains("Did not see an error on line 3");
}
}
@Test
public void fileWithBugMarkerAndMatchingErrorSucceeds() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" public boolean doIt() {",
" // BUG: Diagnostic contains: Method may return normally",
" return true;",
" }",
"}")
.doTest();
}
@Test
public void fileWithBugMatcherAndMatchingErrorSucceeds() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" public boolean doIt() {",
" // BUG: Diagnostic matches: X",
" return true;",
" }",
"}")
.expectErrorMessage("X", Predicates.containsPattern("Method may return normally"))
.doTest();
}
@Test
public void fileWithBugMarkerAndErrorOnWrongLineFails() {
try {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" // BUG: Diagnostic contains:",
" public boolean doIt() {",
" return true;",
" }",
"}")
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage()).contains("Did not see an error on line 3");
}
}
@Test
public void fileWithBugMatcherAndErrorOnWrongLineFails() {
try {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" // BUG: Diagnostic matches: X",
" public boolean doIt() {",
" return true;",
" }",
"}")
.expectErrorMessage("X", Predicates.containsPattern(""))
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage()).contains("Did not see an error on line 3");
}
}
@Test
public void fileWithMultipleBugMarkersAndMatchingErrorsSucceeds() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" public boolean doIt() {",
" // BUG: Diagnostic contains: Method may return normally",
" return true;",
" }",
" public String doItAgain() {",
" // BUG: Diagnostic contains: Method may return normally",
" return null;",
" }",
"}")
.doTest();
}
@Test
public void fileWithMultipleSameBugMatchersAndMatchingErrorsSucceeds() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" public boolean doIt() {",
" // BUG: Diagnostic matches: X",
" return true;",
" }",
" public String doItAgain() {",
" // BUG: Diagnostic matches: X",
" return null;",
" }",
"}")
.expectErrorMessage("X", Predicates.containsPattern("Method may return normally"))
.doTest();
}
@Test
public void fileWithMultipleDifferentBugMatchersAndMatchingErrorsSucceeds() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" public boolean doIt() {",
" // BUG: Diagnostic matches: X",
" return true;",
" }",
" public String doItAgain() {",
" // BUG: Diagnostic matches: Y",
" return null;",
" }",
"}")
.expectErrorMessage("X", Predicates.containsPattern("Method may return normally"))
.expectErrorMessage("Y", Predicates.containsPattern("Method may return normally"))
.doTest();
}
@Test
public void fileWithSyntaxErrorFails() throws Exception {
try {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" // BUG: Diagnostic contains:",
" return}", // there's a syntax error on this line, but it shouldn't register as
// an error-prone diagnostic
"}")
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage())
.contains("Test program failed to compile with non Error Prone error");
}
}
@Test
public void expectedResultMatchesActualResultSucceeds() {
compilationHelper
.expectResult(Result.OK)
.addSourceLines(
"Test.java",
"public class Test {}")
.doTest();
}
@Test
public void expectedResultDiffersFromActualResultFails() {
try {
compilationHelper
.expectResult(Result.ERROR)
.addSourceLines(
"Test.java",
"public class Test {}")
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage()).contains("Expected compilation result ERROR, but was OK");
}
}
@Test
public void expectNoDiagnoticsAndNoDiagnosticsProducedSucceeds() {
compilationHelper
.expectNoDiagnostics()
.addSourceLines(
"Test.java",
"// BUG: Diagnostic contains:",
"public class Test {}")
.doTest();
}
@Test
public void expectNoDiagnoticsAndNoDiagnosticsProducedSucceedsWithMatches() {
compilationHelper
.expectNoDiagnostics()
.addSourceLines(
"Test.java",
"// BUG: Diagnostic matches: X",
"public class Test {}")
.expectErrorMessage("X", Predicates.containsPattern(""))
.doTest();
}
@Test
public void expectNoDiagnoticsButDiagnosticsProducedFails() {
try {
compilationHelper
.expectNoDiagnostics()
.addSourceLines(
"Test.java",
"public class Test {",
" public boolean doIt() {",
" // BUG: Diagnostic contains:",
" return true;",
" }",
"}")
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage()).contains("Expected no diagnostics produced, but found 1");
}
}
@Test
public void expectNoDiagnoticsButDiagnosticsProducedFailsWithMatches() {
try {
compilationHelper
.expectNoDiagnostics()
.addSourceLines(
"Test.java",
"public class Test {",
" public boolean doIt() {",
" // BUG: Diagnostic matches: X",
" return true;",
" }",
"}")
.expectErrorMessage("X", Predicates.containsPattern(""))
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage()).contains("Expected no diagnostics produced, but found 1");
}
}
@Test
public void commandLineArgToDisableCheckWorks() {
compilationHelper
.setArgs(ImmutableList.of("-Xep:ReturnTreeChecker:OFF"))
.expectNoDiagnostics()
.addSourceLines(
"Test.java",
"public class Test {",
" public boolean doIt() {",
" // BUG: Diagnostic contains:",
" return true;",
" }",
"}")
.doTest();
}
@Test
public void missingExpectErrorFails() {
try {
compilationHelper
.addSourceLines(
"Test.java",
" // BUG: Diagnostic matches: X",
"public class Test {}")
.doTest();
fail();
} catch(AssertionError expected) {
assertThat(expected.getMessage()).contains("No expected error message with key [X]");
}
}
@BugPattern(
name = "ReturnTreeChecker",
summary = "Method may return normally.",
explanation = "Consider mutating some global state instead.",
category = JDK,
severity = ERROR
)
public static class ReturnTreeChecker extends BugChecker implements ReturnTreeMatcher {
@Override
public Description matchReturn(ReturnTree tree, VisitorState state) {
return describeMatch(tree);
}
}
@Test
public void unexpectedDiagnosticOnFirstLine() {
try {
CompilationTestHelper.newInstance(PackageTreeChecker.class, getClass())
.addSourceLines("test/Test.java", "package test;", "public class Test {}")
.doTest();
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage()).contains("Package declaration found");
}
}
@BugPattern(
name = "PackageTreeChecker",
summary = "Package declaration found",
explanation = "Prefer to use the default package for everything.",
category = JDK,
severity = ERROR
)
public static class PackageTreeChecker extends BugChecker implements CompilationUnitTreeMatcher {
@Override
public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
if (tree.getPackage() != null) {
return describeMatch(tree.getPackage());
}
return Description.NO_MATCH;
}
}
}