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

Add expression based test filtering #117

Merged
merged 20 commits into from
Apr 12, 2022

Conversation

Guiguiprim
Copy link
Contributor

@Guiguiprim Guiguiprim commented Mar 18, 2022

As you are up apparently, I push this to open the discussion.

Following what you said, I was thinking maybe something along the line of:

DSL

  • package(name)
  • deps(package_name)
  • rdeps(package_name)
  • test(partial_name)
  • not(expr)
  • expr && expr / expr and expr
  • expr || expr / expr or expr
  • (expr)

Like -E "package(driver) or test(use_driver_)", -E "package(utility_package) and rdeps(utility_package)"

@Guiguiprim Guiguiprim force-pushed the expression-filtering branch 2 times, most recently from b5454bf to 0bdbec9 Compare March 18, 2022 16:58
@sunshowers
Copy link
Member

sunshowers commented Mar 18, 2022

Your general design looks great. Let's do:

  • not x to match and and or, along with a short form ! x
  • x & y as the short form for x and y (not &&) because this is similar to BitAnd for sets
  • x | y and x + y for x or y for similar reasons
  • x - y for x and not y
  • all() for all tests and none() for no tests, sometimes useful

-E "package(utility_package) and rdeps(utility_package)"

Note that this performs the intersection of the two sets, which using the and operator honestly makes a bit less clear.

Some other questions:

  • What sort of parser were you planning to use? My personal suggestion would be to use a parser combinator, and if you do so, nom would be my first suggestion (especially since nextest already uses it as a transitive dependency).
  • How are you planning to do error handling? Ideally we'd integrate with something like miette, following this suggestion by Kat.
  • What is the precedence order among operators?
  • It should be possible to pass in multiple expressions, and I'd expect the result to be the union of all of the tests selected by them.
  • Sorting tests by some specific key might be an interesting thing to do in the future, but we probably shouldn't worry about that right now.
  • I would love it if we could support regexes as well. I think the easiest way is probably to use an re: prefix for regexes, and literal: for literals -- usable wherever a name can be specified. However, this creates an additional challenge -- how to use regexes that have () in them? I'd probably use r'' syntax for them, e.g. package(re:r'ba(r|z)')

What do you think?

@Guiguiprim
Copy link
Contributor Author

You are right, I meant all the tests in package utility_package and all the tests using utility_package, we should be careful not to make it error prone.

  • I was thinking of nom !
  • I didn't though about precedence yet. But I will be careful about it.
  • I can look at miette
  • I had the same reflection about regexes, it adds parsing challenges, but yes r'..' looks right. I guess I would consider literal: default and optional in that case.
  • Multiple expression sound fine.

"Sorting tests by key" you meant filtering on test names ? I guess I can keep it for the end, but as it might be useful for me I'm inclined to do it.

I probably won't have much time in the days to come, so this will be somehow slow to come.

@sunshowers
Copy link
Member

"Sorting tests by key" you meant filtering on test names ? I guess I can keep it for the end, but as it might be useful for me I'm inclined to do it.

I mean sorting the order in which tests are run.

@Guiguiprim
Copy link
Contributor Author

I worked on a first draft of DSL definition and parsing, if you want to see where I'm going.

Nothing finished yet, but I have a first version of a full parsing.

@Guiguiprim
Copy link
Contributor Author

I finally changed a bit the syntax:

  • package(name) => contains name
  • package(=name) => equal name
  • package(/name/) => matches regex name

This makes it easier to parse and (I think) easier to use. /../ is already a convention in some languages for regex.

The current limitations are:

  • equal and contains can not contains )
  • regex can not contains /

In both cases, this should not matter as they are both characters that can not be part of a test or package name.

@Guiguiprim Guiguiprim force-pushed the expression-filtering branch 2 times, most recently from 05e5b67 to 038bef0 Compare March 22, 2022 16:49
@Guiguiprim
Copy link
Contributor Author

Guiguiprim commented Mar 22, 2022

I have a first draft of filtering.

deps() and rdeps() are not implemented yet, but everything else is, so the following works:

  • -E "package(runner)"
  • -E "package(=cargo-nextest)"
  • -E "package(/nextest-.*/)"
  • -E "test(parse) + test(basic) - test(env)"
  • -E "all()"
  • -E "none()"
  • -E "test(parse) & test(expr_)"
  • -E "test(expr_) | test(cargo)"

Still todo:

  • deps() / rdeps()
  • Proper parsing error handling
  • Support multiple -E <expr>
  • Unit tests around matching
  • Find a smart way to condition cargo metadata --no-deps usage if possible

As of now, I have issues implementing deps(), guppy::graph::DependsCache::depends_on() doesn't seem to work...

I opened an issue for this facebookarchive/cargo-guppy#632

@Guiguiprim
Copy link
Contributor Author

Guiguiprim commented Mar 22, 2022

So it looks like I need to find out why my test of -E "deps(runner)" runs no tests...

Copy link
Member

@sunshowers sunshowers 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 looking great -- I'm really excited about how this is progressing!

nextest-runner/src/test_filter/expression.rs Outdated Show resolved Hide resolved
nextest-runner/src/test_filter/expression.rs Outdated Show resolved Hide resolved
nextest-runner/src/test_filter/expression_parsing.rs Outdated Show resolved Hide resolved
@Guiguiprim Guiguiprim force-pushed the expression-filtering branch 3 times, most recently from 737d5e7 to e8b1994 Compare March 22, 2022 21:52
@Guiguiprim
Copy link
Contributor Author

Guiguiprim commented Mar 22, 2022

I'm done with my compilation optimization passes, if you want to have another look.

@sunshowers
Copy link
Member

Thank you—I have other stuff coming up within the next couple of days but will try and take a look soon.

@Guiguiprim Guiguiprim force-pushed the expression-filtering branch 2 times, most recently from fd6566c to c41267d Compare March 31, 2022 16:51
@Guiguiprim
Copy link
Contributor Author

I worked on parsing error with miette.

  • it does make the parsing code more complex but the output is somewhat nice.
  • I also moved all the filtering stuff in a new crate.
  • I added some parsing tests

parsing_error

I still need to add tests on the matching itself.

@Guiguiprim
Copy link
Contributor Author

Here we go, I:

  • Added support for multiple expressions
  • Added filtering tests
  • Conditionally included --no-deps in the call to cargo metadata
  • Made the feature experimental
  • Added a first draft of documentation (this is not my stronger suit)

At this point all is working, and I'm ready for reviews and critics !

Copy link
Member

@sunshowers sunshowers left a comment

Choose a reason for hiding this comment

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

Thank you! It might take a little while for me to get through this, have some errands today and medical things for the rest of the week. The other maintainer is busy as well :)

Copy link
Member

@sunshowers sunshowers left a comment

Choose a reason for hiding this comment

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

Looking fantastic! Just a couple more changes and I'm happy to finish up the rest and get this out there!

Cargo.toml Outdated Show resolved Hide resolved
cargo-nextest/src/errors.rs Outdated Show resolved Hide resolved
nextest-filtering/Cargo.toml Outdated Show resolved Hide resolved
site/src/book/filtering-expression.md Outdated Show resolved Hide resolved
site/src/book/filtering-expression.md Outdated Show resolved Hide resolved
@sunshowers sunshowers added the A-filter-expressions Support for filter expressions label Apr 8, 2022
Comment on lines 14 to 19
- `all()`: include everything
- `test(name-matcher)`: include all tests matching `name-matcher`
- `package(name-matcher)`: include all tests in packages matching `name-matcher`
- `deps(name-matcher)`: include all tests in packages depended by packages matching `name-matcher` and include all tests in packages matching `name-matcher`
- `rdeps(name-matcher)`: include all tests in packages depending on packages matching `name-matcher` and include all tests in packages matching `name-matcher`
- `none()`: include nothing
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a partition operator as well?

Copy link
Contributor Author

@Guiguiprim Guiguiprim Apr 11, 2022

Choose a reason for hiding this comment

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

Like --partition ? this might be a little bit more complex and requires state.

Can we do it after, in an other PR ?

Copy link
Member

Choose a reason for hiding this comment

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

Oh yeah absolutely! I figured it would require some state, yeah. We also need to figure out the behavior with the --partition flag (I suspect we want to disallow use of both the partition() operator and the --partition flag, and maybe deprecate the --partition flag).

site/src/book/filtering-expression.md Outdated Show resolved Hide resolved
site/src/book/filtering-expression.md Outdated Show resolved Hide resolved
Copy link
Member

@sunshowers sunshowers left a comment

Choose a reason for hiding this comment

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

Looks great -- just a couple more changes, and I'm happy to land this and finish it up myself if you like.

Comment on lines +27 to +28
- can contains escaped closing parenthesis: `\)`
- can contains unicode sequence: `\u{xxx}` (where `xxx` is an 1 to 6 digits hexadecimal number)
Copy link
Member

Choose a reason for hiding this comment

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

Also \r, \n etc, right?


Precedences from lowest to highest:
- `not`, `()`
- `&`, `|`, `-`
Copy link
Member

Choose a reason for hiding this comment

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

How does an expression with & and | get resolved? In general I'd expect us to follow a similar pattern to https://doc.rust-lang.org/reference/expressions.html#expression-precedence -- from highest to lowest:

  • ()
  • !, not
  • &, and
  • |, +, or

Only real difference is that + is grouped with |.

@sunshowers
Copy link
Member

Going to land this and clean it up, thanks again for all your incredible efforts here.

@sunshowers sunshowers merged commit e90409b into nextest-rs:main Apr 12, 2022
@Guiguiprim Guiguiprim deleted the expression-filtering branch April 13, 2022 06:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-filter-expressions Support for filter expressions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants