Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce BugPatternTestExtractor with tests #494

Merged
merged 23 commits into from
Oct 24, 2023
Merged

Conversation

rickie
Copy link
Member

@rickie rickie commented Feb 8, 2023

This is draft PR on top of #428

It looks like a huge PR but it consists mostly of tests and associated resources!

There are some open points that we should discuss before we mark this as 100% ready for review:

  • We should have a way to make our utils more widely available, instead of having them in error-prone-contrib. It would be nice to be able to use the SourceCode util in documentation-support such that we can de-duplicate code (as well as an Matcher that lives in our utils). Let's discuss how to approach this.
  • The heuristics used for determining what a BugPattern test is are improved w.r.t. the initial implementation. However, we can likely discuss further improvements. Still, the idea of the (internal Jira) ticket for this was to get this working in a better way than we have currently on the website branch. If we want to further improve the heuristics, we can discuss that maybe in a follow up PR. I also added an XXX for this.
  • The current extraction approach of the BugPatternExtractor#extract is not as optimal as it can be. However, again, the goal of this ticket is not to implement the most optimal solution now. I added some XXXs to indicate where we can further improve.

Let's discuss this and hopefully continue with this approach.

@rickie rickie added the documentation A documentation update label Feb 8, 2023
@github-actions
Copy link

github-actions bot commented Feb 8, 2023

  • Surviving mutants in this change: 5
  • Killed mutants in this change: 32
class surviving killed
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor 2 12
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$CollectBugPatternTests 2 11
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$ScanBugPatternTest 1 4
🎉tech.picnic.errorprone.documentation.BugPatternTestExtractor$BugPatternReplacementTestDocumentation 0 2
🎉tech.picnic.errorprone.documentation.DocumentationType 0 3

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Member Author

@rickie rickie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some context.

Added a commit that further simplifies the tests and fixes some formatting. Our check doesn't like the extra level of meta-ness in the tests 😋.

return scanBugPatternTest.hasTestUsingClassInstance(bugPatternName);
}

private static final class ScanBugPatternTest extends TreeScanner<@Nullable Void, VisitorState> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of this Scanner can probably be improved.... The same holds for the encounteredClasses and hasTestUsingClassInstance.... Idk why, but really struggled with these names 🤔. Definitely open to suggestions 😄.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I this we can side-step this issue; will propose something.


@AutoValue
abstract static class BugPatternReplacementTestDocumentation {
static BugPatternReplacementTestDocumentation create(String sourceLines, String outputLines) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't find a way around this, the way we instantiate the BugPatternTestDocumentation earlier on wasn't possible with this class...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inlining it works for me 👀

"import com.google.errorprone.CompilationTestHelper;",
"",
"final class IdentityConversionTest {",
" private static class IdentityConversion extends BugChecker {}",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Came up with this way to make testing a little easier... It's hard to make sure that the IdentityConversion is available without having to do an import. I figured this was "closest" to our actual testing setup...

@rickie rickie marked this pull request as draft February 8, 2023 13:47
@github-actions
Copy link

github-actions bot commented Feb 8, 2023

  • Surviving mutants in this change: 5
  • Killed mutants in this change: 32
class surviving killed
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor 2 12
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$CollectBugPatternTests 2 11
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$ScanBugPatternTest 1 4
🎉tech.picnic.errorprone.documentation.BugPatternTestExtractor$BugPatternReplacementTestDocumentation 0 2
🎉tech.picnic.errorprone.documentation.DocumentationType 0 3

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Base automatically changed from rossendrijver/PSM-1716 to master February 13, 2023 08:27
@rickie
Copy link
Member Author

rickie commented Feb 13, 2023

Applied the feedback @Stephan202 gave offline. It's ready for review 😄.

@rickie rickie marked this pull request as ready for review February 13, 2023 12:16
@github-actions
Copy link

  • Surviving mutants in this change: 5
  • Killed mutants in this change: 32
class surviving killed
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor 2 12
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$CollectBugPatternTests 2 11
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$ScanBugPatternTest 1 4
🎉tech.picnic.errorprone.documentation.BugPatternTestExtractor$BugPatternReplacementTestDocumentation 0 2
🎉tech.picnic.errorprone.documentation.ExtractorType 0 3

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

}

// XXX: Duplicate from `ErrorProneTestSourceFormat`, should we move this to `SourceCode` util?
private static String getSourceLines(MethodInvocationTree tree) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we move this to SourceCode util?

Didn't apply something like this yet, to not introduce a dependency on error-prone-contrib. I think we should have a more generic "utilities" module.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this logic is very specific to the {BugCheckerRefactoring,Compilation}TestHelper APIs, so moving it to SourceCode feels out of place. I guess duplicating this isn't so bad. Or perhaps we need "something" specific to Error Prone testing. 🤔

@github-actions
Copy link

  • Surviving mutants in this change: 5
  • Killed mutants in this change: 32
class surviving killed
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor 2 12
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$CollectBugPatternTests 2 11
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$ScanBugPatternTest 1 4
🎉tech.picnic.errorprone.documentation.BugPatternTestExtractor$BugPatternReplacementTestDocumentation 0 2
🎉tech.picnic.errorprone.documentation.ExtractorType 0 3

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie rickie added this to the 0.10.0 milestone Feb 20, 2023
@github-actions
Copy link

  • Surviving mutants in this change: 5
  • Killed mutants in this change: 32
class surviving killed
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor 2 12
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$CollectBugPatternTests 2 11
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$ScanBugPatternTest 1 4
🎉tech.picnic.errorprone.documentation.BugPatternTestExtractor$BugPatternReplacementTestDocumentation 0 2
🎉tech.picnic.errorprone.documentation.ExtractorType 0 3

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@github-actions
Copy link

  • Surviving mutants in this change: 4
  • Killed mutants in this change: 33
class surviving killed
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$CollectBugPatternTests 2 11
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$ScanBugPatternTest 1 4
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor 1 13
🎉tech.picnic.errorprone.documentation.BugPatternTestExtractor$BugPatternReplacementTestDocumentation 0 2
🎉tech.picnic.errorprone.documentation.ExtractorType 0 3

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@github-actions
Copy link

  • Surviving mutants in this change: 4
  • Killed mutants in this change: 33
class surviving killed
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$CollectBugPatternTests 2 11
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$ScanBugPatternTest 1 4
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor 1 13
🎉tech.picnic.errorprone.documentation.BugPatternTestExtractor$BugPatternReplacementTestDocumentation 0 2
🎉tech.picnic.errorprone.documentation.ExtractorType 0 3

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased and added a commit. This was a partial review; more at another moment. Nice feature!


@AutoValue
abstract static class BugPatternReplacementTestDocumentation {
static BugPatternReplacementTestDocumentation create(String sourceLines, String outputLines) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inlining it works for me 👀

StringBuilder source = new StringBuilder();

for (ExpressionTree sourceLine : sourceLines) {
Object value = ASTHelpers.constValue(sourceLine);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Object value = ASTHelpers.constValue(sourceLine);
String value = ASTHelpers.constValue(sourceLine, String.class);

Has no functional impact, but arguably makes for clearer code. Will also change the other code.

}

// XXX: Duplicate from `ErrorProneTestSourceFormat`, should we move this to `SourceCode` util?
private static String getSourceLines(MethodInvocationTree tree) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this logic is very specific to the {BugCheckerRefactoring,Compilation}TestHelper APIs, so moving it to SourceCode feels out of place. I guess duplicating this isn't so bad. Or perhaps we need "something" specific to Error Prone testing. 🤔

for (ExpressionTree sourceLine : sourceLines) {
Object value = ASTHelpers.constValue(sourceLine);
if (value == null) {
return "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't add the empty string to the result collection.

}

// XXX: Duplicate from `ErrorProneTestSourceFormat`, should we move this to `SourceCode` util?
private static String getSourceLines(MethodInvocationTree tree) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private static String getSourceLines(MethodInvocationTree tree) {
private static String getSourceCode(MethodInvocationTree tree) {

(That the input is represented as a sequence of lines is an implementation detail.)

"outputLines": "class A {}\n"
}
]
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀 Newline missing.

Comment on lines 4 to 14
"replacementTests": [
{
"inputLines": "class A {}\n",
"outputLines": ""
}
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not an accurate representation of expectUnchanged(): either input and output should be the same, or we should omit the test case. From a documentation POV omitting makes more sense, I suppose. (An alternative is to express such a test as an identification test. Which then raises the further question: should we perhaps have a separate category of "unchanged code examples" for source code that doesn't contain a // BUG: Diagnostic (contains|matches) marker?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My current thinking leans towards leaving the unchanged examples out.

private final List<String> identificationTests = new ArrayList<>();
private final List<BugPatternReplacementTestDocumentation> replacementTests = new ArrayList<>();

@Var private String replacementOutputLines = "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stateful solution makes the code more brittle than necessary; will propose an alternative.

.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
.named("addOutputLines");

private final VisitorState state;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be passed around instead of one of the Void params. (Like for ScanBugPatternTest.)

@Override
public @Nullable Void visitMethodInvocation(MethodInvocationTree node, VisitorState state) {
if (BUG_PATTERN_TEST_METHOD.matches(node, state)) {
MemberSelectTree firstArgumentTree = (MemberSelectTree) node.getArguments().get(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While not likely in our code base, this could cause a ClassCastException. Can be fixed by augmenting the Matcher.

@github-actions
Copy link

  • Surviving mutants in this change: 10
  • Killed mutants in this change: 33
class surviving killed
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$CollectBugPatternTests 4 12
🧟tech.picnic.errorprone.documentation.DocumentationGeneratorTaskListener 4 3
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$ScanBugPatternTest 1 4
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor 1 11
🎉tech.picnic.errorprone.documentation.ExtractorType 0 3

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased and added a commit. More later.


String className = tree.getSimpleName().toString();
return new AutoValue_BugPatternTestExtractor_BugPatternTestDocumentation(
className.substring(0, className.lastIndexOf("Test")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is duplicated below 👀

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In part this is due to how Extractor is defined. Perhaps we should combine the two methods into a single Optional-returning method. That mirrors approaches we take elsewhere.

return scanBugPatternTest.hasTestUsingClassInstance(bugPatternName);
}

private static final class ScanBugPatternTest extends TreeScanner<@Nullable Void, VisitorState> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I this we can side-step this issue; will propose something.

Comment on lines 46 to 61
for (Tree m : tree.getMembers()) {
if (JUNIT_TEST_METHOD.matches(m, state)) {
scanner.scan(m, state);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We make a number of assumptions in this extractor class:

  1. FooBugPattern is tested (only) in a class names FooBugPatternTest.
  2. All CompilationTestHelpers and BugCheckerRefactoringTestHelper inside FooBugPatternTest exercise FooBugPattern.

This assumptions are certainly valid for our code, but since {Compilation,BugCheckerRefactoring}TestHelper explicitly mention which bug pattern they exercise, we shouldn't need to make these assumptions. Generalization may require a change to the way in which we persist the resultant JSON, though.

@github-actions
Copy link

github-actions bot commented Mar 3, 2023

  • Surviving mutants in this change: 11
  • Killed mutants in this change: 31
class surviving killed
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$CollectBugPatternTests 4 12
🧟tech.picnic.errorprone.documentation.DocumentationGeneratorTaskListener 4 3
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor 2 10
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$1 1 3
🎉tech.picnic.errorprone.documentation.ExtractorType 0 3

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie
Copy link
Member Author

rickie commented Oct 24, 2023

Will rebase and merge afterwards 😄!

@sonarcloud
Copy link

sonarcloud bot commented Oct 24, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

98.3% 98.3% Coverage
0.0% 0.0% Duplication

@github-actions
Copy link

  • Surviving mutants in this change: 4
  • Killed mutants in this change: 64
class surviving killed
🧟tech.picnic.errorprone.documentation.DocumentationGeneratorTaskListener 3 8
🧟tech.picnic.errorprone.documentation.BugPatternTestExtractor$BugPatternTestCollector 1 43
🎉tech.picnic.errorprone.documentation.BugPatternExtractor 0 8
🎉tech.picnic.errorprone.documentation.BugPatternTestExtractor 0 4
🎉tech.picnic.errorprone.bugpatterns.ErrorProneTestHelperSourceFormat 0 1

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie rickie merged commit 01b30d7 into master Oct 24, 2023
17 checks passed
@rickie rickie deleted the rossendrijver/PSM-1717 branch October 24, 2023 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation A documentation update
Development

Successfully merging this pull request may close these issues.

5 participants