Skip to content
This repository has been archived by the owner on Mar 5, 2023. It is now read-only.

HelpWindowTest: fix failing test in non-headless mode #880

Merged
merged 3 commits into from
Apr 24, 2018

Conversation

yamidark
Copy link
Contributor

HelpWindowTest#focus_helpWindowNotFocused_focused() test checks that
the HelpWindow#focus() method will cause the HelpWindow to be in focus
when called. The test first asserts that the HelpWindow is not in focus
first after it is shown, being calling this method and asserting that
the HelpWindow is now in focus.

When tests are run in non-headless mode, the HelpWindow will be in
focus immediately after it is shown, thus our first assertion is
incorrect, causing the test to consistently fail in non-headless mode.

Let's add a method GuiRobot#removeFocus(), and update the test to call
this method to remove focus from the HelpWindow after it is shown
to ensure the first assertion is correct in non-headless mode.

@CanIHasReview-bot
Copy link

v1

@yamidark submitted v1 for review.

(📚 Archive)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/880/1/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

@yamidark
Copy link
Contributor Author

@vivekscl @Rinder5 for your review.

Copy link
Contributor

@vivekscl vivekscl left a comment

Choose a reason for hiding this comment

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

[1/1] Commit Message
1st para:
HelpWindowTest#focus_helpWindowNotFocused_focused() test checks that
the HelpWindow#focus() method will cause the HelpWindow to be in focus
when called. The test first
asserts that the HelpWindow is not in focus
first after when it is first shown being calling this method and asserting that and then asserts that
the HelpWindow comes into focus after calling HelpWindow#focus().

2nd para:
When tests are run in non-headless mode, the HelpWindow will be in
focus immediately after when it is first shown, thus rendering our initial assumption is
false and causing the test to consistently fail in non-headless mode fail.

3rd para:
Let's add a method GuiRobot#removeFocus(), and update the HelpWindowTest#focus_helpWindowNotFocused_focused() to call
this method to remove focus from the HelpWindow after when it is first shown,
to ensuring that the initial assumption is true.

* Removes focus from the current stage.
*/
public void removeFocus() throws Exception {
FxToolkit.setupStage(dummyStage -> dummyStage.requestFocus());
Copy link
Contributor

Choose a reason for hiding this comment

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

Should there be a try-catch block instead of simply throwing the exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, do you mean something like what UiPartRule does:

catch (TimeoutException te) {
       throw new AssertionError("Timeout should not happen.", te);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup!

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 will be inling the method directly into the test, should we still do a try-catch block in the test?

@yamidark
Copy link
Contributor Author

yamidark commented Apr 17, 2018

Actually, should I add in the comments that the removeFocus() does not work in headless mode due to an issue with Monocle, and make the test method return immediately if in headless mode?

@@ -39,6 +40,13 @@ public void pauseForHuman() {
sleep(PAUSE_FOR_HUMAN_DELAY_MILLISECONDS);
}

/**
* Removes focus from the current stage.
Copy link
Contributor

Choose a reason for hiding this comment

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

A more accurate description would be "focus on the registered TestFX stage". If the "current stage" is the registered TestFX stage, then this won't "remove focus" from it.

With that said, I think this is too oddly specific to be inside GuiRobot. We don't usually "remove focus", we just focus on another window instead.

Just inline this directly in the test?

@pyokagan
Copy link
Contributor

@yamidark

Actually, should I add in the comments that the removeFocus() does not work in headless mode due to an issue with Monocle, and make the test method return immediately if in headless mode?

Actually, I'm not sure why the test is even run in headless mode? For headless mode to be useful its results need to match the real-world head-full mode, otherwise its test results are meaningless since the fact that a test passes/fails in headless mode does not mean that it would pass/fail in the real-world.

If Monocle's window focusing behavior does not match the "real world" then there is no point trying to run focus tests with it. Better to be explicit about that by printing a gigantic "TEST SKIPPED IN HEADLESS MODE, RUN IT IN HEAD-FULL MODE INSTEAD" message.

@pyokagan
Copy link
Contributor

If Monocle's window focusing behavior does not match the "real world" then there is no point trying to run focus tests with it.

Actually I take that back. There's still some value in the test if it fails if requestFocus is not called for whatever reason.

@yamidark yamidark force-pushed the fix-help-window-failing-test branch 3 times, most recently from ccaeedb to 11e7fcb Compare April 17, 2018 17:27
@pyokagan
Copy link
Contributor

@yamidark

Sorry, I re-assert again that the test should not be run in headless mode :-). Its relying on undefined behavior, and I think that is part of the reason why HelpWindowTest is stalling our builds.

Consider this: (Make sure to run it in headless mode)

package seedu.address.ui;

import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.testfx.api.FxRobot;
import org.testfx.api.FxToolkit;

import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class HelpWindowTest {
    Stage stage1;
    Stage stage2;

    @Test
    public void test() throws Exception {
        FxRobot fxRobot = new FxRobot();
        FxToolkit.registerPrimaryStage();

        fxRobot.interact(() -> {
            stage1 = new Stage();
            stage1.setScene(new Scene(new Pane()));

            stage2 = new Stage();
            stage2.setScene(new Scene(new Pane()));
        });

        fxRobot.interact(stage1::show);
        assertTrue(stage1.isFocused()); // NOTE: stage1 is focused once it is shown!!!
    }
}

Note that upon calling stage1::show, stage1 is immediately focused.

Now, consider the following case where stage1 and stage2 share the same scene:

package seedu.address.ui;

import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.testfx.api.FxRobot;
import org.testfx.api.FxToolkit;

import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class HelpWindowTest {
    Stage stage1;
    Stage stage2;

    @Test
    public void test() throws Exception {
        FxRobot fxRobot = new FxRobot();
        FxToolkit.registerPrimaryStage();

        fxRobot.interact(() -> {
            Scene scene = new Scene(new Pane());

            stage1 = new Stage();
            stage1.setScene(scene);

            stage2 = new Stage();
            stage2.setScene(scene);
        });

        fxRobot.interact(stage1::show);
        assertTrue(stage1.isFocused()); // ASSERTION FAILURE!!!!
    }
}

stage1 is now not automatically focused when it is shown.

So, on normal operation in headless mode stages are supposed to be focused automatically when they are shown. But when stages share a Scene (which the JavaFX docs specifically say not to), then the focus does not occur.

I think this is exactly what is happening in HelpWindowTest. Specifically this:

Stage helpWindowStage = FxToolkit.setupStage((stage) -> stage.setScene(helpWindow.getRoot().getScene()));

I also believe that this is tied to HelpWindowTest randomly stalling our builds, since from my investigation it stalls due to a deadlock between the Application thread and the renderer thread because the Application thread miscounts the number of scenes.

@yamidark
Copy link
Contributor Author

yamidark commented Apr 18, 2018

@pyokagan Should I also make the other tests in HelpWindowTest also skip in headless mode?

Edit*:
Actually, since only the display test requires setting up of a helpWindowHandle thus needing a new helpWindowStage with the same Scene set up, we can just move the setting up of helpWindowHandle in display test alone.

Now, in focus_helpWindowNotFocused_focused test, the helpWindow will correctly be in focus after it is shown.
However, we still can't remove focus from it in headless mode, so we would still need to skip this test.

@yamidark yamidark force-pushed the fix-help-window-failing-test branch 3 times, most recently from ae89610 to d3af275 Compare April 19, 2018 06:40
@CanIHasReview-bot
Copy link

v2

@yamidark submitted v2 for review.

(📚 Archive) (📈 Interdiff between v1 and v2)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/880/2/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

@@ -46,8 +49,16 @@ public void isShowing_helpWindowIsHiding_returnsFalse() {
}

@Test
public void focus_helpWindowNotFocused_focused() {
public void focus_helpWindowNotFocused_focused() throws Exception {
if (guiRobot.isHeadlessMode()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Any way of properly telling JUnit to skip the test? (So that it is reported as a skipped test rather than a test that succeeded)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will look up on this.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can use Assumption

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using assumeTrue or assumeFalse will make the test throw a TestAbortedException, causing gradle, travis and appveyor to fail, so it isn't really skipping the test.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm that's weird, cos in the docs:

In direct contrast to failed assertions, failed assumptions do not result in a test failure; rather, a failed assumption results in a test being aborted.

Copy link
Contributor

@Zhiyuan-Amos Zhiyuan-Amos Apr 20, 2018

Choose a reason for hiding this comment

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

Otherwise, maybe you can try conditional execution and see whether there's any output on the test being skipped.

Copy link
Contributor Author

@yamidark yamidark Apr 20, 2018

Choose a reason for hiding this comment

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

Hmm that's weird, cos in the docs:
In direct contrast to failed assertions, failed assumptions do not result in a test failure; rather, a failed assumption results in a test being aborted.

Oh, I think I imported and used the wrong one, I used org.junit.jupiter.api.Assumptions instead of org.junit.Assume. Switching over to org.junit.Assume passes/ignores the test, but no message is shown to show that the test is skipped.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm referring to the org.junit.jupiter.api.Assumptions though, see the docs I linked above :P

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'm referring to the org.junit.jupiter.api.Assumptions though, see the docs I linked above :P

@_@, ok maybe I confuse with the other links I had xD.
Anyway, I can't seem to get conditional execution to work on my side:

@DisabledIf("guiRobot.isHeadlessMode()")

Should I just stick with org.junit.Assume? It doesn't seem to inform us when we skip the test though.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can do something like this to print the names of skipped tests:

diff --git a/build.gradle b/build.gradle
index 75a518e..5c0329e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,6 +2,8 @@
 // For more details take a look at the Java Quickstart chapter in the Gradle
 // user guide available at http://gradle.org/docs/4.6/userguide/tutorial_java_projects.html
 
+import org.gradle.api.tasks.testing.logging.TestLogEvent
+
 plugins {
     id 'java'
     id 'jacoco'
@@ -117,12 +119,14 @@ allTests.dependsOn nonGuiTests
 test {
     systemProperty 'testfx.setup.timeout', '60000'
 
-    // Prints the currently running test's name in the CI's build log,
-    // so that we can check if tests are being silently skipped or
-    // stalling the build.
-    if (System.env.'CI') {
-        beforeTest { descriptor ->
-            logger.lifecycle("Running test: ${descriptor}")
+    testLogging {
+        events TestLogEvent.FAILED, TestLogEvent.SKIPPED
+
+        // Prints the currently running test's name in the CI's build log,
+        // so that we can check if tests are being silently skipped or
+        // stalling the build.
+        if (System.env.'CI') {
+            events << TestLogEvent.STARTED
         }
     }
 

Still haven't figured out how to print the reason for test abortion though.

@yamidark yamidark force-pushed the fix-help-window-failing-test branch 7 times, most recently from 738b105 to ee81a6f Compare April 20, 2018 11:06
@CanIHasReview-bot
Copy link

v3

@yamidark submitted v3 for review.

(📚 Archive) (📈 Interdiff between v2 and v3)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/880/3/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

HelpWindowTest: fix failing test in non-headless mode

HelpWindowTest#focus_helpWindowNotFocused_focused() asserts that the
HelpWindow is not in focus when it is first shown, and then asserts
that the HelpWindow comes into focus after calling HelpWindow#focus().

When tests are run in non-headless mode, the HelpWindow will be in
focus when it is first shown, thus rendering our first assumption false
and causing the test to fail.

Let's update the test to focus on another TestFx Stage, which will
remove focus from the HelpWindow when it is first shown, ensuring that
the initial assumption is true.

OK, fixing the test to work in head-full mode is one change.

Also, since this test has an undefined behaviour when run in headless
mode due to different Stages sharing the same Scene, let's also skip
this test when run in headless mode.

This is a completely different change though, and should be put in a different commit.

You also need to explain why it is undefined behavior.

@@ -46,8 +47,12 @@ public void isShowing_helpWindowIsHiding_returnsFalse() {
}

@Test
public void focus_helpWindowNotFocused_focused() {
public void focus_helpWindowNotFocused_focused() throws Exception {
assumeFalse(guiRobot.isHeadlessMode());
Copy link
Contributor

Choose a reason for hiding this comment

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

Should use assumeFalse(String message, boolean b) to provide a reason for why the test is aborted.

@yamidark yamidark force-pushed the fix-help-window-failing-test branch 3 times, most recently from 6bf51c6 to 6e7cd01 Compare April 21, 2018 10:36
@CanIHasReview-bot
Copy link

v5

@yamidark submitted v5 for review.

(📚 Archive) (📈 Interdiff between v4 and v5)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/880/5/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

[3/3]

build.gradle: enable logging of skipped tests

build.gradle settings does not enable logging of tests which may be

Probably not may be, since we want to only log the tests that were skipped.

skipped for certain reasons, such as buggy behavior in headless mode.

I think buggy behavior in headless mode needs more clarification. (Readers jumping to this commit from git-blame will not know the context)

So, something like:

Our build.gradle configuration does not log to the console tests that were
skipped. These tests may have been skipped for certain reasons, e.g. they do not support headless mode and should be run in non-headless mode instead.
As these tests that are skipped may actually be failing, we should
enable logging of these tests so that users know which tests that
needs to be tested separately.

which tests that needs -> which tests need

Lets update build.gradle to enable logging of tests are that skipped.

of tests are that skipped -> of tests that were skipped

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

Changes Requested.

@yamidark yamidark force-pushed the fix-help-window-failing-test branch from 6e7cd01 to 651d999 Compare April 21, 2018 14:45
@CanIHasReview-bot
Copy link

v6

@yamidark submitted v6 for review.

(📚 Archive) (📈 Interdiff between v5 and v6)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/880/6/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

@pyokagan pyokagan requested a review from damithc April 21, 2018 15:17
HelpWindowTest#focus_helpWindowNotFocused_focused() asserts that the
HelpWindow is not in focus when it is first shown, and then asserts
that the HelpWindow comes into focus after calling HelpWindow#focus().

When tests are run in non-headless mode, the HelpWindow will be in
focus when it is first shown, thus rendering our first assumption false
and causing the test to fail.

Let's update the test to focus on another TestFx Stage, which will
remove focus from the HelpWindow when it is first shown, ensuring that
the initial assumption is true.
HelpWindowTest#focus_helpWindowNotFocused_focused test checks
whether calling HelpWindow#focus() will bring the HelpWindow to focus.
Normally, when HelpWindow#show() is called, the HelpWindow's
Stage should automatically be in focus.

However, when tests are run in headless mode, the HelpWindow in
HelpWindowTest#focus_helpWindowNotFocused_focused test does not
automatically come into focus after it is shown.

This is due to HelpWindowStage in setUp() setting the HelpWindow's
Scene to itself. According to the JavaFx docs for
Window#setScene(Scene)[1], a Scene can only be on one Stage at a time,
and setting a Scene on a different Stage will cause the old Stage to
lose the reference. As a result, the HelpWindow's Stage will not have a
reference to any Scene, resulting in the HelpWindow's Stage not coming
into focus automatically, due to a bug with Monocle's headless mode.

Let's skip HelpWindowTest#focus_helpWindowNotFocused_focused test when
tests are run in headless mode as it is relying on buggy behavior.

[1]: JavaFx docs for Window#setScene(Scene):
https://docs.oracle.com/javase/8/javafx/api/javafx/stage/Window.html#setScene-javafx.scene.Scene-
Our build.gradle configuration does not log to the console tests that
were skipped. These tests may have been skipped for certain reasons,
e.g. they do not support headless mode and should be run in
non-headless mode instead.

As these tests that are skipped may actually be failing, we should
enable logging of these tests so that users know which tests needs
to be tested separately.

Lets update build.gradle to enable logging of tests that were skipped.
@yamidark yamidark force-pushed the fix-help-window-failing-test branch from 651d999 to fc69d10 Compare April 22, 2018 08:18
@pyokagan
Copy link
Contributor

@yamidark Please submit another version because your PR has been modified.

@CanIHasReview-bot
Copy link

v7

@yamidark submitted v7 for review.

(📚 Archive) (📈 Interdiff between v6 and v7)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/880/7/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

Copy link
Contributor

@vivekscl vivekscl left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@pyokagan pyokagan merged commit 8d2322b into se-edu:master Apr 24, 2018
@yamidark yamidark deleted the fix-help-window-failing-test branch July 5, 2018 08:41
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants