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

Fix tests failing on WSL systems. #1511

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

rsashank
Copy link
Member

@rsashank rsashank commented Jun 5, 2024

What does this PR do, and why?

Addressed issues with file path separators \\ and GUI return codes specific to WSL environments.

Fixes the failing of these tests on WSL environments, these have been failing for a while now but I just got used to ignoring them, decided to look into them today and fix. 😓

image

Outstanding aspect(s)

  • [ ]

External discussion & connections

  • Discussed in #zulip-terminal in WSL specific test fails
  • Fully fixes #
  • Partially fixes issue #
  • Builds upon previous unmerged work in PR #
  • Is a follow-up to work in PR #
  • Requires merge of PR #
  • Merge will enable work on #

How did you test this?

  • Manually - Behavioral changes
  • Manually - Visual changes
  • Adapting existing automated tests
  • Adding automated tests for new behavior (or missing tests)
  • Existing automated tests should already cover this (only a refactor of tested code)

Self-review checklist for each commit

  • It is a minimal coherent idea
  • It has a commit summary following the documented style (title & body)
  • It has a commit summary describing the motivation and reasoning for the change
  • It individually passes linting and tests
  • It contains test additions for any new behavior
  • It flows clearly from a previous branch commit, and/or prepares for the next commit

Visual changes

@zulipbot zulipbot added the size: S [Automatic label added by zulipbot] label Jun 5, 2024
Comment on lines 543 to 545
if PLATFORM == "WSL":
media_path = media_path.replace("/", "\\")

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure if I can import PLATFORM into tests, but this seemed like the only way to address the difference in file paths in WSL.

Copy link
Member Author

Choose a reason for hiding this comment

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

To further clarify, based on a discussion on CZO, the issue stems from the normalized_path function used in the download_media method. The media_path assertion doesn't account for the WSL-specific normalized path.

Copy link
Collaborator

Choose a reason for hiding this comment

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

For a conditional in a test, the cleaner way to handle this would normally be to parametrize the test (over PLATFORM), though note you'd want a third path for the expected_return_value or similar output parameter, since it doesn't correspond to url or media_path in every case (though the latter may not be representative - see below re confirming).

The alternative to this conditional/parametrizing solution, which may also be clearer in the long term, is related to how (as we confirmed in the channel) the path returned is the path used for opening the temporary file, not the path used for saving it. So it would be clearer to refactor the code before fixing the tests, such that the normalized_file_path occurs outside of this function, and this function would be platform-independent to test.

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved the normalized_file_path to be called in process_media 👍
Clear explanation, thanks!

Comment on lines 608 to 640
@pytest.mark.parametrize(
"returncode, error",
[
(0, []),
(1 if PLATFORM == "WSL" else 0, []),
(
1,
0 if PLATFORM == "WSL" else 1,
[
" The tool ",
("footer_contrast", "xdg-open"),
" did not run successfully" ". Exited with ",
("footer_contrast", "1"),
("footer_contrast", "0" if PLATFORM == "WSL" else "1"),
],
),
],
Copy link
Member Author

@rsashank rsashank Jun 5, 2024

Choose a reason for hiding this comment

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

This test didn't consider the different return codes in WSL, as given in platform_code.py line 92.

def successful_GUI_return_code() -> int:  # noqa: N802 (allow upper case)
    """
    Returns success return code for GUI commands, which are OS specific.
    """
    # WSL uses GUI return code as 1. Refer below link to know more:
    # https://stackoverflow.com/questions/52423031/
    # why-does-opening-an-explorer-window-and-selecting-a-file-through-pythons-subpro/
    # 52423798#52423798
    if PLATFORM == "WSL":
        return 1

    return 0

Copy link
Member Author

Choose a reason for hiding this comment

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

explorer.exe seems to always return a non-zero exit code, regardless of whether the task is successful.

More information could be found on this issue.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Your initial change may not be relevant now, so skip this paragraph to focus on possible solutions. However, note that (a) we should avoid repeating ourselves in this way, and (b) the way you have it written makes the test itself perform differently when run on WSL and elsewhere, which would be fragile - ie. suddenly break if not tested regularly (we don't test on WSL in CI). As long as the behavior and code can actually be run and tested from a different platform, we should prefer to mock PLATFORM and ensure the outcome matches what we expect.

Based on the behavior you confirmed in the channel (and as per your comment above), it seems this is a bug, so it'd be worth not just fixing the test here.

One simple approach would be to revise the code to only output a message on running the tool, since that's the common factor we can actually determine across platforms.

However, to allow retaining some of the existing behavior, we can refactor the function you quoted, such that it returns some value, depending upon a passed-in return code. This would move the comparison, and hence these explicit error codes, into a platform-dependent location. In turn, the test you're aiming to fix could easily mock the new function, and would be independent of platform.

Depending upon the approach taken, we would lose the ability to test/show the exact error code in the output, but I'm not convinced this adds much to the experience, unless debugging.

To retain the existing behavior, different return values from that new function could signify eg. 'definitely opened', 'failed to open', 'trying to open', to cover the case across platforms.

Depending how far you wish to go with this refactor, a lot of the logic in open_media could move into the new platform-dependent function, including possibly the tools-per-platform themselves. However, that could be a series of refactors after the first simplification.

Copy link
Member Author

Choose a reason for hiding this comment

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

we can refactor the function you quoted, such that it returns some value, depending upon a passed-in return code. This would move the comparison, and hence these explicit error codes, into a platform-dependent location.

Implemented this approach, thanks for the suggestion! Also will look into refactoring some open_media logic to a platform-specific location in another PR.

@rsashank rsashank changed the title bugfix: tests: Fix tests failing on WSL systems. Fix tests failing on WSL systems. Jun 6, 2024
@Niloth-p
Copy link
Collaborator

Niloth-p commented Jun 7, 2024

Nice work! Thanks for addressing those.
The motivation and code LGTM.
As discussed, this is not something that I can run and test though, as I don't use WSL.

@neiljp
Copy link
Collaborator

neiljp commented Jun 9, 2024

@rsashank I started a review, but will leave that pending until I hear back to confirm the implementation details I noted in #zulip-terminal.

@rsashank rsashank added area: tests PR needs review PR requires feedback to proceed platform: WSL labels Jun 10, 2024
Copy link
Collaborator

@neiljp neiljp left a comment

Choose a reason for hiding this comment

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

@rsashank Thanks for finding these problems and working on solutions - we don't have WSL in CI (GitHub PR tests), so some of these issues are not always obvious, and not everything can be tested in CI in any case.

Following the discussion in the channel, I've adjusted some comments, though the same general gist of my suggestions remains the same, ie. that refactoring slightly first would be preferable, and may allow further subsequent refactoring which would make everything a lot easier to read.

Due to limited resources for testing on each platform, in general its better if we can isolate as much into platform-code as we can, during times while people are available to test it manually. That applies to this code in helper, as well as anywhere else we explicitly rely upon PLATFORM. That allows the platform-dependent code to be manually-tested well, and the interface between that and the application to be as clean as possible, as well as tests like these to be less fragile :)

Comment on lines 608 to 640
@pytest.mark.parametrize(
"returncode, error",
[
(0, []),
(1 if PLATFORM == "WSL" else 0, []),
(
1,
0 if PLATFORM == "WSL" else 1,
[
" The tool ",
("footer_contrast", "xdg-open"),
" did not run successfully" ". Exited with ",
("footer_contrast", "1"),
("footer_contrast", "0" if PLATFORM == "WSL" else "1"),
],
),
],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Your initial change may not be relevant now, so skip this paragraph to focus on possible solutions. However, note that (a) we should avoid repeating ourselves in this way, and (b) the way you have it written makes the test itself perform differently when run on WSL and elsewhere, which would be fragile - ie. suddenly break if not tested regularly (we don't test on WSL in CI). As long as the behavior and code can actually be run and tested from a different platform, we should prefer to mock PLATFORM and ensure the outcome matches what we expect.

Based on the behavior you confirmed in the channel (and as per your comment above), it seems this is a bug, so it'd be worth not just fixing the test here.

One simple approach would be to revise the code to only output a message on running the tool, since that's the common factor we can actually determine across platforms.

However, to allow retaining some of the existing behavior, we can refactor the function you quoted, such that it returns some value, depending upon a passed-in return code. This would move the comparison, and hence these explicit error codes, into a platform-dependent location. In turn, the test you're aiming to fix could easily mock the new function, and would be independent of platform.

Depending upon the approach taken, we would lose the ability to test/show the exact error code in the output, but I'm not convinced this adds much to the experience, unless debugging.

To retain the existing behavior, different return values from that new function could signify eg. 'definitely opened', 'failed to open', 'trying to open', to cover the case across platforms.

Depending how far you wish to go with this refactor, a lot of the logic in open_media could move into the new platform-dependent function, including possibly the tools-per-platform themselves. However, that could be a series of refactors after the first simplification.

Comment on lines 543 to 545
if PLATFORM == "WSL":
media_path = media_path.replace("/", "\\")

Copy link
Collaborator

Choose a reason for hiding this comment

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

For a conditional in a test, the cleaner way to handle this would normally be to parametrize the test (over PLATFORM), though note you'd want a third path for the expected_return_value or similar output parameter, since it doesn't correspond to url or media_path in every case (though the latter may not be representative - see below re confirming).

The alternative to this conditional/parametrizing solution, which may also be clearer in the long term, is related to how (as we confirmed in the channel) the path returned is the path used for opening the temporary file, not the path used for saving it. So it would be clearer to refactor the code before fixing the tests, such that the normalized_file_path occurs outside of this function, and this function would be platform-independent to test.

@neiljp neiljp added PR awaiting update PR has been reviewed & is awaiting update or response to reviewer feedback bug Something isn't working area: refactoring and removed PR needs review PR requires feedback to proceed labels Jun 13, 2024
@zulipbot
Copy link
Member

Hello @zulip/server-refactoring members, this pull request was labeled with the "area: refactoring" label, so you may want to check it out!

@zulipbot zulipbot added size: L [Automatic label added by zulipbot] and removed size: S [Automatic label added by zulipbot] labels Jun 28, 2024
@rsashank rsashank added PR needs review PR requires feedback to proceed and removed PR awaiting update PR has been reviewed & is awaiting update or response to reviewer feedback labels Jun 28, 2024
This removes platform-dependence from download_media, and for now leaves
more in process_media, which already had a platform-dependent test.

This addresses a failing test in WSL environments in download_media,
which is no longer dependent upon the platform.
WSL always returns a non-zero exit code. The `successful_GUI_return_code()`
function has been refactored to directly compare exit statuses and return
"success" or "failure" accordingly.
Copy link
Collaborator

@neiljp neiljp left a comment

Choose a reason for hiding this comment

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

@rsashank I hoped to merge this fully but ended up leaving a few comments aimed primarily at the second commit - I've picked off the first commit and merged it already, after making the change I indicated in the comment re which item to patch.

Hopefully that first commit reduces the failing tests you're finding regularly, and the other commit will be quick to follow :)

Comment on lines 575 to 576
mocker.patch(MODULE + ".PLATFORM", platform)
mocker.patch("zulipterminal.platform_code.PLATFORM", platform)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Note that these two lines are patching

  • the PLATFORM imported into helper
  • the PLATFORM in the original module

So, at the very least this is duplication. When patching, we want to adjust the name that's used, and if you only use the version you added (and remove the previous one), then it won't run the test - it doesn't patch properly.

=> The existing line is sufficient.

Comment on lines +98 to +102
# NOTE: WSL always returns a non-zero exit code (1) for GUI commands.
# This is a known issue. Therefore, we consider it a success if the exit code is 1.
# For more information, see: https://github.com/microsoft/WSL/issues/6565
if PLATFORM == "WSL":
return 1
return "success" if exit_status == 1 else "failure"
Copy link
Collaborator

Choose a reason for hiding this comment

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

This claims to be able to return and handle failure in the code, but the comment says it cannot. Then we don't test this code. If we're always going to return success, we should do that and then only test for it with WSL - or be able to return both success and failure, and test for both.

Is it only explorer.exe under WSL that always succeeds?

If that's the case, this is quite fragile since this seems related to knowing that we're using it for explorer.exe only, but in another part of the code entirely. Having this change will be good for now, but we should ensure that we note this in each location that's affected.

Comment on lines +92 to +94
def validate_GUI_exit_status( # noqa: N802 (allow upper case)
exit_status: int,
) -> str:
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you're only returning one of two strings, it's good to use Literal["string1", "string2"] here, since mypy will pick up if there's a mismatch.

Comment on lines +88 to 94
ids=[
"Linux_0_return_code",
"MacOS_0_return_code",
"WSL_1_return_code",
"Linux_1_return_code",
"MacOS_1_return_code",
],
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm normally in favour of ids, but these are essentially the parameters we're passing in, so I'm not convinced they're adding anything?

media_path: str = "/tmp/zt-somerandomtext-image.png",
) -> None:
mocked_run = mocker.patch(MODULE + ".subprocess.run")
mocker.patch("zulipterminal.platform_code.PLATFORM", platform)
Copy link
Collaborator

Choose a reason for hiding this comment

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

See my comment on the other commit - you should be able to tell if this is working properly, by whether running the test shows the code sees the patched value properly. Something as simple as print(PLATFORM) in the code will do the job - give it a try!

("footer_contrast", "1"),
],
),
# NOTE: WSL always returns a non-zero exit code (1), so we do not test for it.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is more re explorer.exe, right, rather than WSL?

Comment on lines +636 to +639
"Linux_os_user",
"Mac_os_user",
"WSL_os_user",
"Linux_os_error",
"Mac_os_error",
Copy link
Collaborator

Choose a reason for hiding this comment

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

These ids are useful since each set now has 4 values, but they'd be better inline (see the case examples elsewhere, which is a rename of a longer pytest import) and with something like Linux:success formatting - keep it simple :)

("WSL", 1, "success"),
("Linux", 1, "failure"),
("MacOS", 1, "failure"),
# NOTE: WSL always returns a non-zero exit code (1), so we do not test for it.
Copy link
Collaborator

Choose a reason for hiding this comment

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

As per another comment, we should test for this, even if it results in the same response - we're testing the function, not the system behavior, in this test.

@neiljp neiljp added PR awaiting update PR has been reviewed & is awaiting update or response to reviewer feedback and removed PR needs review PR requires feedback to proceed labels Jul 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: refactoring area: tests bug Something isn't working platform: WSL PR awaiting update PR has been reviewed & is awaiting update or response to reviewer feedback size: L [Automatic label added by zulipbot]
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants