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

Make xattr dependency conditional, add stub xattr library #228

Merged
merged 9 commits into from
Feb 21, 2023

Conversation

marczalik
Copy link
Contributor

One sentence summary of this PR (This should go in the CHANGELOG!)
Make xattr dependent on platform, add stub library that warns users without xattr that extended attributes are not supported on their platform.

Link to Related Issue(s)
#227

Please describe the changes in your request.
Make xattr dependency optional based on platform. Add stub library that shadows the standard xattr library's functionality, and any function calls to xattr are replaced with warnings that extended attributes are not supported on their platform. Fall back to stub library if xattr not found.

Anyone you think should look at this, specifically?

@marczalik
Copy link
Contributor Author

We should discuss testing requirements for the stub library and whether it makes sense to test it or if the file should be ignored.

@marczalik
Copy link
Contributor Author

marczalik commented Feb 16, 2023

Ignoring redefinitions of xattr for type checking. This is apparently a known and long-standing issue with mypy: python/mypy#1153

Copy link
Member

@rbs-jacob rbs-jacob left a comment

Choose a reason for hiding this comment

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

This looks good! If it's not too difficult, I think it make sense to add tests confirming that when the stubbed library is swapped in, the filesystem tests still pass.

The tests right now don't actually confirm much for us, though they do get test coverage numbers up. Testing the filesystem implementation with the stubs would be more representative of the real-world situation in which the stubs are used, and would therefore be a better set of tests in terms of confirming for us that this change behaves the way we expect.

I'm not sure if it would be possible to test using the stubs in the filesystem tests without adding steps to the GitHub Actions workflow. These steps would uninstall xattr from the Docker container and re-run the filesystem tests. There may be a tricky way to do it with pytest, though.

Long story short, I'd spend a little while (but not too long!) trying to add some better tests. If it proves too difficult or unwieldy, we won't let it block this PR, and I'll merge it.

@whyitfor @EdwardLarson may want to weigh in on this as well.

@marczalik
Copy link
Contributor Author

Testing the filesystem implementation with the stubs would be more representative of the real-world situation in which the stubs are used, and would therefore be a better set of tests in terms of confirming for us that this change behaves the way we expect.

I am currently modifying the tests to confirm the return types of the stub functions match the type of the real xattr library functions. Would these changes meet this request?

@rbs-jacob
Copy link
Member

I am currently modifying the tests to confirm the return types of the stub functions match the type of the real xattr library functions. Would these changes meet this request?

The changes you describe would fix types, but still are more like unit tests. I'm proposing something more like integration or functional tests to confirm not just that the xattr stubs work on their own, but also that they have the expected behavior when the rest of the system is run using them.

While the changes you're making are important, I think they're complementary to what I'm suggesting.

@marczalik
Copy link
Contributor Author

Got it. In that case I think the cleanest way to handle this would be, as you suggested, to somehow rerun the filesystem tests after uninstalling xattr, if that is even possible. I will spend a little bit of time looking into how to accomplish this.

@marczalik marczalik requested a review from rbs-jacob February 20, 2023 20:18
Copy link
Member

@rbs-jacob rbs-jacob left a comment

Choose a reason for hiding this comment

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

One small change, and we'll wrap this up! Nicely done

Copy link
Contributor

@whyitfor whyitfor left a comment

Choose a reason for hiding this comment

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

In addition to comments, this PR does not seem to include an update to the CHANGELOG

Comment on lines +15 to +19

.PHONY: serial
serial:
$(PYTHON) -m pytest -n0 -m "serial" test_ofrak --cov=ofrak --cov-report=term-missing
fun-coverage
Copy link
Contributor

Choose a reason for hiding this comment

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

Am I understanding correctly that the "serial" tests need to be run in a specified order?

If so, this is not ideal -- each "test" should be standalone, and any setup or teardown for it should be handled by a fixture (and not be a side-effect of a previously run test).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The order of the serial tests does not matter. What matters is that the two tests are not run in parallel, since one test requires the real xattr library be installed, while the other requires it be uninstalled. So to guarantee that they are not run in parallel I have set them to run in serial.

import logging


LOGGER = logging.getLogger(__name__)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this LOGGER ever used?

Comment on lines +10 to +16
subprocess.run(["pip", "uninstall", "xattr", "-y"])
cwd = os.getcwd()
os.chdir(os.path.dirname(__file__))
result = subprocess.run(["pytest", "./test_filesystem_component.py"])
assert result.returncode == 0
os.chdir(cwd)
subprocess.run(["pip", "install", "xattr"])
Copy link
Contributor

@whyitfor whyitfor Mar 3, 2023

Choose a reason for hiding this comment

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

  1. I wonder if there is another way to do this that does not require running this test in a subprocess (is there a way to monkeypatch this)
  2. Point 1 aside, the uninstall/install steps here are not part of the test. They should really be extracted into a fixture.

Comment on lines +11 to +16
@pytest.fixture(scope="session")
def move_to_test_directory():
current_directory = os.getcwd()
os.chdir(EXAMPLE_DIRECTORY)
yield
os.chdir(current_directory)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this fixture needed here?



async def test_get(xattr_stub_fixture, xattr_real_fixture, caplog):
xattr_real_fixture.set("user.foo", b"bar")
Copy link
Contributor

Choose a reason for hiding this comment

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

This creates a side effect -- the xattr for the target file is set here, but no cleanup happens. Test setup/teardown should all be happening in a fixture, to ensure no side effects.



async def test_set(xattr_stub_fixture, xattr_real_fixture, caplog):
value = xattr_stub_fixture.set(None, None)
Copy link
Contributor

Choose a reason for hiding this comment

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

It is confusing here (and later on) that the stub fixture is getting passed different arguments than the real fixture, since it is unclear what is being tested.

My understanding for this and other tests in this file is, that the test is the following:

Run the method on stub and real xattr with same arguments, and validate that the return type matches

Making the arguments the same will make this intention clearer.

value = xattr_stub.getxattr(None, None)
assert "Function getxattr" in caplog.text
assert type(value) == type(
xattr.getxattr(os.path.join(EXAMPLE_DIRECTORY, "README.md"), "user.foo")
Copy link
Contributor

Choose a reason for hiding this comment

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

This assumes that the xattr "user.foo" is already set. What happens if it isn't set?

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.

3 participants