ci

CI for all possible combinations of features in Cargo.toml.
git clone https://git.philomathiclife.com/repos/ci
Log | Files | Refs | README

commit 8e3e5829b00974dd113cc245d0c297ee6f108b5e
parent 7d3a9f3b8de69683b9cfc0a3dc86ce0d574bdf69
Author: Zack Newman <zack@philomathiclife.com>
Date:   Mon, 19 May 2025 10:42:01 -0600

add ignore tests

Diffstat:
MCargo.toml | 47++++++++++++++++++++++++++++++++++++++++++++++-
MREADME.md | 8+++++---
Msrc/args.rs | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/main.rs | 47+++++------------------------------------------
4 files changed, 139 insertions(+), 61 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -10,7 +10,52 @@ name = "ci" readme = "README.md" repository = "https://git.philomathiclife.com/repos/ci/" rust-version = "1.86.0" -version = "0.2.3" +version = "0.3.0" + +[lints.rust] +unknown_lints = { level = "deny", priority = -1 } +future_incompatible = { level = "deny", priority = -1 } +let_underscore = { level = "deny", priority = -1 } +missing_docs = { level = "deny", priority = -1 } +nonstandard_style = { level = "deny", priority = -1 } +refining_impl_trait = { level = "deny", priority = -1 } +rust_2018_compatibility = { level = "deny", priority = -1 } +rust_2018_idioms = { level = "deny", priority = -1 } +rust_2021_compatibility = { level = "deny", priority = -1 } +rust_2024_compatibility = { level = "deny", priority = -1 } +unsafe_code = { level = "deny", priority = -1 } +unused = { level = "deny", priority = -1 } +warnings = { level = "deny", priority = -1 } + +[lints.clippy] +all = { level = "deny", priority = -1 } +cargo = { level = "deny", priority = -1 } +complexity = { level = "deny", priority = -1 } +correctness = { level = "deny", priority = -1 } +nursery = { level = "deny", priority = -1 } +pedantic = { level = "deny", priority = -1 } +perf = { level = "deny", priority = -1 } +restriction = { level = "deny", priority = -1 } +style = { level = "deny", priority = -1 } +suspicious = { level = "deny", priority = -1 } +# Noisy, opinionated, and likely don't prevent bugs or improve APIs. +arbitrary_source_item_ordering = "allow" +blanket_clippy_restriction_lints = "allow" +exhaustive_enums = "allow" +exhaustive_structs = "allow" +implicit_return = "allow" +min_ident_chars = "allow" +missing_trait_methods = "allow" +multiple_crate_versions = "allow" +pub_with_shorthand = "allow" +pub_use = "allow" +question_mark_used = "allow" +ref_patterns = "allow" +return_and_then = "allow" +self_named_module_files = "allow" +single_call_fn = "allow" +single_char_lifetime_names = "allow" +unseparated_literal_suffix = "allow" [dependencies] serde = { version = "1.0.219", default-features = false } diff --git a/README.md b/README.md @@ -23,18 +23,20 @@ invoking `cargo` with each possible combination of features, this handles it aut * `clippy`: `cargo clippy -q` is invoked for each combination of features. * `doc_tests`: `cargo t -q --doc` is invoked for each combination of features. * `tests`: `cargo t -q --tests` is invoked for each combination of features. +* `ignored`: `cargo t -q --tests -- --ignored` is invoked for each combination of features. +* `include-ignored`: `cargo t -q --tests -- --include-ignored` is invoked for each combination of features. * `--color`: `--color always` is passed to the above commands; otherwise without this option, `--color never` is passed. * `--dir <path to directory Cargo.toml is in>`: `ci` changes the working directory to the passed path (after canonicalizing it) before executing. Without this, the working directory and all ancestor directories are searched for `Cargo.toml` before changing the working directory to its location. -When `clippy`, `doc_tests`, and `tests` are not passed; then all three are invoked. +When `clippy`, `doc_tests`, `tests`, `ignored`, and `include-ignored` are not passed; then `clippy`, `doc_tests`, +and `tests` are all invoked. ## Limitations -Any use of `compile_error` _not_ related to incompatible features will be silently ignored. Any tests that -are marked `ignored` will not be run. +Any use of `compile_error` _not_ related to incompatible features will be silently ignored. ## Minimum Supported Rust Version (MSRV) diff --git a/src/args.rs b/src/args.rs @@ -30,7 +30,7 @@ impl Display for ArgsErr { ), Self::InvalidOption(ref arg) => write!( f, - "{arg} is an invalid option. No arguments or exactly one of 'clippy', 'doc_tests', or 'tests' is allowed followed by nothing, '--color', or '--dir' and the path to the directory ci should run in" + "{arg} is an invalid option. No arguments or exactly one of 'clippy', 'doc_tests', 'tests', 'ignored', or 'include-ignored' is allowed followed by nothing, '--color', or '--dir' and the path to the directory ci should run in" ), Self::DuplicateOption(ref arg) => write!(f, "{arg} was passed more than once"), Self::MissingPath => { @@ -60,6 +60,14 @@ pub enum Opts { /// /// The contained `bool` is `true` iff `--color always` should be used; otherwise `--color never` is used. Tests(bool), + /// Variant when `ignored` is passed. + /// + /// The contained `bool` is `true` iff `--color always` should be used; otherwise `--color never` is used. + IgnoredTests(bool), + /// Variant when `include-ignored` is passed. + /// + /// The contained `bool` is `true` iff `--color always` should be used; otherwise `--color never` is used. + IncludeIgnoredTests(bool), } /// Kind of successful completion of a command. pub enum Success { @@ -77,7 +85,9 @@ impl Opts { Self::None(color, _) | Self::Clippy(color) | Self::DocTests(color) - | Self::Tests(color) => color, + | Self::Tests(color) + | Self::IgnoredTests(color) + | Self::IncludeIgnoredTests(color) => color, } } /// Changes `self` such that it should contain color. @@ -86,7 +96,9 @@ impl Opts { Self::None(ref mut color, _) | Self::Clippy(ref mut color) | Self::DocTests(ref mut color) - | Self::Tests(ref mut color) => *color = true, + | Self::Tests(ref mut color) + | Self::IgnoredTests(ref mut color) + | Self::IncludeIgnoredTests(ref mut color) => *color = true, } } /// Returns the arguments to pass to the command based on `self`. @@ -108,21 +120,26 @@ impl Opts { "--doc", "--no-default-features", ], - Self::Tests(color) => vec![ - "t", - "-q", - "--color", - if color { "always" } else { "never" }, - "--tests", - "--no-default-features", - ], + Self::Tests(color) | Self::IgnoredTests(color) | Self::IncludeIgnoredTests(color) => { + vec![ + "t", + "-q", + "--color", + if color { "always" } else { "never" }, + "--tests", + "--no-default-features", + ] + } } } /// Returns the appropriate [`Stdio`] to be used to capture `stdout` based on `self`. fn stdout(self) -> Stdio { match self { Self::None(_, _) | Self::Clippy(_) => Stdio::null(), - Self::DocTests(_) | Self::Tests(_) => Stdio::piped(), + Self::DocTests(_) + | Self::Tests(_) + | Self::IgnoredTests(_) + | Self::IncludeIgnoredTests(_) => Stdio::piped(), } } /// Returns the entire command based on `self`. @@ -148,6 +165,7 @@ impl Opts { /// /// `self` is mutated iff `Self::None(_, false)` and `cargo t -q --doc` errors /// due to a lack of library target; in which case, the second `bool` becomes `true`. + #[expect(clippy::else_if_without_else, reason = "don't want it")] #[expect(clippy::too_many_lines, reason = "not too many")] pub fn run_cmd( &mut self, @@ -175,17 +193,32 @@ impl Opts { } }) } - Self::Clippy(color) | Self::DocTests(color) | Self::Tests(color) => { + Self::Clippy(color) + | Self::DocTests(color) + | Self::Tests(color) + | Self::IgnoredTests(color) + | Self::IncludeIgnoredTests(color) => { let mut args = self.args(); if !features.is_empty() { args.push("--features"); args.push(features); } - if matches!(*self, Self::DocTests(_) | Self::Tests(_)) { + if matches!( + *self, + Self::DocTests(_) + | Self::Tests(_) + | Self::IgnoredTests(_) + | Self::IncludeIgnoredTests(_) + ) { args.push("--"); args.push("--color"); args.push(if color { "always" } else { "never" }); } + if matches!(*self, Self::IgnoredTests(_)) { + args.push("--ignored"); + } else if matches!(*self, Self::IncludeIgnoredTests(_)) { + args.push("--include-ignored"); + } let output = Command::new("cargo") .stderr(Stdio::piped()) .stdin(Stdio::null()) @@ -254,13 +287,24 @@ impl Opts { msg.push_str(" --features "); msg.push_str(features); } - if matches!(*self, Self::DocTests(_) | Self::Tests(_)) { + if matches!( + *self, + Self::DocTests(_) + | Self::Tests(_) + | Self::IgnoredTests(_) + | Self::IncludeIgnoredTests(_) + ) { msg.push_str(if color { " -- --color always" } else { " -- --color never" }); } + if matches!(*self, Self::IgnoredTests(_)) { + msg.push_str(" --ignored"); + } else if matches!(*self, Self::IncludeIgnoredTests(_)) { + msg.push_str(" --include-ignored"); + } Err(E::Cmd(msg)) }, ) @@ -313,6 +357,30 @@ impl Opts { opt = Self::Tests(false); } } + "ignored" => { + if !matches!(opt, Self::None(_, _)) { + return Err(E::Args(ArgsErr::InvalidOption(String::from("ignored")))); + } else if opt.contains_color() { + return Err(E::Args(ArgsErr::InvalidOption(String::from("--color")))); + } else if path.is_some() { + return Err(E::Args(ArgsErr::InvalidOption(String::from("--dir")))); + } else { + opt = Self::IgnoredTests(false); + } + } + "include-ignored" => { + if !matches!(opt, Self::None(_, _)) { + return Err(E::Args(ArgsErr::InvalidOption(String::from( + "include-ignored", + )))); + } else if opt.contains_color() { + return Err(E::Args(ArgsErr::InvalidOption(String::from("--color")))); + } else if path.is_some() { + return Err(E::Args(ArgsErr::InvalidOption(String::from("--dir")))); + } else { + opt = Self::IncludeIgnoredTests(false); + } + } "--color" => { if opt.contains_color() { return Err(E::Args(ArgsErr::DuplicateOption(arg))); diff --git a/src/main.rs b/src/main.rs @@ -26,56 +26,19 @@ //! * `clippy`: `cargo clippy -q` is invoked for each combination of features. //! * `doc_tests`: `cargo t -q --doc` is invoked for each combination of features. //! * `tests`: `cargo t -q --tests` is invoked for each combination of features. +//! * `ignored`: `cargo t -q --tests -- --ignored` is invoked for each combination of features. +//! * `include-ignored`: `cargo t -q --tests -- --include-ignored` is invoked for each combination of features. //! * `--color`: `--color always` is passed to the above commands; otherwise without this option, `--color never` is //! passed. //! * `--dir <path to directory Cargo.toml is in>`: `ci` changes the working directory to the passed path (after //! canonicalizing it) before executing. Without this, the current directory is used. //! -//! When `clippy`, `doc_tests`, and `tests` are not passed; then all three are invoked. +//! When `clippy`, `doc_tests`, `tests`, `ignored`, and `include-ignored` are not passed; then `clippy`, +//! `doc_tests`, and `tests` are invoked. //! //! ## Limitations //! -//! Any use of [`compile_error`] _not_ related to incompatible features will be silently ignored. Any `ignored` -//! tests are not run. -#![deny( - unknown_lints, - future_incompatible, - let_underscore, - missing_docs, - nonstandard_style, - rust_2018_compatibility, - rust_2018_idioms, - rust_2021_compatibility, - rust_2024_compatibility, - unsafe_code, - unused, - unused_crate_dependencies, - warnings, - clippy::all, - clippy::cargo, - clippy::complexity, - clippy::correctness, - clippy::nursery, - clippy::pedantic, - clippy::perf, - clippy::restriction, - clippy::style, - clippy::suspicious -)] -#![expect( - clippy::blanket_clippy_restriction_lints, - clippy::arbitrary_source_item_ordering, - clippy::implicit_return, - clippy::min_ident_chars, - clippy::missing_trait_methods, - clippy::question_mark_used, - clippy::ref_patterns, - clippy::return_and_then, - clippy::single_call_fn, - clippy::single_char_lifetime_names, - clippy::unseparated_literal_suffix, - reason = "noisy, opinionated, and likely doesn't prevent bugs or improve APIs" -)] +//! Any use of [`compile_error`] _not_ related to incompatible features will be silently ignored. extern crate alloc; /// Functionality related to the passed arguments. mod args;