Skip to content

Fix Failing Tests on Windows (WIP) #27

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

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

Conversation

Chiemezuo
Copy link
Collaborator

@Chiemezuo Chiemezuo commented Oct 22, 2024

This is in line with #8

I'm working on getting the test cases to pass for Windows.

Upon installation, 6 of the 70 existing tests were failing. Here's a short summary of them:

=================================================== short test summary info =================================================== 
FAILED test/test_open.py::TestSafer::test_file_perms - AssertionError: -rw-rw-rw-
FAILED test/test_open.py::TestSafer::test_symlink_directory - OSError: [WinError 1314] A required privilege is not held by the client: 'sub' -> 'sub.sym'
FAILED test/test_open.py::TestSafer::test_symlink_file - OSError: [WinError 1314] A required privilege is not held by the client: 'one' -> 'one.sym'
FAILED test/test_open.py::TestSafer::test_tempfile_perms - assert 33206 in (33188, 33204)
FAILED test/test_writer.py::test_wrapper_bug2 - PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\...
FAILED test/test_writer.py::test_wrapper_bug3 - PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\...

Fixed tests

  • test_file_perms
  • test_symlink_directory
  • test_symlink_file
  • test_temlpfile_perms
  • test_wrapper_bug2
  • test_wrapper_bug3

Summary by Sourcery

Bug Fixes:

  • Fix the test_file_perms test to handle file permission checks correctly on Windows by adjusting the logic to account for Windows-specific file handling.

Copy link

sourcery-ai bot commented Oct 22, 2024

Reviewer's Guide by Sourcery

This pull request addresses failing tests on Windows platforms, focusing on file permission and symlink-related issues. The changes primarily involve modifying the test suite to account for differences in file handling between UNIX and Windows systems.

No diagrams generated as the changes look simple and do not need a visual representation.

File-Level Changes

Change Details Files
Modify file permission checks in test cases to accommodate Windows
  • Add conditional logic to handle file permission checks differently on Windows
  • Replace specific permission bit checks with a more general file type check on Windows
  • Retain original UNIX-specific permission checks for POSIX systems
test/test_open.py
Address symlink-related test failures on Windows
  • Identify symlink creation issues due to privilege restrictions on Windows
  • Mark symlink-related tests as failing and needing further investigation
test/test_open.py
Investigate file access issues in wrapper-related tests
  • Identify PermissionError occurrences in wrapper bug tests
  • Mark wrapper-related tests as failing and needing further investigation
test/test_writer.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @Chiemezuo - I've reviewed your changes - here's some feedback:

Overall Comments:

  • While we appreciate the effort to fix Windows compatibility, bypassing permission checks on Windows is not the ideal solution. Consider finding Windows-equivalent ways to perform these security checks rather than removing them.
  • This PR only addresses one of the six failing tests. Please continue working on the other failures, particularly the symlink tests and file access issues, to ensure full Windows compatibility.
  • For cross-platform development, look into Windows-specific APIs or libraries that can provide functionality equivalent to what's being tested on Unix systems. This will lead to a more robust and secure solution across all platforms.
Here's what I looked at during the review
  • 🟢 General issues: all looks good
  • 🟢 Security: all looks good
  • 🟡 Testing: 2 issues found
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 133 to 140
# UNIX systems view and manipulate file permissions as bits
if os.name is 'posix':
assert mode in (0o100664, 0o100644), stat.filemode(mode)
new_mode = mode & 0o100770
# disparity in Windows file handling
elif os.name is 'nt':
# instead, just check if its a regular file without permission bit specifics
new_mode = mode
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Consider adding specific Windows permission checks

While the current change allows the test to pass on Windows, it doesn't verify any specific permission attributes. Consider adding Windows-specific checks to ensure the file has appropriate permissions, such as checking if it's readable and writable.

if os.name == 'posix':
    assert mode in (0o100664, 0o100644), stat.filemode(mode)
    new_mode = mode & 0o100770
elif os.name == 'nt':
    import win32security
    import ntsecuritycon as con
    sd = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION)
    dacl = sd.GetSecurityDescriptorDacl()
    assert dacl.GetEffectiveRightsFromAcl({win32security.ConvertSidToStringSid(win32security.GetTokenInformation(win32security.OpenProcessToken(win32api.GetCurrentProcess(), win32security.TOKEN_QUERY), win32security.TokenUser)[0]): con.FILE_GENERIC_READ | con.FILE_GENERIC_WRITE})
    new_mode = mode

Comment on lines 134 to 140
if os.name is 'posix':
assert mode in (0o100664, 0o100644), stat.filemode(mode)
new_mode = mode & 0o100770
# disparity in Windows file handling
elif os.name is 'nt':
# instead, just check if its a regular file without permission bit specifics
new_mode = mode
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Consider using unittest.skipIf for OS-specific tests

Instead of using if-else statements in the test, consider using unittest.skipIf decorators to clearly separate OS-specific test cases. This would improve test readability and maintenance.

@unittest.skipIf(os.name != 'posix', "Test specific to POSIX systems")
def test_posix_file_mode(self):
    assert mode in (0o100664, 0o100644), stat.filemode(mode)
    new_mode = mode & 0o100770

@unittest.skipIf(os.name != 'nt', "Test specific to Windows systems")
def test_windows_file_mode(self):
    new_mode = mode

@Chiemezuo
Copy link
Collaborator Author

@rec
The symlink directory and files tests will work only on the condition that the tests are being run in a terminal that contains admin privileges. Unfortunately, admin privileges are not given to terminals by default (but can be changed). I set the test to skip for Windows if admin privileges are not given (instead of failing), but they will pass if given (or will fail if the source of errors is not from privileges).

Copy link
Owner

@rec rec left a comment

Choose a reason for hiding this comment

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

Good stuff, only tiny quibbles here!

I'll take a look later today at the last two tests, I might be able to just guess.

if os.name == 'nt':
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
Copy link
Owner

@rec rec Oct 24, 2024

Choose a reason for hiding this comment

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

Bare except is a bad idea because you catch things that aren't actually Exception - things like OutOfMemoryError or KeyboardInterrupt: https://stackoverflow.com/questions/54948548/what-is-wrong-with-using-a-bare-except

I think the CI (that I just enabled) will catch it, but if not, here's my general purpose lint that I use: https://github.com/rec/dotfiles/blob/master/bin/run-test

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the review!
I'll get started on implementing your recommendations.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Would except Exception as e work just fine for this case?

Copy link
Owner

Choose a reason for hiding this comment

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

Yes, that would work much better!

Linting would reveal a tiny defect: you don't use the new variable e.

Best is exception Exception:.

On linting

The few years have seen a significant change in how people write Python. More and more, projects simply require you to run ruff with all the features turned on, on your code.

I think this is great - the code gets standardized, common errors and defects are avoided, and ruff is blazingly fast.

You should get into the habit of using ruff with a lot of stuff turned on, and also mypy, even on trivial code, because it catches so many bugs.

# disparity in Windows file handling
elif os.name == 'nt':
# instead, just check if its a regular file without permission bit specifics
new_mode = mode
Copy link
Owner

Choose a reason for hiding this comment

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

  1. The AI had some sort of Windows-y test for modes here, I didn't look at it, you might try it...?
  2. You need another else to catch the other cases, whatever they might be:
else:
    assert False, f'Do not understand os.name = {os.name}'
  1. Should be # Instead, just check if it's a regular file without permission bit specifics - capital I, apostrophe.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For:

  1. I wanted a human opinion, first. 😅
  2. Spot on! I'll get to that.
  3. Thanks a bunch for that fix.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also, for the Windows-y mode, I'll have to go through the code in more detail and check docs, because a chunk of it was new to me. I'll give feedback in a couple of hours, though.

@@ -184,6 +202,12 @@ def test_mode_t(self, safer_open):
assert FILENAME.read_text() == 'hello'

def test_symlink_file(self, safer_open):
# check if we are on Windows and have admin rights
if os.name == 'nt' and not is_windows_admin():
Copy link
Owner

Choose a reason for hiding this comment

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

You should instead decorate this test case with https://docs.python.org/3/library/unittest.html#unittest.skipIf

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks!
I've done this.

@@ -196,6 +220,13 @@ def test_symlink_file(self, safer_open):

def test_symlink_directory(self, safer_open):
FILENAME = Path('sub/test.txt')

Copy link
Owner

Choose a reason for hiding this comment

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

Here too...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This too.

@Chiemezuo Chiemezuo requested a review from rec October 25, 2024 07:13
@Chiemezuo
Copy link
Collaborator Author

Chiemezuo commented Oct 25, 2024

Looks good for another round of reviews

PS: I'll look deeper into the Windows-y command.

@rec
Copy link
Owner

rec commented Oct 25, 2024

So it's very natural to demonstrate that you comprehend and are keeping up, but a magic secret to appearing to be a more senior programmer is only talking about operative or actionable questions.

Overall, you don't need to respond to comments if you agree with them, or say what you are going to do, unless there's a question! :-D (Also, you should use the review feature to group together comments in one email, not that it's a big deal, particularly with such a small project.)

Computer programming is a very noisy occupation so I spend extra care in only sending people messages which are actionable, i.e., ask them to do something, or ask a question, or give them information they asked for.

Otherwise, people simply miss stuff in the traffic.

This also leaves more time for actual chat. 🔈

As I said, it's extremely natural, so don't think it's some sort of sin. Mostly you discover this somewhat later, when you have multiple junior programmers each trying to out-volume the other in progress reports... 😈

@Chiemezuo
Copy link
Collaborator Author

@rec A pending workflow approval

@@ -198,8 +198,8 @@ def test_wrapper_bug():
@tdir
def test_wrapper_bug2():
with pytest.raises(NotImplementedError) as e:
fp = open(FILENAME, 'w')
safer.writer(fp, close_on_exit=True, temp_file=True)
with open(FILENAME, 'w') as fp:
Copy link
Owner

Choose a reason for hiding this comment

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

Aha, I'm pretty sure you've found the issue!


So this change needs two non-obvious tweaks.

I didn't accidentally forget to open fp in a context in these - I'm using the close_on_exit flag, which should automatically close fp.

In this first test, I think you have actually revealed an issue:

with safer.writer(fp, close_on_exit=True, temp_file=True) as writer:might not be closing fp if there's an exception. I need to fix that. (It doesn't get detected in the Linux tests because the file handle gets closed immediately it gets destructed.)

So I need to make this change, or verify the bug doesn't exist, and then the first piece of code needs to change to:

fp = open(FILENAME, 'w')
with safer.writer(fp, close_on_exit=True, temp_file=True):
    pass

Probably the second code block should stay the same...

I'll make that change and let you know!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nice! Our bug hunt pays off. :)

@@ -183,6 +203,11 @@ def test_mode_t(self, safer_open):
fp.write('hello')
assert FILENAME.read_text() == 'hello'

# Skip if we are on Windows and do not have admin rights
@unittest.skipIf(
Copy link
Owner

Choose a reason for hiding this comment

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

Quibble: extract the common variable!

IS_WINDOWS_USER = os.name == 'nt' and not is_windows_admin()
skip_windows = unittest.skipIf(
    IS_WINDOWS_USER, 'This test requires admin privileges to create symlink files on Windows'
)

[....]

    @skip_windows
     def test_something():

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I definitely learned something new here!

@rec
Copy link
Owner

rec commented Nov 2, 2024

Voila: #28

I'll add you as a reviewer once you've accepted my invitation on this repo...

Copy link
Owner

@rec rec left a comment

Choose a reason for hiding this comment

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

Really just textual issues, nothing big.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants