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

Discussion: 'tags' experiment #1

Open
regexident opened this issue Oct 26, 2017 · 4 comments
Open

Discussion: 'tags' experiment #1

regexident opened this issue Oct 26, 2017 · 4 comments

Comments

@regexident
Copy link
Contributor

Hey @mackwic,

I'd like to hear you views on the prospect adding type-safe tagging to rspec (assuming that we decide that we want to support tagging to begin with).

Pretty much all BDD frameworks out there support some form of through tagging:

Generalized Tags:

  • Ruby's Rspec does it through symbols, what would be interned strings in Rust, as used in rustc itself.
  • Swift's Quick does it through [String : Bool], what would be a …Map<String, bool> in Rust.
  • C++'s Catch does it through std::set<std::string>, what would be a …Set<String> in Rust.
  • C++'s bandit does it through std::vector<struct { std::string, bool }>, what would be a Vec<(String, bool)> in Rust.

The benefit us making tags string-based is the obvious one of extensibility. With string-based extensibility comes a cost however: it's hard to catch typos.

Specialized Functions:

Apart from Catch they also all provide API method variants along the lines of fdescribe ("focus describe") or idescribe ("ignore describe").

I'd prefer to not go this route as it simply doesn't scale and can easily be generalized through proper tagging.

Rust Rspec

How I envision rspec's tagging is by providing a carefully chosen set of batteries-included tags, like this:

bitflags! {
    #[derive(Default)]
    struct Tag: usize {
        const FOCUS  = 1 << 0;
        const IGNORE = 1 << 1;
        const SMOKE  = 1 << 2;
    }
}

impl TagsTrait for Tag {}

Which then, assuming an existing test scenario defined like this …

scenario!("scenario", env: usize, || {
    suite!("suite", env: 42, || {
        context!("context A", || {
            // ...
        });
        context!("context B", || {
            // ...
        });
    });
});

… would be used like this …

scenario!("scenario", tags: Tag, env: usize, || {
    // ...
        context!("context A", tags: Tag::IGNORE, || {
            // ...
        });
        // ...
    // ...
});

… causing only scenario : suite : context B to be executed, when run with --skip="ignore".

If the default set of tags is not enough for one's needs then by simply specifying a custom type T: TagsTrait

scenario!("scenario", tags: CustomTag, env: usize, || {
    // ...
        context!("context A", tags: (CustomTag::LOREM | CustomTag::IPSUM), || {
            // ...
        });
        // ...
    // ...
});

… which then could be filtered for through --filter="lorem, ipsum"

The big benefit here is two-fold:

  1. Typing tags: Tag::INGORE instead of tags: Tag::IGNORE would trigger a compile-time error.
  2. Typing --filter="ingore" instead of --filter="ignore" would trigger a run-time error.

Both is possible by having type Tag define a closed set of allowed tags, which still remains open for extension through custom types.

Caveat:

To make tagging ergonomic we would basically be forced to migrate to a use of macros à la suite!(…) instead of ctx.suite(…). I would however see this as a feature, not a bug, as it also greatly improves extensibility through support for optional arguments. I don't expect optional function/method arguments to lang in stable Rust (or even nightly) any time soon, given the current focus on async/await, NLL, RLS, etc.

@JustinRyanH
Copy link

JustinRyanH commented Oct 27, 2017

I don't know if this is the right location, but the TryFrom seems a little bit complicated for what we might need. Especially since we might want multiple attributes out of our tags (such as filtering).

https://github.com/JustinRyanH/experiments/blob/master/tags/src/main.rs
We could make tags argument be &['static str] and there shouldn't be any scenarios for errors.

@regexident
Copy link
Contributor Author

The point of my version was to not give the actual types/tags semantic meaning. They’re no more than a set of names, limited to a predefined set of possible values.

Combined with logic for inclusive as well as exclusive filterung we should be able to provide all options one might need.
I want to avoid featuritis, where we end up adding support for filtering for this, filtering for that, but not if it’s a monday or after sunset, etc.
One of the biggest concerns the Rust programming language has, is orthogonality of its features. And so should rspec, imho.

@mackwic
Copy link
Member

mackwic commented Oct 28, 2017

Not convinced by the need of tagging as the state of rspec right now. Maybe in a year, but for now it doesn't strike me as something needed.

Maybe wait for someone who has a need for it ?

@regexident
Copy link
Contributor Author

See rust-rspec/rspec#42 (comment)

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

No branches or pull requests

3 participants