-
Notifications
You must be signed in to change notification settings - Fork 179
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
TEST: Run all tests through pytest
#2090
TEST: Run all tests through pytest
#2090
Conversation
@Alexsandruss Would you perhaps have any pointers about why the failing CI job here: .. might be failing to import |
I've had to fight against pytest's package discovery recently and had to use |
Thanks for the tip. But that was actually for a different reason: some test files import other tests files - for example But since now these tests should only be getting executed through |
Looks like it was just a matter of deleting the |
pytest
pytest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you for doing this. Here are my initial thoughts.
.ci/scripts/test_global_patch.py
Outdated
err_code = subprocess.call( | ||
[sys.executable, "-m", "sklearnex.glob", "patch_sklearn", "-a", "svc"] | ||
) | ||
assert not err_code | ||
|
||
def unpatch_from_cmd(): | ||
err_code = subprocess.call( | ||
[sys.executable, "-m", "sklearnex.glob", "unpatch_sklearn"] | ||
) | ||
assert not err_code | ||
|
||
request.addfinalizer(unpatch_from_cmd) | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err_code = subprocess.call( | |
[sys.executable, "-m", "sklearnex.glob", "patch_sklearn", "-a", "svc"] | |
) | |
assert not err_code | |
def unpatch_from_cmd(): | |
err_code = subprocess.call( | |
[sys.executable, "-m", "sklearnex.glob", "unpatch_sklearn"] | |
) | |
assert not err_code | |
request.addfinalizer(unpatch_from_cmd) | |
return | |
err_code = subprocess.call( | |
[sys.executable, "-m", "sklearnex.glob", "patch_sklearn", "-a", "svc"] | |
) | |
assert not err_code | |
yield | |
err_code = subprocess.call( | |
[sys.executable, "-m", "sklearnex.glob", "unpatch_sklearn"] | |
) | |
assert not err_code |
Thoughts on using yield instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be the advantage of using yield here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its not a must, but its the pytest recommended procedure over adding a finalizer https://docs.pytest.org/en/stable/how-to/fixtures.html#yield-fixtures-recommended
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to yield
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the windows job doesn't like usage of 'yield':
INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^^^^^
INTERNALERROR> File "C:\Miniconda\envs\CB\Lib\site-packages\_pytest\logging.py", line 790, in pytest_collection
INTERNALERROR> return (yield)
INTERNALERROR> ^^^^^
...
joblib.externals.loky.process_executor.BrokenProcessPool: A task has failed to un-serialize. Please ensure that the arguments of the function are all picklable.
Will try to see where that multi-processing invocation is coming from.
def patch_from_function(request): | ||
from sklearnex import patch_sklearn, unpatch_sklearn | ||
|
||
patch_sklearn(name=["svc"], global_patch=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again possibly yield?
.ci/scripts/test_global_patch.py
Outdated
) | ||
|
||
|
||
def test_unpatching_from_command_line(patch_from_command_line): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is the fixture used here again? Is this trying to expand functionality or match the previous one (because this is slightly different from what was before). What was previous assumed that the global setting was returned to normal after the unpatch, so by reapplying the "sklearnex.glob", "patch_sklearn", "-a", "svc"
the state of this test is different than what occurred in the file before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fixture has a finalizer. So on each test, it gets initialized (global patching is applied), and after the test finishes, gets finalized (global unpatching is applied). Then the same repeats for the next test that uses the fixture, ensuring that state doesn't propagate across tests. This is of course assuming that patching and unpatching are working correctly, which is what the tests are looking at in the first place, but I thought it better than the idea of merging patching and unpatching in a single test.
assert not err_code | ||
from sklearnex import unpatch_sklearn | ||
|
||
unpatch_sklearn() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/intel/scikit-learn-intelex/blob/main/sklearnex/tests/test_monkeypatch.py#L65 Just be careful with these functions since they impact global state and aren't local to the test (anything with a patch_sklearn call should be with a 'try-finally' with an unpatch_sklearn)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's the idea behind the fixtures with finalizers.
.ci/scripts/test_global_patch.py
Outdated
) | ||
|
||
|
||
def test_unpatching_from_function(patch_from_function): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I look at what the code had previously been, I am glad you have done this. Much better isolation of tests.
conda-recipe/meta.yaml
Outdated
- mpirun -n 4 python -m unittest discover -v -s tests -p test*spmd*.py # [not win] | ||
- mpiexec -localonly -n 4 python -m unittest discover -v -s tests -p test*spmd*.py # [win] | ||
- python -m unittest discover -v -s tests -p test*.py | ||
- mpirun -n 4 pytest --pyargs --verbose -s tests/test*spmd*.py # [not win] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is --pyargs
necessary since you are using a path and not a package name? (same goes for the others in run_test.bat
/ run_test.sh
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know. But all the other pytest
calls have it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say cut them out and see what happens. If they fail you can readd them (and ping me)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like they weren't necessary.
err_code = subprocess.call([sys.executable, "-m", "sklearnex.glob", "unpatch_sklearn"]) | ||
assert not err_code | ||
unpatch_sklearn() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The state of the unpatch_sklearn() test was previously after a subprocess.call([sys.executable, "-m", "sklearnex.glob", "unpatch_sklearn"])
not a
subprocess.call(
[sys.executable, "-m", "sklearnex.glob", "patch_sklearn", "-a", "svc"] call, meaning the state of the test is different in the new implementation. This is okay if this was what you desired
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I had missed that detail entirely.
So I tried adding a new test for global state separately from just SVC
, but the test actually ends up failing:
@pytest.fixture
def patch_all_from_command_line():
err_code = subprocess.call(
[sys.executable, "-m", "sklearnex.glob", "patch_sklearn"]
)
assert err_code == os.EX_OK
yield
err_code = subprocess.call(
[sys.executable, "-m", "sklearnex.glob", "unpatch_sklearn"]
)
assert err_code == os.EX_OK
def test_patching_all_from_command_line(patch_all_from_command_line):
from sklearn.svm import SVC, SVR
assert SVC.__module__.startswith("daal4py")
assert SVC.__module__.startswith("sklearnex")
assert SVR.__module__.startswith("daal4py")
assert SVR.__module__.startswith("sklearnex")
should that be expected to succeed? same happens if checking them with "or", and happens for both SVC and SVR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume that its failing on
assert SVC.__module__.startswith("daal4py")
. You can remove
assert SVC.__module__.startswith("daal4py")
and assert SVR.__module__.startswith("daal4py")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also fails for sklearnex
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added it this new test as an extra commit here, let's see if it also fails in the CI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be a legit failure then, lets see
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like it also fails in the CI jobs:
def test_patching_all_from_command_line(patch_all_from_command_line):
from sklearn.svm import SVC, SVR
> assert SVC.__module__.startswith("daal4py") or SVC.__module__.startswith("sklearnex")
E AssertionError: assert (False or False)
E + where False = <built-in method startswith of str object at 0x7f8679a04300>('daal4py')
E + where <built-in method startswith of str object at 0x7f8679a04300> = 'sklearn.svm._classes'.startswith
E + where 'sklearn.svm._classes' = <class 'sklearn.svm._classes.SVC'>.__module__
E + and False = <built-in method startswith of str object at 0x7f8679a04300>('sklearnex')
E + where <built-in method startswith of str object at 0x7f8679a04300> = 'sklearn.svm._classes'.startswith
E + where 'sklearn.svm._classes' = <class 'sklearn.svm._classes.SVC'>.__module__
scripts/test_global_patch.py:83: AssertionError
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this is bad, i'll admit I don't know much about the glob
use. I wonder if sklearn has been pre-imported via sklearnex imports, and the change in glob not affecting it. There is definitely something here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, looks like that does indeed make a difference. If leaving it as the only test then it actually succeeds. Will try to find a workaround.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't manage to find any solution so in the end I just removed the test and left a comment in the file.
/intelci: run |
/intelci: run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming small English correction ready to merge
Co-authored-by: Ian Faust <icfaust@gmail.com>
Description
Currently, unit tests execute through a mixture of built-in
unittest
andpytest
.From the commit history, it looks like initial tests were added using python's built-in
unittest
framework, and then subsequent tests were introduced usingpytest
.Compared to
unittest
,pytest
has a much nicer output format, offers many options for how to execute tests and how to see their outputs, and has plugins that allow it do extra things which the built-in cannot when desired (e.g. JSON outputs, test coverage reports, etc.).This PR switches the automated scripts to execute all of the tests using
pytest
. This doesn't change what tests were executed - just usespytest
as the runner.One challenge here is how each of these interprets the path from where modules are imported - here I'm trying to control these with the
PYTHONPATH
variable whenpytest
is called within shell scripts, but don't know if this will work out for all the variants under which tests are executed.Checklist to comply with before moving PR from draft:
PR completeness and readability
Testing
Performance