-
Notifications
You must be signed in to change notification settings - Fork 76
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
Use pytest #30
base: master
Are you sure you want to change the base?
Use pytest #30
Conversation
Instead of doing the tests manually use a third party library, like `pytest` to make it easier to do and verify tests.
The tests requiring multiple threads (one for publishing the interfaces and one for using them) are largely rewritten to work with pytest. It doesn't support assertions which were not asserted in the main thread. So the threads need to either report the failed assertion or the actual result and that is asserted in the test itself. It also removes any modification of global variables outside the global scope, because that would make it not possible to run multiple tests. Instead there is a new Thread class which automatically quits the main loop after all tests are finished. If the main loop is quit, the main thread can get the status from the threads to check what the result was. If the thread should hang for any reason, it'll timeout after 100 ms and report it as a failure. Also add more tests for the class using multiple interfaces, by using only a specific interface. The test script itself is rewritten to call pytest only once.
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.
Seems like a good idea, as pytest.raises context manager makes the tests much more readable.
However, I'm worried that the threading machinery might produce more problems than it's trying to solve. Especially when I merge the greenglib branch - 833dcd8 - which adds support for greenlets and gevent.
Also, as a comment says, it might lose information about where exactly an exception was raised if it was raised in a thread.
assert(False) | ||
except RuntimeError: | ||
pass | ||
def test_remove_dbus(): |
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.
This should be called test_bus_closure - as it tests if connected buses are automatically closed when going outside of the with statement.
|
||
with bus.request_name("net.lew21.Test"): | ||
pass | ||
def test_use_exited_bus(): |
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.
This should be called test_standard_bus_object_preservation, or something like that, in line with the comment "SessionBus() and SystemBus() are not closed automatically, so this should work"
def run(self): | ||
with self._lock: | ||
while not self.loop.is_running: | ||
pass |
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.
Won't this make us use 100% CPU? That's a no-go.
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 reason I added this statement is that if the thread is too fast and tries to run before the loop is actually activated it reacted strangely. Maybe this isn't actually an issue, but if it is the only other reasonable implementation would be to wait a bit (similar to how TimeLock
waits a bit).
if self._lock.acquire(False): | ||
return True | ||
else: | ||
time.sleep(0.001) |
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.
Meh, busy waiting is not a good idea.
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.
Not sure what you mean here? If the lock cannot be acquired it should wait a bit and retest. There is no way around to either sleep a bit or not at all. It's not like the thread is going to do anything else.
To be honest I don't really know how your But mainly the advantage of using And maybe there is actually a way to say where the problematic assertion happened. |
Okay I have found a way to actually show where the assertion happened, unfortunately the Python 2 code cannot be parsed in Python 3 so I need to find a way round that. |
Okay I think I've resolved the issue that it didn't show where the assertion happened in both Python versions. Additionally I do think that at some point your repository should use a testing suite. At the moment if you just use assertions it does tell you only that they failed but not why (if there was a comparison what the separate values for example). So I'm wondering if I should separate the second commit which adds the threading stuff? Because a patch like #29 would benefit from that as it doesn't use threads and thus could be written to be used by But I have to say it is cumbersome to run tests because you only get to know that it failed but not what happened (unless you manually print it out). If you are fine with separating the commits please tell me which commit this pull request should contain (aka the first or second). There is also the |
I've never had any problems with checking why the test has failed. There is a traceback, and the line with >>> import pytest
>>> with pytest.raises(AttributeError):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/usr/lib/python3.5/site-packages/_pytest/python.py", line 1217, in __exit__
pytest.fail(self.message)
File "/usr/lib/python3.5/site-packages/_pytest/runner.py", line 537, in fail
raise Failed(msg=msg, pytrace=pytrace)
Failed: DID NOT RAISE <class 'AttributeError'>
>>> with pytest.raises(AttributeError):
... raise AttributeError()
...
>>> |
And BTW, the GNOME Music test is not really useful for testing pydbus. I've made it when I was porting GNOME Music to pydbus (however, they've chosen to go straight for GDBus). It isn't 100% automated, depends on external circumstances (for example the user has to have some songs in his GNOME Music collection), it's slow, affects the user, and it's impossible to use on Travis - while I'm almost exclusively using Travis to run the test collection. It's there, because it somewhat works, but it's not a good idea to run it automatically. |
Here is an example what is shown when the assertion failed. To force it I used this branch and modified
If I don't use pytest I get the following:
Apart from that it does only stops the test function itself after an assertion failed. Other test functions will still be run. The other tests may fail as well, but if they don't depend on each other (and they shouldn't, otherwise they should be in the same test function) it may tell more about the actual issue. If you look in the tests of #29 you'll see that you can basically run each test independently. |
This rewrites the existing tests to be used with
pytest
which produces a more helpful output when an assertion failed. It also allows to have multiple separate tests in one file but separated in functions.It also adds some tests when using multiple interfaces in one class and it checks if the results are actually valid instead of just printing them out.