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

First prototype of native pytest plugin for launch based tests #528

Merged
merged 48 commits into from
Nov 1, 2021

Conversation

ivanpauno
Copy link
Member

Some early discussion can be found here: https://gist.github.com/ivanpauno/761a7cbca83335ea081512f3494304ba.

I'm planning to add support for shutdown tests.
There are also a lot of other details that can be discussed.

@ivanpauno ivanpauno added the enhancement New feature or request label Sep 7, 2021
@ivanpauno ivanpauno self-assigned this Sep 7, 2021
Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

First pass, great stuff!

launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
):
mod_locals[name] = getter(scope)
else:
mod_locals[name] = getter(scope)
Copy link
Contributor

Choose a reason for hiding this comment

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

@ivanpauno I think I understand the override-able fixture magic, but doesn't this imply fixtures of different scopes cannot coexist in the same source file? I mean, you could have 3 tests using a module scope fixture and 3 tests using a function scope fixture and there wouldn't be any overlap, would it?

Copy link
Member Author

@ivanpauno ivanpauno Sep 8, 2021

Choose a reason for hiding this comment

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

If you have one launch_test fixture with class scope and one with function scope, the launch_service function will be class scoped.

This is required because of the following issue:

@pytest.fixture()
def foo():
  ...

@pytest.fixture(scope='module')
def bar(foo):
  ...

In that case you get a "scope mismatch error", because you cannot access a fixture with a lower scope from another fixture.

The fact that the launch_service/event_loop is automatically redefined with a larger scope only means that it might be shared by both launch_test fixtures, which is not necessarly a problem.
We could actually create one launch_service per launch_test fixture, and don't use a fixture for that at all.
For the event_loop though, you can only have one active event loop in a thread, so it would be an issue anyway.

Maybe in the future pytest solves this issue, see pytest-dev/pytest#6101.

Copy link
Contributor

Choose a reason for hiding this comment

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

In that case you get a "scope mismatch error", because you cannot access a fixture with a lower scope from another fixture.

Aye, the launch_service fixture scope has to be at least as large as the largest scope of all launch fixtures found (BTW isn't that example reversed?).

The fact that the launch_service/event_loop is automatically redefined with a larger scope only means that it might be shared by both launch_test fixtures, which is not necessarly a problem.

I see two problems. Unrelated larger scope fixtures creeping into tests with smaller scope fixtures, and a large scope launch fixture being shutdown (which IIRC is a terminal state) by a smaller scope fixture upon finalization. The first one is not really a problem if we enforce a 1-to-1 relationship between tests and launch fixtures. The second one is.

We could actually create one launch_service per launch_test fixture, and don't use a fixture for that at all.

Having separate instances is probably easiest, yeah. Alternatively, we can make LaunchService shutdowns not a terminal state (i.e. allow re-running after a shutdown).

Copy link
Member Author

Choose a reason for hiding this comment

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

(BTW isn't that example reversed?).

lol yes, fixed.

and a large scope launch fixture being shutdown (which IIRC is a terminal state) by a smaller scope fixture upon finalization

Yes, I think we can detect that case and warn the user though.

Copy link
Member

@wjwwood wjwwood left a comment

Choose a reason for hiding this comment

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

I left a few comments, but honestly the pytest stuff is a bit over my head (opaque without some special knowledge it seems).

launch/launch/launch_service.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
@hidmic
Copy link
Contributor

hidmic commented Sep 7, 2021

On a related note, would it make sense to roll out a separate launch_pytest package? Imports would be shorter and it's easier to draw a line between this new thing and launch_testing. WDYT @ivanpauno @wjwwood @jacobperron ?

@ivanpauno
Copy link
Member Author

On a related note, would it make sense to roll out a separate launch_pytest package? Imports would be shorter and it's easier to draw a line between this new thing and launch_testing. WDYT @ivanpauno @wjwwood @jacobperron ?

The imports wouldn't really be much shorter.
Having a separate package sounds fine to me, I actually started that way and then integrated the changes in launch_testing.

There're a few things that I can reuse from the current launch_testing though, like the ReadyToTest action.
I'm not sure if we can also take advantage of some of the existent tools to e.g. launch a new process, capture process stdout, etc.

@hidmic
Copy link
Contributor

hidmic commented Sep 8, 2021

I'm not sure if we can also take advantage of some of the existent tools to e.g. launch a new process, capture process stdout, etc.

Maybe, to some extent. We can probably do better but it's not a bad start.

@ivanpauno
Copy link
Member Author

@hidmic could you take another look?

I have added support for shutdown tests, including generator/asyncgenerators based ones.
The code needs more polishing, more tests, and maybe we should handle launch_service higyene in a better way; but I don't think we need many more features (except for improving tools to expect for process output/launch process/etc, but that's kind of unrelated).

Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

Alright, I left some comments, questions, suggestions, and ideas. This is complex code, sorry if I mixed things up 😅

launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
args[name] = value
if inspect.iscoroutinefunction(func):
pyfuncitem.obj = wrap_coroutine(func, args, event_loop, before_test)
elif inspect.isgeneratorfunction(func):
Copy link
Contributor

Choose a reason for hiding this comment

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

@ivanpauno meta: does it make sense to have pure post-shutdown tests that are sync or async generators?

Copy link
Member Author

Choose a reason for hiding this comment

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

Not really, I think the shutdown flag is being ignored in that case, though maybe better we should show an error.
I'm also having problems to support pure post-shutdown tests, so maybe I'll delete that option altogether.

Copy link
Member Author

Choose a reason for hiding this comment

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

See bf8c790

launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
@clalancette
Copy link
Contributor

On a related note, would it make sense to roll out a separate launch_pytest package? Imports would be shorter and it's easier to draw a line between this new thing and launch_testing. WDYT @ivanpauno @wjwwood @jacobperron ?

The imports wouldn't really be much shorter.
Having a separate package sounds fine to me, I actually started that way and then integrated the changes in launch_testing.

On the one hand, replacing launch_testing completely with the new pytest-based thing is where we eventually want to be. On the other hand, just replacing all of the guts of launch_testing is probably going to break all downstream packages that have managed to use launch_testing successfully.

Because of that, my inclination is to make a new package, and once that is in have import launch_testing raise a DeprecationWarning in Humble. Then for I-Turtle we can remove the old launch_testing. What do you think about that plan?

On a different meta-note, I'd also like to see some examples on how to use this new functionality in downstream packages. For instance, how would I change https://github.com/ros-teleop/teleop_tools/blob/foxy-devel/joy_teleop/test/test_action_fibonacci.py to use this new package? Maybe those examples should be in here, or maybe they should be over in https://github.com/ros2/examples, or maybe they should be in the documentation.

@ros-discourse
Copy link

This pull request has been mentioned on ROS Discourse. There might be relevant details there:

https://discourse.ros.org/t/ros-2-tsc-meeting-minutes-2021-9-16/22372/1

@wjwwood
Copy link
Member

wjwwood commented Sep 24, 2021

On the one hand, replacing launch_testing completely with the new pytest-based thing is where we eventually want to be. On the other hand, just replacing all of the guts of launch_testing is probably going to break all downstream packages that have managed to use launch_testing successfully.

I'm fine with this approach, to be honest. It also opens us up to being able to do launch_next_new_cool_testing_thing down the line when that comes around.

Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

Second (or third?) pass!

launch_testing/launch_testing/pytest/fixture.py Outdated Show resolved Hide resolved
Comment on lines 89 to 93
raise RuntimeError(
'launch_pytest fixtures must return a LaunchDescription '
'containing only one ReadyToTest action'
)
Copy link
Contributor

Choose a reason for hiding this comment

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

@ivanpauno meta: just like launch_testing, this cannot really detect all ReadyToTest instances (thinking of includes). We need to document the fact that we'll simply pick up the first one found we can statically find. In which case, I'm slightly inclined to make this a warning. Thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

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

A warn would be fine as well.

launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
return launch.LaunchDescription([
launch_testing.util.KeepAliveProc(),
launch_testing.actions.ReadyToTest(),
])
Copy link
Contributor

Choose a reason for hiding this comment

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

@ivanpauno It'd be nice to test a launch_description that yields (though I don't see why it wouldn't work)

launch_testing/launch_testing/pytest/plugin.py Outdated Show resolved Hide resolved
@ivanpauno
Copy link
Member Author

ivanpauno commented Oct 14, 2021

FYI, force-pushed to rebase with master

@ivanpauno
Copy link
Member Author

The reason why the Rpr checker fails is interesting.

It seems that we're using the pip installed pytest in the buildfarm, due to pytest-rerunfailures being installed from pip (no debian package available in Focal).
pytest-asyncio from python3-pytest-asyncio (via rosdep), and the version available in ubuntu debians is not compatible with the pip installed pytest.

I'm not using much of pytest-asyncio, only a fixture that I can duplicate in our plugin, but the issue is a bit unfortunate.

@ivanpauno
Copy link
Member Author

Note to self: Make sure our plugin works with both the pip and debian package version of pytest.

@ivanpauno
Copy link
Member Author

I'm not using much of pytest-asyncio, only a fixture that I can duplicate in our plugin, but the issue is a bit unfortunate.

Issue fixed in 8514c4e

Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

This is amazing @ivanpauno.

Barring minor comments, I'm happy with it if CI's happy with it.

launch/launch/actions/execute_process.py Outdated Show resolved Hide resolved
launch/launch/actions/execute_process.py Outdated Show resolved Hide resolved
launch_pytest/launch_pytest/actions/__init__.py Outdated Show resolved Hide resolved
launch_pytest/launch_pytest/fixture.py Show resolved Hide resolved
launch_pytest/launch_pytest/plugin.py Outdated Show resolved Hide resolved
launch_pytest/launch_pytest/plugin.py Show resolved Hide resolved
launch_pytest/launch_pytest/plugin.py Outdated Show resolved Hide resolved
launch_pytest/launch_pytest/plugin.py Outdated Show resolved Hide resolved
launch_pytest/launch_pytest/tools/process.py Show resolved Hide resolved
@ivanpauno
Copy link
Member Author

@hidmic I have addressed you last comments.
I have also added a lot of test cases in 57656fe, 5dfb905, using the pytester plugin.

ivanpauno and others added 7 commits October 29, 2021 11:51
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno ivanpauno force-pushed the ivanpauno/launch-testing-pytest-prototype branch from 74bbec3 to af0c896 Compare October 29, 2021 14:54
@ivanpauno
Copy link
Member Author

Force pushed to resolve conflicts after #454 was merged.

This reverts commit af0c896.

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno
Copy link
Member Author

ivanpauno commented Oct 29, 2021

Windows is passing after 6c4f4d8 and 0279cf7:

  • Windows Build Status

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno
Copy link
Member Author

@hidmic could you take another look?
I have fixed the windows issues mentioned above and I've also introduced compatibility with the version of pytest shipped in Focal, see a9300bf (we're using the pip install version in CI).

@ivanpauno ivanpauno requested a review from hidmic October 29, 2021 20:30
@ivanpauno
Copy link
Member Author

ivanpauno commented Oct 29, 2021

CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status (unrelated failures)

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
@ivanpauno ivanpauno merged commit edcca09 into master Nov 1, 2021
@delete-merged-branch delete-merged-branch bot deleted the ivanpauno/launch-testing-pytest-prototype branch November 1, 2021 18:07
@ros-discourse
Copy link

This pull request has been mentioned on ROS Discourse. There might be relevant details there:

https://discourse.ros.org/t/ros-2-tsc-meeting-minutes-2021-10-28/22947/1

@Blast545
Copy link
Contributor

Blast545 commented Nov 3, 2021

@ivanpauno Can I ask you to take a look to this PR? it introduced some regressions to the repeated jobs of the buildfarm:

nightly_linux_repeated#2459
nightly_linux-aarch64_repeated#1766/
nightly_win_rep#2416
nightly_osx_repeated#2526

I imagine it's missing a cleanup that's only showing once the tests are run in a sequence.

@ivanpauno
Copy link
Member Author

@ivanpauno Can I ask you to take a look to this PR? it introduced some regressions to the repeated jobs of the buildfarm:

Interesting... I will take a look

@ros-discourse
Copy link

This pull request has been mentioned on ROS Discourse. There might be relevant details there:

https://discourse.ros.org/t/ros-2-tsc-meeting-minutes-2021-11-18/23209/1

@wep21
Copy link

wep21 commented Dec 2, 2021

@ivanpauno @hidmic Is backporting launch_pytest to galactic acceptable? If so, I would like to create a backport PR for launch_pytest.

@ivanpauno
Copy link
Member Author

@ivanpauno @hidmic Is backporting launch_pytest to galactic acceptable? If so, I would like to create a backport PR for launch_pytest.

I think that up to now it hasn't been used enough to say it's worth backporting.
I would rather start using it more and prove its usefulness before backporting it.

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

Successfully merging this pull request may close these issues.

8 participants