-
-
Notifications
You must be signed in to change notification settings - Fork 355
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
review: refactor: Update EnumsTest to JUnit 5 #4138
Conversation
6f25216
to
635ae55
Compare
635ae55
to
65d5a43
Compare
@monperrus @slarse do you know more about the visibility situation? In my opinion that's currently a bug, but as it was explicitly added in #1999 I'm not sure if I'm missing something here. |
No. If there is no info in the JLS, the compiler is the spec (try different combinations modifiers/access and see if it compiles). |
The problem is that the modifiers are implicit. Explicit modifiers are not allowed at all, but the produced bytecode marks the fields with |
Thanks for the analysis. Let's fix the bug then. |
Enum constants are syntactical sugar, they don't semantically have visibility themselves. The fields derived from them do, however. JLS says the following about those fields (from section 8.9.2 of JLS SE17):
So, |
As This refactoring somehow ended up in bug fixing, I hope that's okay. The SignatureBasedSortedSet still makes me thinking, so I'm happy about any input for that too. |
Woops, this has been left unattended for a while now.
This is a little bit of a problem. Making non-trivial changes to test and production code in a single PR, beyond what is strictly necessary, is something of a no-no. Would it be much effort for you to split this into one PR that actually just updates the test to JUnit5, and one PR that fixes the modifier bug? That would be much preferable from a reviewer perspective.
I agree that it violates the set contract. What to do about that should be discussed in a separate issue, and is (as far as I can understand) orthogonal to updating this class to use JUnit5. For the sake of explicitness: It violates the set contract by imposing an ordering (with the custom comparator) that is not consistent with equals (i.e. it does not satisfy " |
No, that should be doable, When updating the tests first, they will remain wrong (as they test something that is wrong), which feels a bit weird. Same basically applies for the wrong So it's kind of a chicken and egg problem... Edit: I thought I could extract the issue with the JavaReflectionTreeBuilder, but those are basically two different issues too:
Both make the same test fail because the test compares the reflection model with the jdt model which is wrong too... |
Yes, but it's not the concern of a PR meant to update the test framework to also fix bugs. It's very common to find semantic problems when refactoring code (this is a refactoring of the test suite), and the "proper" way to proceed then is to document those bugs and continue with the refactoring. Take the reviewer's perspective. If a PR migrates a test class from JUnit4 to JUnit5, the only thing the reviewer needs to do is to verify that the semantics of the tests are unchanged. But if the PR also changes the semantics of the test case (on purpose), then the reviewer needs to verify that the semantics of the tests are unchanged except for where they purposefully have been changed, and also check that the production code is updated correctly. It becomes very easy to make a mistake as a reviewer. To summarize, when refactoring and you find bugs: document the bugs, complete the refactoring, then fix the bugs separately :) |
That makes sense, I'll do so. |
76e3e8b
to
b10a5d6
Compare
I removed the fixes and commented the affected test code accordingly. Should I open issues for the bugs or should I just open PRs for them after this is merged? |
Thanks @SirYwell, I'll have a look at this tonight
There's nothing in the contributing guidelines saying an issue must exist for a PR, so I think you can do it whichever way you feel like. If it's a straightforward bugfix that doesn't require any particular discussion about how to fix it, I think it's fine to just open a PR without having an issue. For complex stuff I personally prefer to always open an issue first. |
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.
Okay, had a look now.
I would not say that this PR is an update of EnumsTest
to JUnit5, such a PR would be small. This is rather a very significant overhaul of the entire test class. I think it's a bit much, especially as most of the test changes aren't related to each other.
For example, when just considering updating to JUnit5, changing an assertEquals
to an assertThat
is not in any way necessary. Especially seeing as Hamcrest matchers are available for JUnit4 as well, it's not even related.
Let's continue the PR with it's current scope, most changes are for the better (see my comments on the things I don't think are improvements). But for future reference, this is a bit too many too unrelated things at the same time.
|
||
@Override | ||
public String toString() { | ||
return "CtExtendedModifier{" | ||
+ "implicit=" + implicit | ||
+ ", kind=" + kind + '}'; | ||
} |
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.
This is a production code change, it should not be necessary to migrate the tests to JUnit5. Where exactly is this utilized?
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.
Failing tests where modifiers don't match were hard (impossible) to read. I can move this change to the bug fixing PR too, there it would be more related probably.
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.
Right, I see. That's a fine enough justification for that.
if (visibility != null) { | ||
assertThat(type.getField("VALUE").getExtendedModifiers(), contentEquals( | ||
// TODO this is wrong, the field should be public | ||
new CtExtendedModifier(visibility.getKind(), true), | ||
new CtExtendedModifier(ModifierKind.STATIC, true), | ||
new CtExtendedModifier(ModifierKind.FINAL, true) | ||
)); | ||
} else { | ||
assertThat(type.getField("VALUE").getExtendedModifiers(), contentEquals( | ||
// TODO this is wrong, the field should be public (and then the if else, | ||
// the visibility param and the Streams#zip can be removed) | ||
new CtExtendedModifier(ModifierKind.STATIC, true), | ||
new CtExtendedModifier(ModifierKind.FINAL, true) | ||
)); |
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 don't think this is an improvement over the original test. While the original has the problem that it's 4 tests in one, it's very easy to read. The refactored version has 2 mutually exclusive branching paths (in general, avoid branches in tests) and also requires one to understand the non-trivial parameterization.
I favor the original test, it's much easier to grasp. I'm guessing you have a plan here for when you fix the modifier bug, but that's not a concern of this PR. Could you revert to the original? Feel free to reintroduce the parameterized test if it actually simplifies things in a future PR.
// this does not work due to the SignatureBasedSortedSet violating the Set contract | ||
// assertThat(burritos.getAllMethods(), hasItem(name)); |
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.
This comment is noise for this test, and it's very unlikely to be removed if SignatureBasedSortedSet
is fixed. I'd remove it.
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.
Thanks @SirYwell, this looks good to merge for me.
It's great that you're giving the test suite some much needed attention, such things tend to be neglected in favor of working on production code :)
As I and @I-Al-Istannen were looking into issues with implicit modifiers recently, I decided to update this test class as it contains several tests which I consider problematic and might need changes in near future.
I tried to infer the contract for the tests which didn't have one before. I hope I got it right more or less.
Now to the problems I encountered:
testNestedPrivateEnumValues
The enums are currently not marked as
final
. In my opinion, they should have an implicitfinal
modifier, as the JLS $ 8.9 says:The second part of that sentence brings us to a second issue that is not covered for now. It is also related to #4129 (see how the implicit check adds
()
even if they don't exist in the original code because of the constant being an anonymous class). I also added TODOs for the other tests that expect specific printer output.Another issue is the visibility of the enum constants. The test currently expects them to have the same visibility modifiers as their class. This was introduced in #1999 but I wasn't able to reproduce that but I also didn't find any more specific information in the JLS. It might be a version specific implementation detail, but for examle - with Java 16 - looking at the output of `javap -v NestedEnums$ProtectedENUM.class` (after compiling the `NestedEnum` class with `javac` shows the following for the VALUE constant:
(while the test expects it to be protected)
In foresight
Java 17 will introduce sealed classes. While changes are still possible there, there is a very interesting point that makes more sense as more as I think about it: Enums are still
final
if no constant has a body, but becomesealed
once a constant has a body. The anonymous class represented with that body is additionallyfinal
. The EnumsTest class likely needs to be updated once that feature is supported, with version-specific tests for that.Edit
The
testGetAllMethods
test revealed that theSignatureBasedSortedSet
basically violates the contract ofSet
. Thecontains
method returns true while iterating and doingequals
checks returns false eventually:spoon/src/test/java/spoon/test/enums/EnumsTest.java
Lines 111 to 113 in 6f25216