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

8265828: [TestBug] Save and restore the default Locale in javafx.base unit test LocalDateTimeStringConverterTest #954

Closed
wants to merge 7 commits into from

Conversation

lukostyra
Copy link
Contributor

@lukostyra lukostyra commented Nov 17, 2022

The change moves Locale setting in the test to @BeforeClass and @AfterClass calls. @BeforeClass method call stores current default VM locale and applies Locale.US, while @AfterClass method restores old VM locale after all tests are completed.

I tested it both on Mac and Windows, in both cases Locale is changed, restored properly and tests pass.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8265828: [TestBug] Save and restore the default Locale in javafx.base unit test LocalDateTimeStringConverterTest

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx pull/954/head:pull/954
$ git checkout pull/954

Update a local copy of the PR:
$ git checkout pull/954
$ git pull https://git.openjdk.org/jfx pull/954/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 954

View PR using the GUI difftool:
$ git pr show -t 954

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/954.diff

@bridgekeeper
Copy link

bridgekeeper bot commented Nov 17, 2022

👋 Welcome back lukostyra! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk openjdk bot added the rfr Ready for review label Nov 18, 2022
@mlbridge
Copy link

mlbridge bot commented Nov 18, 2022

Webrevs

Copy link
Member

@kevinrushforth kevinrushforth left a comment

Choose a reason for hiding this comment

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

Looks good.

@@ -94,6 +93,17 @@ public LocalDateTimeStringConverterTest(LocalDateTimeStringConverter converter,
this.parser = parser;
}

@BeforeClass public static void setupBeforeAll() {
Copy link
Member

Choose a reason for hiding this comment

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

Minor: we usually put annotations on a separate line, although some files (like this one) put the @Test annotation on the same line, splitting them is preferred. I'll approve it as-is, and reapprove if you decide to change (I'll leave it up to you).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since I have to make some changes to this PR, I will update this as well.

@openjdk
Copy link

openjdk bot commented Nov 18, 2022

@lukostyra This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8265828: [TestBug] Save and restore the default Locale in javafx.base unit test LocalDateTimeStringConverterTest

Reviewed-by: kcr

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 2 new commits pushed to the master branch:

  • d040c1f: 8297680: JavaDoc example for PseudoClass has minor typo
  • 3376228: 8294809: ListenerHelper for managing and disconnecting listeners

Please see this link for an up-to-date comparison between the source branch of this pull request and the master branch.
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@kevinrushforth) but any other Committer may sponsor as well.

➡️ To flag this PR as ready for integration with the above commit message, type /integrate in a new comment. (Afterwards, your sponsor types /sponsor in a new comment to perform the integration).

@openjdk openjdk bot added the ready Ready to be integrated label Nov 18, 2022
@@ -56,11 +57,9 @@ public class LocalDateTimeStringConverterTest {

private static final DateTimeFormatter aFormatter = DateTimeFormatter.ofPattern("dd MM yyyy HH mm ss");
private static final DateTimeFormatter aParser = DateTimeFormatter.ofPattern("yyyy MM dd hh mm ss a");
private static Locale oldLocale;
Copy link
Member

@Maran23 Maran23 Nov 18, 2022

Choose a reason for hiding this comment

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

Isn't the creation of the DateTimeFormatter using the default locale? If so, this should probably be done after the locale is set.

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 a good point. Moving the initialization of those two fields to the setupBeforeAll method seems safest.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is a fair point.

I'll have to change the code a bit, as implementations() method is called before a @BeforeClass-tagged method (which is probably why originally Locale.setDefault() was called there) and aFormatter/aParser are already used there, expected to be initialized.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe JUnit5 can help you here.
You can check out my recently merged PR for an example for the usage of the parameterized api introduced in JUnit5.
#936

Copy link
Member

Choose a reason for hiding this comment

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

I guess they fixed this rather irritating "feature" in JUnit5. Another, possibly less-intrusive, approach would be to initialize all of the static fields in a static { ... } block (with a comment as to why you can't use an @BeforeClass method.

Copy link
Member

@kevinrushforth kevinrushforth left a comment

Choose a reason for hiding this comment

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

Pending response to comment raised by Marius.

@openjdk openjdk bot removed the ready Ready to be integrated label Nov 18, 2022
@@ -56,11 +57,9 @@ public class LocalDateTimeStringConverterTest {

private static final DateTimeFormatter aFormatter = DateTimeFormatter.ofPattern("dd MM yyyy HH mm ss");
private static final DateTimeFormatter aParser = DateTimeFormatter.ofPattern("yyyy MM dd hh mm ss a");
private static Locale oldLocale;
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder how many other tests we have that depend on specific Locale? Perhaps we need to apply the same treatment to:

  • LocalDateStringConverterTest
  • LocalTimeStringConverterTest

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could also change those, as they use DateTimeFormatter as well which uses Locale underneath as discussed above. @kevinrushforth what do you think?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it seems reasonable to include those tests as well, since those tests have the same problem.

Copy link
Member

@kevinrushforth kevinrushforth left a comment

Choose a reason for hiding this comment

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

see inline comments

@@ -56,11 +57,9 @@ public class LocalDateTimeStringConverterTest {

private static final DateTimeFormatter aFormatter = DateTimeFormatter.ofPattern("dd MM yyyy HH mm ss");
private static final DateTimeFormatter aParser = DateTimeFormatter.ofPattern("yyyy MM dd hh mm ss a");
private static Locale oldLocale;
Copy link
Member

Choose a reason for hiding this comment

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

I guess they fixed this rather irritating "feature" in JUnit5. Another, possibly less-intrusive, approach would be to initialize all of the static fields in a static { ... } block (with a comment as to why you can't use an @BeforeClass method.

@@ -56,11 +57,9 @@ public class LocalDateTimeStringConverterTest {

private static final DateTimeFormatter aFormatter = DateTimeFormatter.ofPattern("dd MM yyyy HH mm ss");
private static final DateTimeFormatter aParser = DateTimeFormatter.ofPattern("yyyy MM dd hh mm ss a");
private static Locale oldLocale;
Copy link
Member

Choose a reason for hiding this comment

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

Yes, it seems reasonable to include those tests as well, since those tests have the same problem.

…ocale

* Locale initialization was moved to @BeforeClass method.
* DateTimeFormatter objects are allocated after Locale initialization
* Since LocalDateTimeStringConverter depends on DateTimeFormatter and on VM's Locale,
  creation of it was moved to @before method.
Treatment done in this commit is similar to the previous change.
@lukostyra
Copy link
Contributor Author

After implementing CR fixes it turned out that these tests started to fail at random. Upon more investigation it turned out that the order in which JUnit calls test methods matters a lot. In general:

  1. @Parametrized.Parameters-tagged methods are grouped and called first in an undefined moment, before any test class is executed
  2. Then the order is more expected and predictable; for each test class JUnit calls: @BeforeClass, constructor, @Before, test, @After, ..., @AfterClass.

Additionally, Test Class order is selected at random.

That means that modifying a VM-wide setting like Locale cannot reside in @Parametrized.Parameters method or in static initializer block. When I added Locale change in all Local*StringConverterTest test classes, during step 1. one test captured machine's default Locale and changed it to en-US, while other tests captured previously set en-US Locale and set it again to en-US. Then JUnit progresses into step 2 and executes each test class, so once a test class completes, it might happen to be the one that captured machine's default Locale. It will revert Locale for other tests, effectively breaking them.

I had to refactor initialization code for these tests in order to resolve all problems and make them more independent. In short:

  • Locale change has to be done in @BeforeClass-tagged static method
  • Locale revert has to happen in @AfterClass-tagged static method
  • DateTimeFormatter instances have to be created after Locale is set, also in @BeforeClass method
  • Local*StringConverter classes that are checked by these tests have to be allocated afterwards, as they are also depending on set Locale and sometimes on DateTimeFormatter instances created earlier.
  • Test also acquired Format Locale for further use, so that part had to be moved after Locale setup as well.
  • As all these fields (like Local*StringConverter instance or current format Locale) are members of the test class, they must be setup somewhere after @BeforeClass method. The best spot I had for these is a @Before-tagged method.

The solution I made does not feel the most convenient but with above constraints it was difficult to come up with anything cleaner/less intrusive. This might also be a side-effect of me not knowing a mechanism in JUnit that could help us in such situation, so if there are any bits worth looking into I'd be happy to add them and simplify this change a bit.

@openjdk
Copy link

openjdk bot commented Nov 25, 2022

@lukostyra this pull request can not be integrated into master due to one or more merge conflicts. To resolve these merge conflicts and update this pull request you can run the following commands in the local repository for your personal fork:

git checkout JDK-8265828-locale
git fetch https://git.openjdk.org/jfx master
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge master"
git push

@openjdk openjdk bot added merge-conflict Pull request has merge conflict with target branch and removed rfr Ready for review labels Nov 25, 2022
@kevinrushforth
Copy link
Member

After implementing CR fixes it turned out that these tests started to fail at random. Upon more investigation it turned out that the order in which JUnit calls test methods matters a lot.

Thank you for getting to the bottom of this.

In general:

  1. @Parametrized.Parameters-tagged methods are grouped and called first in an undefined moment, before any test class is executed

This was the surprising find, and explains the odd problems you were seeing.

  1. Then the order is more expected and predictable; for each test class JUnit calls: @BeforeClass, constructor, @Before, test, @After, ..., @AfterClass.

Additionally, Test Class order is selected at random.

Right. This is documented behavior and is (or at least should be) well understood. It's why we are careful to avoid mutating static data or global state in a @Test method (at least not without restoring it in an @After method).

That means that modifying a VM-wide setting like Locale cannot reside in @Parametrized.Parameters method or in static initializer block.

Yes, this is the conclusion I would draw as well.

@openjdk openjdk bot removed the merge-conflict Pull request has merge conflict with target branch label Nov 28, 2022
@openjdk openjdk bot added the rfr Ready for review label Nov 28, 2022
Copy link
Member

@kevinrushforth kevinrushforth left a comment

Choose a reason for hiding this comment

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

The refactoring looks good to me. I left a few comments on one of the tests. Those comments apply to all of them.

Copy link
Member

@kevinrushforth kevinrushforth left a comment

Choose a reason for hiding this comment

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

Looks good. You can remove the now-unused import of java.security.InvalidParameterException (I'll reapprove when you do).

@openjdk openjdk bot added the ready Ready to be integrated label Nov 29, 2022
@openjdk openjdk bot removed the ready Ready to be integrated label Nov 29, 2022
@openjdk openjdk bot added the ready Ready to be integrated label Nov 29, 2022
@lukostyra
Copy link
Contributor Author

/integrate

@openjdk openjdk bot added the sponsor Ready to sponsor label Nov 29, 2022
@openjdk
Copy link

openjdk bot commented Nov 29, 2022

@lukostyra
Your change (at version f0fa376) is now ready to be sponsored by a Committer.

@kevinrushforth
Copy link
Member

/sponsor

@openjdk
Copy link

openjdk bot commented Nov 29, 2022

Going to push as commit 4ad8582.
Since your change was applied there have been 2 commits pushed to the master branch:

  • d040c1f: 8297680: JavaDoc example for PseudoClass has minor typo
  • 3376228: 8294809: ListenerHelper for managing and disconnecting listeners

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Nov 29, 2022
@openjdk openjdk bot closed this Nov 29, 2022
@openjdk openjdk bot removed ready Ready to be integrated rfr Ready for review sponsor Ready to sponsor labels Nov 29, 2022
@openjdk
Copy link

openjdk bot commented Nov 29, 2022

@kevinrushforth @lukostyra Pushed as commit 4ad8582.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

@lukostyra lukostyra deleted the JDK-8265828-locale branch November 29, 2022 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integrated Pull request has been integrated
Development

Successfully merging this pull request may close these issues.

4 participants