-
Notifications
You must be signed in to change notification settings - Fork 201
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
base: master
Are you sure you want to change the base?
Conversation
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.
There was a problem hiding this 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`. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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(); | |||
} | |||
|
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
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 :) ) |
There was a problem hiding this 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`. | |||
|
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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": [ |
There was a problem hiding this comment.
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;", |
There was a problem hiding this comment.
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)
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 orassert()
methods in test code that are useless after a feature flag is removed.
Limitations:
have to account for cases of the test methods that will not be
removed.