-
Notifications
You must be signed in to change notification settings - Fork 93
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
Refactor tests and setup #1968
Refactor tests and setup #1968
Conversation
@@ -210,32 +210,6 @@ def _load_module(module_name, path): | |||
_load_module("mscolab_settings", path) | |||
|
|||
|
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.
Removing this will Error all tests with an unhandled QMessageBox
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 think that is true. I just had a test pass with this fixture removed even though it showed a message box and failed with my modified fixture.
@@ -248,3 +222,7 @@ def configure_testsetup(request): | |||
VIRT_DISPLAY.stop() | |||
else: | |||
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.
the new approach is not to start on any test a mscolab service but once per test class. We need at the end to terminate all processes
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 that really a good idea? We could run into issues where one test affects the outcome of another due to the shared mscolab instance, which could be a nightmare to debug. Also, I do not really like the implementation with this global variable here, couldn't this be a fixture sort of like this (pseudocode):
@pytest.fixture()
def mscolab_server():
server = start_mscolab_server()
yield server
server.shutdown()
?
Every test that needs a running mscolab server then should just use the fixture. That way we could even use the scope of the fixture to decide how "shared" we want the instance to be.
For what it's worth, I would prefer to get rid of test classes entirely, anyway. A yield fixture is much better suited for setup and teardown code, which leaves classes only as an organizational unit to group tests together. But modules, in turn, do that better.
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 is not a global variable, it is defined as module local variable.
That's a great article about https://betterprogramming.pub/alternatives-to-using-globals-in-python-a3b2a7d5411b
we need for xdist calls servers running on different ports,
try it.
what do you mean by removing test classes entirely, I think setup and teardown may become complicated. But 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.
each test cleans up the database, and xdist is currently called on --dist loadfile
The requirement by this is only different port per file.
I also like to have different servers on each run, which I introduced before.
Anyway any test needs independent from each other. So not having a test which creates data and another test which tries to vaildate something based on that.
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 do you mean by removing test classes entirely, I think setup and teardown may become complicated. But lets see.
The recommended way (by pytest) to have setup and teardown code is using a yield fixture. You can read more about that here: https://docs.pytest.org/en/6.2.x/fixture.html#yield-fixtures-recommended
They have an example there to demonstrate how user creation and deletion would work with fixtures, in an example testing a hypothetical email server. No class needed.
Anyway any test needs independent from each other. So not having a test which creates data and another test which tries to vaildate something based on that.
Hypothetically, what would happen with these shared mscolab servers if we had a test for user deletion trying to delete user "UV10@uv10" in Test_Mscolab_connect_window
, but also try to login to this user in test_login
? My concern is that we might run into similar, but less obvious, race conditions in the future.
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 is not a global variable, it is defined as module local variable.
In
Lines 226 to 228 in e60909c
def pytest_sessionfinish(session, exitstatus): | |
for process in MSCOLAB_PROCESSES: | |
process.terminate() |
MSCOLAB_PROCESSES
a global variable in the scope of the test session, then? We have one instance of this variable in which we accumulate all the running mscolab processes while executing the tests.
If I understand that right, with this PR we would also keep all the processes running until the end of the session, instead of stopping them as soon as they are not needed anymore.
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 do you mean by removing test classes entirely, I think setup and teardown may become complicated. But lets see.
The recommended way (by pytest) to have setup and teardown code is using a yield fixture. You can read more about that here: https://docs.pytest.org/en/6.2.x/fixture.html#yield-fixtures-recommended They have an example there to demonstrate how user creation and deletion would work with fixtures, in an example testing a hypothetical email server. No class needed.
Anyway any test needs independent from each other. So not having a test which creates data and another test which tries to vaildate something based on that.
Hypothetically, what would happen with these shared mscolab servers if we had a test for user deletion trying to delete user "UV10@uv10" in
Test_Mscolab_connect_window
, but also try to login to this user intest_login
? My concern is that we might run into similar, but less obvious, race conditions in the future.
connect_window and
test_login has to setup the user,
after each test per teardown the database is cleared.
Any test has to do all the steps for testing an approach.
when the tests are paralell running by xdist, any worker creates new tmp dirs
two workers don't access the same database
We had the described problems before, that's why tests currently not based on some others.
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 would prefer more functional tests, without using the UI. At least everything what can be tested without the UI should be done.
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.
At least everything what can be tested without the UI should be done.
I have to disagree on that. We should test at the "level" at which users interact with the application as well, and that is the UI. There is no point in testing if a login works if we don't test that the actual login button works also.
Or to phrase it differently: everything that can be done with the UI cannot be tested without 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.
we do UI tests already in many ways also by the tutorials, But we also have a lot helper functions which also work without the UI and these should become tested if possibile without the UI.
@@ -96,7 +96,7 @@ jobs: | |||
&& source /opt/conda/etc/profile.d/conda.sh \ | |||
&& source /opt/conda/etc/profile.d/mamba.sh \ | |||
&& mamba activate mss-${{ inputs.branch_name }}-env \ | |||
&& pytest -vv -n 6 --dist loadfile --max-worker-restart 0 tests \ |
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.
currently will loose all tests assigned to a worker when that crashes
@@ -1056,8 +1056,11 @@ def handle_delete_operation(self): | |||
self.reload_operations() | |||
self.signal_operation_removed.emit(self.active_op_id) | |||
logging.debug("activate local") | |||
self.ui.listFlightTracks.setCurrentRow(0) | |||
self.ui.activate_selected_flight_track() | |||
if self.ui.listFlightTracks.count() == 0: |
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.
in mscolab we can remove "all" operations from the list, while we can't in the local flight path (left site of the UI)
When we do this, we have to switch to an active operation, which is done by switch_to_local
mslib/msui/mscolab.py
Outdated
@@ -1986,11 +1989,9 @@ def logout(self): | |||
|
|||
self.operation_archive_browser.hide() | |||
|
|||
# Don't try to activate local flighttrack while testing | |||
if "pytest" not in sys.modules: |
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 is not needed when the tests and code are updated
504 passed, 27 skipped, 208 warnings in 217.48s (0:03:37) |
tests/_test_msui/test_mscolab.py
Outdated
def test_login(self): | ||
@mock.patch("PyQt5.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.No) | ||
def test_login(self, mockquestion): | ||
self._connect_to_mscolab() | ||
self._login(self.userdata[0], self.userdata[2]) |
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 disagree with the mocking here, we shouldn't just "click no" and be done with it. The issue here, why the message box pops up at all, is that in the msui config used in the test the "MSS_auth" does not have self.userdata[0]
set as the username/email for the mscolab server we connect to here. With my approach to this:
MSS/tests/_test_msui/test_mscolab.py
Lines 123 to 126 in ee1c48c
def test_login(self): | |
self._connect_to_mscolab() | |
modify_config_file({"MSS_auth": {self.url: self.userdata[0]}}) | |
self._login(self.userdata[0], self.userdata[2]) |
mockquestion.assert_called_once_with(<args>)
in every test. I think that is something we should just do once in a separate test case.
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.
Case in point: there was a message box appearing in test_get_airspaces
saying there was no airspace data in the file. That made no sense. It led me to discover this bug: https://github.com/Open-MSS/MSS/pull/1967/files#diff-ce023988bd24cea5863ed80b6ef4058629448da2f64480cd8dc31f06c5186a57R254-R256
On the other hand, in test_get_airspaces_missing_data
we expected exactly this message box, so there we should mock the message box but then also assert that we get what we expect: https://github.com/Open-MSS/MSS/pull/1967/files#diff-d76a934985b02e5c8cafb478f5e5221eacd80d770c69798e9370ebb2232d8f9dR206-R215
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.
Obviously this argument applies to most of the other locations where this message box is mocked here as well.
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.
that's why I thought when you incorporate changes of this into your branch you can decide this on the merge or cherry pick, afterwards we close this PR and you can verify the other ideas.
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 think we should combine this larger architectural change with my, I'd argue, "less invasive" PR. I almost have my PR in a state in which I think it would be merge-able, getting rid of these warnings now and cleaning up the CI outputs, and then we could have a look at this more fundamental PR.
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.
ok
won't be done |
Purpose of PR?:
Fixes #1957 for the mscolab part
Does this PR introduce a breaking change?
If the changes in this PR are manually verified, list down the scenarios covered::
Additional information for reviewer? :
the PR reduces the amount of servers needed for mscolab tests. Also a bug in the decription for a category change fixed.
For develop the tests were not adjusted to the UI refactoring. The conftest has removed all QMessage boxes.
Some errors we had were related that we don't loaded an initial flight track to the window for tests. Other Time out Blockers occure when some of the Questions are not mocked.
Does this PR results in some Documentation changes?
If yes, include the list of Documentation changes
Checklist:
<type>: <subject>