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

[PiranhaJava] Remove configured test methods #151

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

tnterdan
Copy link
Contributor

Fixes #47

This development allows the user of Piranha to specify a list of methods
where if a dynamic flag evaluation is in a certain argument position, we
remove that method.

This is useful for cleaning up mock() wrapper methods or assert()
methods in test code that are useless after a feature flag is removed.

Limitations:

  • This does not remove unused imports for the test methods because we
    have to account for cases of the test methods that will not be
    removed.

This development allows the user of Piranha to specify a list of methods
where if a dynamic flag evaluation is in a certain argument position, we
remove that method.

This is useful for cleaning up `mock()` wrapper methods or `assert()`
methods in test code that are useless after a feature flag is removed.

Limitations:
  - This does not remove unused imports for the test methods because we
    have to account for cases of the test methods that will not be
    removed.
Copy link
Contributor

@mkr-plse mkr-plse left a comment

Choose a reason for hiding this comment

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

Overall, looks good. A minor refactoring as suggested in the comment will be helpful.

java/README.md Outdated
An optional top-level field is `enumProperties`.
An optional top-level field is `testMethodProperties`.

Within that, there is an array of JSON objects, having the required fields `methodName` and `argumentIndex`. The both behave the same as the fields with the same name in `methodProperties`.
Copy link
Contributor

Choose a reason for hiding this comment

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

The -> They


Within that, there is an array of JSON objects, having the required fields `methodName` and `argumentIndex`. The both behave the same as the fields with the same name in `methodProperties`.

What this field does, is that if we find one of the `methodProperties` fields inside a method that matches one of the methods in `testMethodProperties`, we remove that method. This is useful for removing `mock()` wrappers or `assert()` calls that are no longer useful after a flag is cleaned up.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we refer this to as testMethodWrappers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should we keep the pattern so they all still end in properties? testMethodWrapperProperties? Since it behaves so similarly to methodProperties.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am not even sure that testMethodWrapperProperties is correct. I wonder if there is a name here that can make it clear at a glance that we mean "Methods from testing/assertion libraries" as opposed to "Methods used to test configuration flags specifically", and that is also not insanely long... if no such name exists, I prefer testMethodProperties than testMethodWrapperProperties...

Perhaps testingLibraryMethodProperties?

@@ -833,4 +833,196 @@ public void testCleanBySettersHeuristicIgnoreSettersForOtherFlags() throws IOExc
"}")
.doTest();
}

Copy link
Contributor

Choose a reason for hiding this comment

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

nice set of tests.

static PiranhaFlagMethodRecord parseFromJSONPropertyEntryMap(
Map<String, Object> methodPropertyEntry, boolean isArgumentIndexOptional)
throws PiranhaConfigurationException {
String methodName = getValueStringFromMap(methodPropertyEntry, METHOD_NAME_KEY);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can code from line 111 to 132 be hoisted into a utility method and called. The same method can be called from PiranhaTestMethodRecord too. The messaging in the throw and the check on flagType seem to be the only differences between the two methods.

}
}
} else {
for (ExpressionTree argTree : methodTree.getArguments()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

does this mean the flagTest API can occur at any index?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but only if the argument index is not defined.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is consistent with other configuration options. Though, if I recall, the index-less config was mostly allowed for backwards compatibility. Maybe it should be index-required for configuration options going forward?

@lazaroclapp
Copy link
Contributor

Limitations:

  • This does not remove unused imports for the test methods because we
    have to account for cases of the test methods that will not be
    removed.

Arguably better in a follow-up, but this sounds like the use case for UsageCounter, which I believe we use in similar cases. Not 100% sure because I haven't yet reviewed the code (starting on it right now :) )

Copy link
Contributor

@lazaroclapp lazaroclapp left a comment

Choose a reason for hiding this comment

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

A couple very minor nits. Overall this looks good to me. My biggest concern is that the test methods in question tend to have very generic names and wether or not it might make sense in this case to also try to match on declaring class name.

@@ -106,7 +113,13 @@ public void some_unit_test() { ... }

when `IsTreated` is `true`, and will be deleted completely when `IsTreated` is `false`.

An optional top-level field is `enumProperties`.
An optional top-level field is `testMethodProperties`.

Copy link
Contributor

Choose a reason for hiding this comment

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

Either no new line here, or add a new line on the other similar property "headers" above and below.


Within that, there is an array of JSON objects, having the required fields `methodName` and `argumentIndex`. The both behave the same as the fields with the same name in `methodProperties`.

What this field does, is that if we find one of the `methodProperties` fields inside a method that matches one of the methods in `testMethodProperties`, we remove that method. This is useful for removing `mock()` wrappers or `assert()` calls that are no longer useful after a flag is cleaned up.
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not even sure that testMethodWrapperProperties is correct. I wonder if there is a name here that can make it clear at a glance that we mean "Methods from testing/assertion libraries" as opposed to "Methods used to test configuration flags specifically", and that is also not insanely long... if no such name exists, I prefer testMethodProperties than testMethodWrapperProperties...

Perhaps testingLibraryMethodProperties?

if (tree.getExpression().getKind().equals(Kind.METHOD_INVOCATION)) {
MethodInvocationTree mit = (MethodInvocationTree) tree.getExpression();
ExpressionTree receiver = ASTHelpers.getReceiver(mit);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this logic external to the matchTestMethod, rather than part of it? Why not unconditionally pass mit and check if you can get a receiver within the more specific method? Do we use receiver for something else in the caller?

}
}
} else {
for (ExpressionTree argTree : methodTree.getArguments()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is consistent with other configuration options. Though, if I recall, the index-less config was mostly allowed for backwards compatibility. Maybe it should be index-required for configuration options going forward?

* configMethodsMap is a map where key is method name and value is a list where each item in the
* list is a map that corresponds to each method property from properties.json. In most cases, the
* list would have only one element. But if someone reuses the same method name with different
* configMethodProperties is a map where key is method name and value is a list where each item in
Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch! 👍

@@ -38,6 +38,20 @@
"argumentIndex": 0
}
],
"testMethodProperties": [
Copy link
Contributor

Choose a reason for hiding this comment

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

One thing I wonder about test library methods vs flag-configuration-system methods, is that they tend to have fairly generic names (like accept). I am wondering if in a large codebase, we might not need to match class name (i.e. FQN of the receiver), in order to avoid false positives here.

Specially if this is going to ship as the default/base config file for Piranha.

"import org.junit.Test;",
"import static com.uber.piranha.TestExperimentName.OTHER_FLAG_1;",
"import static com.uber.piranha.TestExperimentName.STALE_FLAG;",
"import static com.uber.piranha.TestMethods.expect;",
Copy link
Contributor

Choose a reason for hiding this comment

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

Obvious thing about Java imports that I just thought about seeing this: static imports can actually import a set of methods, rather than a single method, because they match on method name and not method signature. TIL.

(Not a comment on this PR at all, just something I can't believe I just realized... wondering about implications for dead-code removal, etc)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PiranhaJava // Remove meaningless "assert" and "expect" from JUnits
3 participants