iai_callgrind/
bin_bench.rs

1use std::ffi::{OsStr, OsString};
2use std::fmt::Display;
3use std::fs::File;
4use std::io::{BufRead, BufReader};
5use std::net::SocketAddr;
6use std::path::{Path, PathBuf};
7use std::time::Duration;
8
9use derive_more::AsRef;
10use iai_callgrind_macros::IntoInner;
11
12use crate::{DelayKind, Stdin, Stdio, ValgrindTool, __internal};
13
14/// [low level api](`crate::binary_benchmark_group`) only: Create a new benchmark id
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct BenchmarkId(String);
17
18/// The configuration of a binary benchmark
19///
20/// The [`BinaryBenchmarkConfig`] can be specified at multiple levels and configures the benchmarks
21/// at this level. For example a [`BinaryBenchmarkConfig`] at (`main`)[`crate::main`] level
22/// configures all benchmarks. A configuration at [`group`](crate::binary_benchmark_group) level
23/// configures all benchmarks in this group inheriting the configuration of the `main` level and if
24/// not specified otherwise overwrites the values of the `main` configuration if the option is
25/// specified in both [`BinaryBenchmarkConfig`]s. The deeper levels are the
26/// (`#[binary_benchmark] attribute`)[`crate::binary_benchmark`], then `#[bench]` and the
27/// `#[benches]` attribute.
28///
29/// # Examples
30///
31/// ```rust
32/// # use iai_callgrind::binary_benchmark_group;
33/// # binary_benchmark_group!(name = some_group; benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
34/// use iai_callgrind::{BinaryBenchmarkConfig, main, Callgrind};
35///
36/// main!(
37///     config = BinaryBenchmarkConfig::default()
38///         .tool(Callgrind::with_args(["toggle-collect=something"]));
39///     binary_benchmark_groups = some_group
40/// );
41/// ```
42#[derive(Debug, Default, Clone, IntoInner, AsRef)]
43pub struct BinaryBenchmarkConfig(__internal::InternalBinaryBenchmarkConfig);
44
45/// [low level api](`crate::binary_benchmark_group`) only: The top level struct to add binary
46/// benchmarks to
47///
48/// This struct doesn't need to be instantiated by yourself. It is passed as mutable reference to
49/// the expression in `benchmarks`.
50///
51/// ```rust
52/// use iai_callgrind::binary_benchmark_group;
53///
54/// binary_benchmark_group!(
55///     name = my_group;
56///     benchmarks = |_group: &mut BinaryBenchmarkGroup| {
57///         // Access the BinaryBenchmarkGroup with the identifier `group` to add benchmarks to the
58///         // group.
59///         //
60///         // group.binary_benchmark(/* BinaryBenchmark::new(...) */);
61///     }
62/// );
63/// ```
64#[derive(Debug, Default, PartialEq, Clone)]
65pub struct BinaryBenchmarkGroup {
66    /// All [`BinaryBenchmark`]s
67    pub binary_benchmarks: Vec<BinaryBenchmark>,
68}
69
70/// [low level api](`crate::binary_benchmark_group`) only: This struct mirrors the `#[bench]` and
71/// `#[benches]` attribute of a [`crate::binary_benchmark`]
72#[derive(Debug, Clone)]
73pub struct Bench {
74    /// The [`BenchmarkId`] used to uniquely identify this benchmark within a [`BinaryBenchmark`]
75    pub id: BenchmarkId,
76    /// All [`Command`]s
77    pub commands: Vec<Command>,
78    /// An optional [`BinaryBenchmarkConfig`]
79    ///
80    /// This field stores the internal representation of the [`BinaryBenchmarkConfig`]. Use
81    /// `BinaryBenchmarkConfig::into` to generate the internal configuration from a
82    /// [`BinaryBenchmarkConfig`]
83    pub config: Option<__internal::InternalBinaryBenchmarkConfig>,
84    /// The `setup` function to be executed before the [`Command`] is executed
85    pub setup: Option<fn()>,
86    /// The `teardown` function to be executed after the [`Command`] is executed
87    pub teardown: Option<fn()>,
88}
89
90/// [low level api](`crate::binary_benchmark_group`) only: Mirror the [`crate::binary_benchmark`]
91/// attribute
92///
93/// A `BinaryBenchmark` can be created in two ways. Either with [`BinaryBenchmark::new`]. Or via the
94/// [`crate::binary_benchmark_attribute`] macro used with a function annotated with the
95/// [`crate::binary_benchmark`] attribute. So, you can start with the high-level api using the
96/// attribute and then go on in the low-level api.
97///
98/// # Examples
99///
100/// For examples using [`BinaryBenchmark::new`], see there. Here's an example using the
101/// [`crate::binary_benchmark_attribute`]
102///
103/// ```rust
104/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
105/// use iai_callgrind::{
106///     binary_benchmark, binary_benchmark_group, binary_benchmark_attribute, Bench
107/// };
108///
109/// #[binary_benchmark]
110/// #[bench::foo("foo")]
111/// fn bench_binary(arg: &str) -> iai_callgrind::Command {
112///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
113///         .arg(arg)
114///         .build()
115/// }
116///
117/// binary_benchmark_group!(
118///     name = my_group;
119///     benchmarks = |group: &mut BinaryBenchmarkGroup| {
120///         let mut binary_benchmark = binary_benchmark_attribute!(bench_binary);
121///
122///         // Continue and add another `Bench` to the `BinaryBenchmark`
123///         binary_benchmark.bench(Bench::new("bar")
124///             .command(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
125///                 .arg("bar")
126///             )
127///         );
128///
129///         // Finally, add the `BinaryBenchmark` to the group
130///         group
131///             .binary_benchmark(binary_benchmark);
132///     }
133/// );
134/// # fn main() {}
135/// ```
136#[derive(Debug, Clone)]
137pub struct BinaryBenchmark {
138    /// An id which has to be unique within the same [`BinaryBenchmarkGroup`]
139    ///
140    /// In the high-level api this is the name of the function which is annotated by
141    /// [`crate::binary_benchmark`]
142    pub id: BenchmarkId,
143    /// An optional [`BinaryBenchmarkConfig`] which is applied to all [`Command`]s within this
144    /// [`BinaryBenchmark`]
145    pub config: Option<__internal::InternalBinaryBenchmarkConfig>,
146    /// All [`Bench`]es which were added to this [`BinaryBenchmark`]
147    pub benches: Vec<Bench>,
148    /// The default `setup` function for all [`Bench`]es within this [`BinaryBenchmark`]. It can be
149    /// overwritten in a [`Bench`]
150    pub setup: Option<fn()>,
151    /// The default `teardown` function for all [`Bench`]es within this [`BinaryBenchmark`]. It can
152    /// be overwritten in a [`Bench`]
153    pub teardown: Option<fn()>,
154}
155
156/// Provide the [`Command`] to be benchmarked
157///
158/// `Command` is a builder for the binary which is going to be benchmarked providing fine-grained
159/// control over how the `Command` for the valgrind benchmark should be executed.
160///
161/// The default configuration is created with [`Command::new`] providing a path to an executable.
162/// Adding a crate's binary is usually done with `env!("CARGO_BIN_EXE_<name>")` where `<name>` is
163/// the name of the binary. The builder methods allow the configuration to be changed prior to
164/// [`Command::build`]. The [`Command`] can be reused to build multiple processes.
165///
166/// # Examples
167///
168/// Suppose your crate's binary is called `my-echo`:
169///
170/// ```rust
171/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
172/// use iai_callgrind::Command;
173/// let command = Command::new(env!("CARGO_BIN_EXE_my-echo"));
174/// ```
175///
176/// However, an iai-callgrind benchmark is not limited to a crate's binaries, it can be any
177/// executable in the `$PATH`, or an absolute path to a binary installed on your system. The
178/// following will create a `Command` for the system's `echo` from the `$PATH`:
179///
180/// ```rust
181/// use iai_callgrind::Command;
182/// let command = Command::new("echo");
183/// ```
184#[derive(Debug, Default, Clone, PartialEq, IntoInner, AsRef)]
185pub struct Command(__internal::InternalCommand);
186
187/// Provide the [`crate::Delay`] to specify the event for [`crate::Command`] execution start.
188///
189/// The default configuration is created with [`Delay::new`] providing a [`crate::DelayKind`] to
190/// specify the event type and parameters for the `Delay`.
191///
192/// Additionally, the `Delay` can be created using `from*()` methods.
193/// - [`Delay::from(duration)`](Delay::from)
194/// - [`Delay::from_tcp_socket(addr)`](Delay::from_tcp_socket)
195/// - [`Delay::from_udp_request(addr, request)`](Delay::from_udp_request)
196/// - [`Delay::from_path(path)`](Delay::from_path)
197///
198/// # Examples
199///
200/// Suppose your command needs to start 60 seconds after the benchmark started:
201///
202/// ```rust
203/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
204/// use std::time::Duration;
205///
206/// use iai_callgrind::{Command, Delay, DelayKind};
207///
208/// let command = Command::new(env!("CARGO_BIN_EXE_my-echo")).delay(Delay::new(
209///     DelayKind::DurationElapse(Duration::from_secs(60)),
210/// ));
211///
212/// let command_from =
213///     Command::new(env!("CARGO_BIN_EXE_my-echo")).delay(Delay::from(Duration::from_secs(60)));
214///
215/// let command_duration =
216///     Command::new(env!("CARGO_BIN_EXE_my-echo")).delay(Duration::from_secs(60));
217/// ```
218///
219/// However, an iai-callgrind [`Delay`] is not limited to a duration, it can be any
220/// path creation event, a successful TCP connect or as well a received UDP response.
221///
222/// ```rust
223/// use iai_callgrind::{Command, Delay, DelayKind};
224///
225/// let command = Command::new("echo").delay(Delay::new(DelayKind::PathExists(
226///     "/your/path/to/wait/for".into(),
227/// )));
228///
229/// let command_from = Command::new("echo").delay(Delay::from_path("/your/path/to/wait/for"));
230/// ```
231///
232/// ```rust
233/// use std::net::SocketAddr;
234/// use std::time::Duration;
235///
236/// use iai_callgrind::{Command, Delay, DelayKind};
237///
238/// let command = Command::new("echo").delay(
239///     Delay::new(DelayKind::TcpConnect(
240///         "127.0.0.1:31000".parse::<SocketAddr>().unwrap(),
241///     ))
242///     .timeout(Duration::from_secs(3))
243///     .poll(Duration::from_millis(50)),
244/// );
245///
246/// let command_from = Command::new("echo").delay(
247///     Delay::from_tcp_socket("127.0.0.1:31000".parse::<SocketAddr>().unwrap())
248///         .timeout(Duration::from_secs(3))
249///         .poll(Duration::from_millis(50)),
250/// );
251/// ```
252///
253/// ```rust
254/// use std::net::SocketAddr;
255/// use std::time::Duration;
256///
257/// use iai_callgrind::{Command, Delay, DelayKind};
258///
259/// let command = Command::new("echo").delay(
260///     Delay::new(DelayKind::UdpResponse(
261///         "127.0.0.1:34000".parse::<SocketAddr>().unwrap(),
262///         vec![1],
263///     ))
264///     .timeout(Duration::from_secs(3))
265///     .poll(Duration::from_millis(50)),
266/// );
267///
268/// let command_from = Command::new("echo").delay(
269///     Delay::from_udp_request("127.0.0.1:34000".parse::<SocketAddr>().unwrap(), vec![1])
270///         .timeout(Duration::from_secs(3))
271///         .poll(Duration::from_millis(50)),
272/// );
273/// ```
274#[derive(Debug, Default, Clone, PartialEq, IntoInner, AsRef)]
275pub struct Delay(__internal::InternalDelay);
276
277// TODO: Refactor: Use api::ExitWith directly
278/// Set the expected exit status of a binary benchmark
279///
280/// Per default, the benchmarked binary is expected to succeed, but if a benchmark is expected to
281/// fail, setting this option is required.
282///
283/// # Examples
284///
285/// ```rust
286/// # use iai_callgrind::{binary_benchmark_group};
287/// # binary_benchmark_group!(
288/// #    name = my_group;
289/// #    benchmarks = |group: &mut BinaryBenchmarkGroup| {});
290/// use iai_callgrind::{main, BinaryBenchmarkConfig, ExitWith};
291///
292/// # fn main() {
293/// main!(
294///     config = BinaryBenchmarkConfig::default().exit_with(ExitWith::Code(1));
295///     binary_benchmark_groups = my_group
296/// );
297/// # }
298/// ```
299#[derive(Debug, Clone, Copy)]
300pub enum ExitWith {
301    /// Exit with success is similar to `ExitCode(0)`
302    Success,
303    /// Exit with failure is similar to setting the `ExitCode` to something different from `0`
304    /// without having to rely on a specific exit code
305    Failure,
306    /// The exact `ExitCode` of the benchmark run
307    Code(i32),
308}
309
310/// The `Sandbox` in which the `setup`, `teardown` and the [`Command`] are run
311///
312/// The `Sandbox` is a temporary directory which is created before the execution of the
313/// [`setup`](`crate::binary_benchmark`) and deleted after the
314/// [`teardown`](`crate::binary_benchmark`). `setup`, the [`Command`] and `teardown` are executed
315/// inside this temporary directory.
316///
317/// # Background and reasons for using a `Sandbox`
318///
319/// A [`Sandbox`] can help mitigating differences in benchmark results on different machines. As
320/// long as `$TMP_DIR` is unset or set to `/tmp`, the temporary directory has a constant length on
321/// unix machines (except android which uses `/data/local/tmp`). The directory itself
322/// is created with a constant length but random name like `/tmp/.a23sr8fk`. It is not implausible
323/// that an executable has different event counts just because the directory it is executed in has a
324/// different length. For example, if a member of your project has set up the project in
325/// `/home/bob/workspace/our-project` running the benchmarks in this directory, and the ci runs the
326/// benchmarks in `/runner/our-project`, the event counts might differ. If possible, the benchmarks
327/// should be run in an as constant as possible environment. Clearing the environment variables is
328/// also such a counter-measure.
329///
330/// Other reasons for using a `Sandbox` are convenience, such as if you're creating files during
331/// `setup` and the [`Command`] run and don't want to delete all the files manually. Or, more
332/// importantly, if the [`Command`] is destructive and deletes files, it is usually safer to execute
333/// such a [`Command`] in a temporary directory where it cannot do any harm to your or others file
334/// systems during the benchmark runs.
335///
336/// # Sandbox cleanup
337///
338/// The changes the `setup` makes in this directory persist until the `teardown` has finished. So,
339/// the [`Command`] can for example pick up any files created by the `setup` method. If run in a
340/// `Sandbox`, the `teardown` usually doesn't have to delete any files, because the whole
341/// directory is deleted after its usage. There is an exception to the rule. If any of the files
342/// inside the directory is not removable, for example because the permissions of a file don't allow
343/// the file to be deleted, then the whole directory persists. You can use the `teardown` to reset
344/// all permission bits to be readable and writable, so the cleanup can succeed.
345///
346/// To simply copy fixtures or whole directories into the `Sandbox` use [`Sandbox::fixtures`].
347#[derive(Debug, Clone, IntoInner, AsRef)]
348pub struct Sandbox(__internal::InternalSandbox);
349
350impl Bench {
351    /// Create a new `Bench` with a unique [`BenchmarkId`]
352    ///
353    /// If the provided [`BenchmarkId`] is invalid, `iai-callgrind` exits with an error.
354    ///
355    /// # Scope of uniqueness of the [`BenchmarkId`]
356    ///
357    /// The id needs to be unique within the same [`BinaryBenchmark`]
358    ///
359    /// # Examples
360    ///
361    /// The [`BenchmarkId`] can be created from any &str-like
362    ///
363    /// ```
364    /// use iai_callgrind::Bench;
365    ///
366    /// let bench = Bench::new("my_unique_id");
367    /// ```
368    ///
369    /// but you can also provide the [`BenchmarkId`]
370    ///
371    /// ```
372    /// use iai_callgrind::{Bench, BenchmarkId};
373    ///
374    /// let bench = Bench::new(BenchmarkId::new("my_unique_id"));
375    /// ```
376    pub fn new<T>(id: T) -> Self
377    where
378        T: Into<BenchmarkId>,
379    {
380        Self {
381            id: id.into(),
382            config: None,
383            commands: vec![],
384            setup: None,
385            teardown: None,
386        }
387    }
388
389    /// Add a [`BinaryBenchmarkConfig`] for this `Bench`
390    ///
391    /// A `Bench` without a `BinaryBenchmarkConfig` behaves like having specified the default
392    /// [`BinaryBenchmarkConfig`]. This [`BinaryBenchmarkConfig`] overwrites the values of a
393    /// [`BinaryBenchmarkConfig`] specified at a higher level. See there for more details.
394    ///
395    /// # Examples
396    ///
397    /// ```
398    /// use iai_callgrind::{Bench, BinaryBenchmarkConfig};
399    ///
400    /// let bench = Bench::new("some_id").config(BinaryBenchmarkConfig::default().env("FOO", "BAR"));
401    /// ```
402    pub fn config<T>(&mut self, config: T) -> &mut Self
403    where
404        T: Into<__internal::InternalBinaryBenchmarkConfig>,
405    {
406        self.config = Some(config.into());
407        self
408    }
409
410    /// Add a [`Command`] to this `Bench`
411    ///
412    /// A `Bench` with multiple `Commands` behaves exactly as the
413    /// [`#[benches]`](crate::binary_benchmark) attribute
414    ///
415    /// # Errors
416    ///
417    /// It is an error if a `Bench` does not contain any `Commands`, so this method or
418    /// [`Bench::commands`] has to be called at least once.
419    ///
420    /// # Examples
421    ///
422    /// Suppose the crate's binary is called `my-echo`:
423    ///
424    /// ```rust
425    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
426    /// use iai_callgrind::{Bench, Command};
427    ///
428    /// let bench = Bench::new("some_id")
429    ///     .command(Command::new(env!("CARGO_BIN_EXE_my-echo")))
430    ///     .clone();
431    ///
432    /// assert_eq!(bench.commands.len(), 1);
433    /// ```
434    pub fn command<T>(&mut self, command: T) -> &mut Self
435    where
436        T: Into<Command>,
437    {
438        self.commands.push(command.into());
439        self
440    }
441
442    /// Add multiple [`Command`]s to this `Bench`
443    ///
444    /// See also [`Bench::command`].
445    ///
446    /// # Examples
447    ///
448    /// Suppose the crate's binary is called `my-echo`
449    ///
450    /// ```rust
451    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
452    /// use iai_callgrind::{Bench, Command};
453    ///
454    /// let mut command = Command::new(env!("CARGO_BIN_EXE_my-echo"));
455    ///
456    /// let echo_foo = command.clone().arg("foo").build();
457    /// let echo_bar = command.arg("bar").build();
458    ///
459    /// let mut bench = Bench::new("some_id");
460    /// bench.commands([echo_foo, echo_bar]).clone();
461    ///
462    /// assert_eq!(bench.commands.len(), 2);
463    /// ```
464    pub fn commands<I, T>(&mut self, commands: T) -> &mut Self
465    where
466        I: Into<Command>,
467        T: IntoIterator<Item = I>,
468    {
469        self.commands.extend(commands.into_iter().map(Into::into));
470        self
471    }
472
473    /// Add a `setup` function to be executed before the [`Command`] is executed
474    ///
475    /// This `setup` function overwrites the `setup` function of [`BinaryBenchmark`]. In the
476    /// presence of a [`Sandbox`], this function is executed in the sandbox.
477    ///
478    /// # Examples
479    ///
480    /// ```rust
481    /// use iai_callgrind::Bench;
482    ///
483    /// fn my_setup() {
484    ///     println!("Place everything in this function you want to be executed prior to the Command");
485    /// }
486    ///
487    /// let mut bench = Bench::new("some_id");
488    /// bench.setup(my_setup);
489    ///
490    /// assert!(bench.setup.is_some())
491    /// ```
492    ///
493    /// Overwrite the setup function from a [`BinaryBenchmark`]
494    ///
495    /// ```rust
496    /// use iai_callgrind::{Bench, BinaryBenchmark};
497    /// fn binary_benchmark_setup() {
498    ///     println!("setup in BinaryBenchmark")
499    /// }
500    ///
501    /// fn bench_setup() {
502    ///     println!("setup in Bench")
503    /// }
504    ///
505    /// BinaryBenchmark::new("bench_binary")
506    ///     .setup(binary_benchmark_setup)
507    ///     .bench(Bench::new("some_id").setup(bench_setup));
508    /// ```
509    pub fn setup(&mut self, setup: fn()) -> &mut Self {
510        self.setup = Some(setup);
511        self
512    }
513
514    /// Add a `teardown` function to be executed after the [`Command`] is executed
515    ///
516    /// This `teardown` function overwrites the `teardown` function of [`BinaryBenchmark`]. In the
517    /// presence of a [`Sandbox`], this function is executed in the sandbox.
518    ///
519    /// # Examples
520    ///
521    /// ```rust
522    /// use iai_callgrind::Bench;
523    ///
524    /// fn my_teardown() {
525    ///     println!(
526    ///         "Place everything in this function you want to be executed after the execution of the \
527    ///          Command"
528    ///     );
529    /// }
530    ///
531    /// let mut bench = Bench::new("some_id");
532    /// bench.teardown(my_teardown);
533    ///
534    /// assert!(bench.teardown.is_some())
535    /// ```
536    ///
537    /// Overwrite the teardown function from a [`BinaryBenchmark`]
538    ///
539    /// ```rust
540    /// use iai_callgrind::{Bench, BinaryBenchmark};
541    /// fn binary_benchmark_teardown() {
542    ///     println!("teardown in BinaryBenchmark")
543    /// }
544    ///
545    /// fn bench_teardown() {
546    ///     println!("teardown in Bench")
547    /// }
548    ///
549    /// BinaryBenchmark::new("bench_binary")
550    ///     .teardown(binary_benchmark_teardown)
551    ///     .bench(Bench::new("some_id").teardown(bench_teardown));
552    /// ```
553    pub fn teardown(&mut self, teardown: fn()) -> &mut Self {
554        self.teardown = Some(teardown);
555        self
556    }
557
558    /// Add each line of a file as [`Command`] to this [`Bench`] using a `generator` function.
559    ///
560    /// This method mirrors the `file` parameter of the `#[benches]` attribute as far as possible.
561    /// In the low-level api you can achieve the same or more quickly yourself and this method
562    /// exists for the sake of completeness (and convenience).
563    ///
564    /// The file has to exist at the time you're using this method and the file has to be encoded in
565    /// UTF-8. The `generator` function tells us how to convert each line of the file into a
566    /// [`Command`].
567    ///
568    /// # Notable differences to high-level api
569    ///
570    /// If the file path in the high-level api is relative we interpret the path relative to the
571    /// workspace root (and make it absolute). In this method we use the path AS IS.
572    ///
573    /// # Errors
574    ///
575    /// If the file is empty, cannot be opened for reading or a line in the file cannot be converted
576    /// to a String. Also, the error from the `generator` is propagated. The errors containing the
577    /// line number use a 0-indexed line number.
578    ///
579    /// # Examples
580    ///
581    /// Suppose your cargo's binary is named `my-echo` and you want to convert a file with inputs
582    /// `benches/inputs` into commands and each line is the only argument for your `my-echo` binary:
583    ///
584    /// ```rust
585    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
586    /// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench};
587    ///
588    /// binary_benchmark_group!(
589    ///     name = my_group;
590    ///     benchmarks = |group: &mut BinaryBenchmarkGroup| {
591    ///         group.binary_benchmark(BinaryBenchmark::new("some_id")
592    ///             .bench(Bench::new("bench_id")
593    ///                 .file("benches/inputs", |line| {
594    ///                     Ok(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-echo"))
595    ///                         .arg(line)
596    ///                         .build())
597    ///                 }).unwrap()
598    ///             )
599    ///         )
600    ///     }
601    /// );
602    /// # fn main() {}
603    /// ```
604    pub fn file<T>(
605        &mut self,
606        path: T,
607        generator: fn(String) -> Result<Command, String>,
608    ) -> Result<&mut Self, String>
609    where
610        T: AsRef<Path>,
611    {
612        let path = path.as_ref();
613        let file = File::open(path).map_err(|error| {
614            format!(
615                "{}: Error opening file '{}': {error}",
616                self.id,
617                path.display(),
618            )
619        })?;
620
621        let reader = BufReader::new(file);
622        let mut has_lines = false;
623        for (index, line) in reader.lines().enumerate() {
624            has_lines = true;
625
626            let line = line.map_err(|error| {
627                format!(
628                    "{}: Error reading line {index} in file '{}': {error}",
629                    self.id,
630                    path.display()
631                )
632            })?;
633
634            let command = generator(line).map_err(|error| {
635                format!(
636                    "{}: Error generating command from line {index} in file '{}': {error}",
637                    self.id,
638                    path.display()
639                )
640            })?;
641            self.commands.push(command);
642        }
643
644        if !has_lines {
645            return Err(format!("{}: Empty file '{}'", self.id, path.display()));
646        }
647        Ok(self)
648    }
649}
650
651impl From<&mut Bench> for Bench {
652    fn from(value: &mut Bench) -> Self {
653        value.clone()
654    }
655}
656
657impl From<&Bench> for Bench {
658    fn from(value: &Bench) -> Self {
659        value.clone()
660    }
661}
662
663impl PartialEq for Bench {
664    fn eq(&self, other: &Self) -> bool {
665        self.id == other.id
666            && self.commands == other.commands
667            && self.config == other.config
668            && (self.setup.is_some() && other.setup.is_some()
669                || self.setup.is_none() && other.setup.is_none())
670            && (self.teardown.is_some() && other.teardown.is_some()
671                || self.teardown.is_none() && other.teardown.is_none())
672    }
673}
674
675impl BenchmarkId {
676    /// Convenience method to create a `BenchmarkId` with a parameter in the [low level
677    /// api](crate::binary_benchmark_group)
678    ///
679    /// The `parameter` is simply appended to the `id` with an underscore, so
680    /// `BenchmarkId::with_parameter("some", 1)` is equivalent to `BenchmarkId::new("some_1")`
681    ///
682    /// # Examples
683    ///
684    /// ```
685    /// use iai_callgrind::BenchmarkId;
686    ///
687    /// let new_id = BenchmarkId::new("prefix_1");
688    /// let with_parameter = BenchmarkId::with_parameter("prefix", 1);
689    /// assert_eq!(new_id, with_parameter);
690    /// ```
691    ///
692    /// ```rust
693    /// # use iai_callgrind::main;
694    /// use iai_callgrind::{binary_benchmark_group,BenchmarkId, BinaryBenchmark, Bench, Command};
695    /// use std::ffi::OsStr;
696    ///
697    /// binary_benchmark_group!(
698    ///     name = low_level_group;
699    ///     benchmarks = |group: &mut BinaryBenchmarkGroup| {
700    ///         let mut binary_benchmark = BinaryBenchmark::new("some_id");
701    ///         for arg in 0..10 {
702    ///             let id = BenchmarkId::with_parameter("prefix", arg);
703    ///             binary_benchmark.bench(
704    ///                 Bench::new(id)
705    ///                     .command(
706    ///                         Command::new("echo").arg(arg.to_string()).build()
707    ///                     )
708    ///             );
709    ///         }
710    ///         group.binary_benchmark(binary_benchmark);
711    ///     }
712    /// );
713    /// # fn main() {
714    /// # main!(binary_benchmark_groups = low_level_group);
715    /// # }
716    /// ```
717    pub fn with_parameter<T, P>(id: T, parameter: P) -> Self
718    where
719        T: AsRef<str>,
720        P: Display,
721    {
722        Self(format!("{}_{parameter}", id.as_ref()))
723    }
724
725    /// Create a new `BenchmarkId`
726    ///
727    /// `BenchmarkId`s can be created from any string-like input. See [`BenchmarkId::validate`] for
728    /// ids which are considered valid.
729    ///
730    /// # Examples
731    ///
732    /// ```rust
733    /// use iai_callgrind::BenchmarkId;
734    ///
735    /// let id = BenchmarkId::new("my_id");
736    ///
737    /// assert!(id.validate().is_ok());
738    /// ```
739    pub fn new<T>(id: T) -> Self
740    where
741        T: Into<String>,
742    {
743        Self(id.into())
744    }
745
746    /// Returns ok if this [`BenchmarkId`] is valid
747    ///
748    /// An id should be short, descriptive besides being unique. The requirements for the uniqueness
749    /// differ for the structs where a `BenchmarkId` is used and is further described there.
750    ///
751    /// We use a minimal subset of rust's identifiers. A valid `BenchmarkId` starts with an ascii
752    /// alphabetic letter `[a-zA-Z]` or underscore `[_]`. All following characters can be an ascii
753    /// alphabetic letter, underscore or a digit `[0-9]`. At least one valid character must be
754    /// present.
755    ///
756    /// The `BenchmarkId` is used by `iai-callgrind` as file and directory name for the output files
757    /// of the benchmarks. Therefore, the limit for an id is chosen to be 120 bytes. This is a
758    /// calculation with some headroom. There are file systems which do not even allow 255 bytes. If
759    /// you're working on such a peculiar file system, you have to restrict your ids to even fewer
760    /// bytes which is `floor(MAX_LENGTH/2) - 1`. Leaving the maximum bytes aside, the best IDs are
761    /// simple, short and descriptive.
762    ///
763    /// Usually, it is not necessary to call this function, since we already check the validity of
764    /// benchmark ids prior to the execution of the benchmark runner. But if your ids come from an
765    /// untrusted source you can use this method for more immediate feedback.
766    ///
767    /// # Errors
768    ///
769    /// This function will return an error describing the source of the error if the id is invalid
770    ///
771    /// # Examples
772    ///
773    /// ```rust
774    /// use iai_callgrind::BenchmarkId;
775    ///
776    /// assert!(BenchmarkId::new("").validate().is_err());
777    /// assert!(BenchmarkId::new("0a").validate().is_err());
778    /// assert!(BenchmarkId::new("id with spaces").validate().is_err());
779    /// assert!(BenchmarkId::new("path/to").validate().is_err());
780    /// assert!(BenchmarkId::new("no::module::too").validate().is_err());
781    ///
782    /// assert!(BenchmarkId::new("_").validate().is_ok());
783    /// assert!(BenchmarkId::new("abc").validate().is_ok());
784    /// assert!(BenchmarkId::new("a9").validate().is_ok());
785    /// assert!(BenchmarkId::new("z_").validate().is_ok());
786    /// assert!(BenchmarkId::new("some_id").validate().is_ok());
787    /// ```
788    #[allow(clippy::missing_panics_doc)]
789    pub fn validate(&self) -> Result<(), String> {
790        const MAX_LENGTH_ID: usize = 255;
791        if self.0.is_empty() {
792            return Err("Invalid id: Cannot be empty".to_owned());
793        }
794
795        let mut bytes = self.0.bytes();
796        // This unwrap is safe, since we just checked that the string is not empty
797        let first = bytes.next().unwrap();
798
799        if first.is_ascii_alphabetic() || first == b'_' {
800            for (index, byte) in (1..).zip(bytes) {
801                if index > MAX_LENGTH_ID {
802                    return Err(format!(
803                        "Invalid id '{}': Maximum length of {MAX_LENGTH_ID} bytes reached",
804                        &self.0,
805                    ));
806                }
807                if byte.is_ascii() {
808                    if !(byte.is_ascii_alphanumeric() || byte == b'_') {
809                        return Err(format!(
810                            "Invalid id '{}' at position {index}: Invalid character '{}'",
811                            &self.0,
812                            char::from(byte)
813                        ));
814                    }
815                } else {
816                    return Err(format!(
817                        "Invalid id '{}' at position {index}: Encountered non-ascii character",
818                        &self.0
819                    ));
820                }
821            }
822        } else if first.is_ascii() {
823            return Err(format!(
824                "Invalid id '{}': As first character is '{}' not allowed",
825                &self.0,
826                char::from(first)
827            ));
828        } else {
829            return Err(format!(
830                "Invalid id '{}': Encountered non-ascii character as first character",
831                &self.0
832            ));
833        }
834        Ok(())
835    }
836}
837
838impl Display for BenchmarkId {
839    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
840        f.write_str(&self.0)
841    }
842}
843
844impl From<BenchmarkId> for String {
845    fn from(value: BenchmarkId) -> Self {
846        value.0
847    }
848}
849
850impl<T> From<T> for BenchmarkId
851where
852    T: AsRef<str>,
853{
854    fn from(value: T) -> Self {
855        Self(value.as_ref().to_owned())
856    }
857}
858
859impl BinaryBenchmark {
860    /// Create a new `BinaryBenchmark`
861    ///
862    /// A `BinaryBenchmark` is the equivalent of the
863    /// [`#[binary_benchmark]`](`crate::binary_benchmark`) attribute in the low-level api and needs
864    /// a [`BenchmarkId`]. In the high-level api the id is derived from the function name.
865    ///
866    /// The [`BenchmarkId`] is used together with the id of each [`Bench`] to create a directory
867    /// name. This usually limits the combined length of the ids to `255 - 1` but can be less
868    /// depending on the file system. See [`BenchmarkId`] for more details
869    ///
870    /// # Scope of uniqueness of the [`BenchmarkId`]
871    ///
872    /// The id needs to be unique within the same [`crate::binary_benchmark_group`]. It is
873    /// recommended to use an id which is unique within the whole benchmark file, although doing
874    /// otherwise does currently not incur any negative consequence.
875    ///
876    /// # Examples
877    ///
878    /// ```rust
879    /// use iai_callgrind::{BenchmarkId, BinaryBenchmark};
880    ///
881    /// let binary_benchmark = BinaryBenchmark::new("some_id");
882    /// assert_eq!(binary_benchmark.id, BenchmarkId::new("some_id"));
883    /// ```
884    pub fn new<T>(id: T) -> Self
885    where
886        T: Into<BenchmarkId>,
887    {
888        Self {
889            id: id.into(),
890            config: None,
891            benches: vec![],
892            setup: None,
893            teardown: None,
894        }
895    }
896
897    /// Add a [`BinaryBenchmarkConfig`] to this `BinaryBenchmark`
898    ///
899    /// # Examples
900    ///
901    /// ```rust
902    /// use iai_callgrind::{BinaryBenchmark, BinaryBenchmarkConfig};
903    ///
904    /// let binary_benchmark = BinaryBenchmark::new("some_id")
905    ///     .config(BinaryBenchmarkConfig::default().env("FOO", "BAR"))
906    ///     .clone();
907    ///
908    /// assert_eq!(
909    ///     binary_benchmark.config,
910    ///     Some(BinaryBenchmarkConfig::default().env("FOO", "BAR").into())
911    /// );
912    /// ```
913    pub fn config<T>(&mut self, config: T) -> &mut Self
914    where
915        T: Into<__internal::InternalBinaryBenchmarkConfig>,
916    {
917        self.config = Some(config.into());
918        self
919    }
920
921    /// Add a [`Bench`] to this `BinaryBenchmark`
922    ///
923    /// Adding a [`Bench`] which doesn't contain a [`Command`] is an error.
924    ///
925    /// # Examples
926    ///
927    /// ```rust
928    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
929    /// use iai_callgrind::{Bench, BinaryBenchmark, Command};
930    ///
931    /// // Each `Bench` needs at least one `Command`!
932    /// let mut bench = Bench::new("some_id");
933    /// bench.command(Command::new(env!("CARGO_BIN_EXE_my-echo")));
934    ///
935    /// let binary_benchmark = BinaryBenchmark::new("bench_binary")
936    ///     .bench(bench.clone())
937    ///     .clone();
938    ///
939    /// assert_eq!(binary_benchmark.benches[0], bench);
940    /// ```
941    pub fn bench<T>(&mut self, bench: T) -> &mut Self
942    where
943        T: Into<Bench>,
944    {
945        self.benches.push(bench.into());
946        self
947    }
948
949    /// Add a `setup` function to this `BinaryBenchmark`
950    ///
951    /// This `setup` function is used in all [`Bench`]es of this `BinaryBenchmark` if not overridden
952    /// by the `Bench`.
953    ///
954    /// # Examples
955    ///
956    /// ```rust
957    /// use iai_callgrind::Bench;
958    /// fn my_setup() {
959    ///     println!(
960    ///         "Place everything in this function you want to be executed before the execution of \
961    ///          the Command"
962    ///     );
963    /// }
964    ///
965    /// let bench = Bench::new("some_id").setup(my_setup).clone();
966    ///
967    /// assert!(bench.setup.is_some())
968    /// ```
969    ///
970    /// Overwrite the setup function from this `BinaryBenchmark` in a [`Bench`]
971    ///
972    /// ```rust
973    /// use iai_callgrind::{Bench, BinaryBenchmark};
974    /// fn binary_benchmark_setup() {
975    ///     println!("setup in BinaryBenchmark")
976    /// }
977    ///
978    /// fn bench_setup() {
979    ///     println!("setup in Bench")
980    /// }
981    ///
982    /// BinaryBenchmark::new("bench_binary")
983    ///     .setup(binary_benchmark_setup)
984    ///     .bench(Bench::new("some_id").setup(bench_setup));
985    /// ```
986    pub fn setup(&mut self, setup: fn()) -> &mut Self {
987        self.setup = Some(setup);
988        self
989    }
990
991    /// Add a `teardown` function to this `BinaryBenchmark`
992    ///
993    /// This `teardown` function is used in all [`Bench`]es of this `BinaryBenchmark` if not
994    /// overridden by the `Bench`.
995    ///
996    /// # Examples
997    ///
998    /// ```rust
999    /// use iai_callgrind::Bench;
1000    /// fn my_teardown() {
1001    ///     println!(
1002    ///         "Place everything in this function you want to be executed after the execution of the \
1003    ///          Command"
1004    ///     );
1005    /// }
1006    ///
1007    /// let bench = Bench::new("some_id").teardown(my_teardown).clone();
1008    ///
1009    /// assert!(bench.teardown.is_some())
1010    /// ```
1011    ///
1012    /// Overwrite the teardown function from this `BinaryBenchmark` in a [`Bench`]
1013    ///
1014    /// ```rust
1015    /// use iai_callgrind::{Bench, BinaryBenchmark};
1016    /// fn binary_benchmark_teardown() {
1017    ///     println!("teardown in BinaryBenchmark")
1018    /// }
1019    ///
1020    /// fn bench_teardown() {
1021    ///     println!("teardown in Bench")
1022    /// }
1023    ///
1024    /// BinaryBenchmark::new("bench_binary")
1025    ///     .teardown(binary_benchmark_teardown)
1026    ///     .bench(Bench::new("some_id").teardown(bench_teardown));
1027    /// ```
1028    pub fn teardown(&mut self, teardown: fn()) -> &mut Self {
1029        self.teardown = Some(teardown);
1030        self
1031    }
1032}
1033
1034impl From<&mut BinaryBenchmark> for BinaryBenchmark {
1035    fn from(value: &mut BinaryBenchmark) -> Self {
1036        value.clone()
1037    }
1038}
1039
1040impl From<&BinaryBenchmark> for BinaryBenchmark {
1041    fn from(value: &BinaryBenchmark) -> Self {
1042        value.clone()
1043    }
1044}
1045
1046impl PartialEq for BinaryBenchmark {
1047    fn eq(&self, other: &Self) -> bool {
1048        self.id == other.id
1049            && self.config == other.config
1050            && self.benches == other.benches
1051            && (self.setup.is_some() && other.setup.is_some()
1052                || self.setup.is_none() && other.setup.is_none())
1053            && (self.teardown.is_some() && other.teardown.is_some()
1054                || self.teardown.is_none() && other.teardown.is_none())
1055    }
1056}
1057
1058impl BinaryBenchmarkConfig {
1059    /// Change the default tool to something different than callgrind
1060    ///
1061    /// See also [`crate::LibraryBenchmarkConfig::default_tool`].
1062    ///
1063    /// # Example for using cachegrind on the fly
1064    ///
1065    /// ```rust
1066    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1067    /// use iai_callgrind::{
1068    ///     main, binary_benchmark, binary_benchmark_group, BinaryBenchmarkConfig, ValgrindTool
1069    /// };
1070    ///
1071    /// #[binary_benchmark(
1072    ///     config = BinaryBenchmarkConfig::default()
1073    ///         .default_tool(ValgrindTool::Cachegrind)
1074    /// )]
1075    /// fn bench_me() -> iai_callgrind::Command {
1076    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_echo"))
1077    /// }
1078    ///
1079    /// binary_benchmark_group!(
1080    ///    name = my_group;
1081    ///    benchmarks = bench_me
1082    /// );
1083    ///
1084    /// # fn main() {
1085    /// main!(binary_benchmark_groups = my_group);
1086    /// # }
1087    /// ```
1088    pub fn default_tool(&mut self, tool: ValgrindTool) -> &mut Self {
1089        self.0.default_tool = Some(tool);
1090        self
1091    }
1092
1093    /// Pass valgrind arguments to all tools
1094    ///
1095    /// Only core [valgrind
1096    /// arguments](https://valgrind.org/docs/manual/manual-core.html#manual-core.options) are
1097    /// allowed.
1098    ///
1099    /// These arguments can be overwritten by tool specific arguments for example with
1100    /// [`crate::Callgrind::args`].
1101    ///
1102    /// # Examples
1103    ///
1104    /// Specify `--trace-children=no` for all configured tools (including callgrind):
1105    ///
1106    /// ```rust
1107    /// # use iai_callgrind::{binary_benchmark_group};
1108    /// # binary_benchmark_group!(
1109    /// #    name = my_group;
1110    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1111    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Dhat};
1112    ///
1113    /// # fn main() {
1114    /// main!(
1115    ///     config = BinaryBenchmarkConfig::default()
1116    ///         .valgrind_args(["--trace-children=no"])
1117    ///         .tool(Dhat::default());
1118    ///     binary_benchmark_groups = my_group
1119    /// );
1120    /// # }
1121    /// ```
1122    ///
1123    /// Overwrite the valgrind argument `--num-callers=25` for `DHAT` with `--num-callers=30`:
1124    ///
1125    /// ```rust
1126    /// # use iai_callgrind::{binary_benchmark_group};
1127    /// # binary_benchmark_group!(
1128    /// #    name = my_group;
1129    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1130    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Dhat};
1131    ///
1132    /// # fn main() {
1133    /// main!(
1134    ///     config = BinaryBenchmarkConfig::default()
1135    ///         .valgrind_args(["--num-callers=25"])
1136    ///         .tool(Dhat::with_args(["--num-callers=30"]));
1137    ///     binary_benchmark_groups = my_group
1138    /// );
1139    /// # }
1140    /// ```
1141    pub fn valgrind_args<I, T>(&mut self, args: T) -> &mut Self
1142    where
1143        I: AsRef<str>,
1144        T: IntoIterator<Item = I>,
1145    {
1146        self.0.valgrind_args.extend_ignore_flag(args);
1147        self
1148    }
1149
1150    /// Add an environment variable to the [`Command`]
1151    ///
1152    /// These environment variables are available independently of the setting of
1153    /// [`BinaryBenchmarkConfig::env_clear`].
1154    ///
1155    /// # Examples
1156    ///
1157    /// An example for a custom environment variable "FOO=BAR":
1158    ///
1159    /// ```rust
1160    /// # use iai_callgrind::{binary_benchmark_group};
1161    /// # binary_benchmark_group!(
1162    /// #    name = my_group;
1163    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1164    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1165    ///
1166    /// # fn main() {
1167    /// main!(
1168    ///     config = BinaryBenchmarkConfig::default().env("FOO", "BAR");
1169    ///     binary_benchmark_groups = my_group
1170    /// );
1171    /// # }
1172    /// ```
1173    pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
1174    where
1175        K: Into<OsString>,
1176        V: Into<OsString>,
1177    {
1178        self.0.envs.push((key.into(), Some(value.into())));
1179        self
1180    }
1181
1182    /// Add multiple environment variables to the [`Command`]
1183    ///
1184    /// # Examples
1185    ///
1186    /// ```rust
1187    /// # use iai_callgrind::{binary_benchmark_group};
1188    /// # binary_benchmark_group!(
1189    /// #    name = my_group;
1190    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1191    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1192    ///
1193    /// # fn main() {
1194    /// main!(
1195    ///     config = BinaryBenchmarkConfig::default().envs([("FOO", "BAR"),("BAR", "BAZ")]);
1196    ///     binary_benchmark_groups = my_group
1197    /// );
1198    /// # }
1199    /// ```
1200    pub fn envs<K, V, T>(&mut self, envs: T) -> &mut Self
1201    where
1202        K: Into<OsString>,
1203        V: Into<OsString>,
1204        T: IntoIterator<Item = (K, V)>,
1205    {
1206        self.0
1207            .envs
1208            .extend(envs.into_iter().map(|(k, v)| (k.into(), Some(v.into()))));
1209        self
1210    }
1211
1212    /// Specify a pass-through environment variable
1213    ///
1214    /// Usually, the environment variables before running a binary benchmark are cleared
1215    /// but specifying pass-through variables makes this environment variable available to
1216    /// the benchmark as it actually appeared in the root environment.
1217    ///
1218    /// Pass-through environment variables are ignored if they don't exist in the root
1219    /// environment.
1220    ///
1221    /// # Examples
1222    ///
1223    /// Here, we chose to pass through the original value of the `HOME` variable:
1224    ///
1225    /// ```rust
1226    /// # use iai_callgrind::{binary_benchmark_group};
1227    /// # binary_benchmark_group!(
1228    /// #    name = my_group;
1229    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1230    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1231    ///
1232    /// # fn main() {
1233    /// main!(
1234    ///     config = BinaryBenchmarkConfig::default().pass_through_env("HOME");
1235    ///     binary_benchmark_groups = my_group
1236    /// );
1237    /// # }
1238    /// ```
1239    pub fn pass_through_env<K>(&mut self, key: K) -> &mut Self
1240    where
1241        K: Into<OsString>,
1242    {
1243        self.0.envs.push((key.into(), None));
1244        self
1245    }
1246
1247    /// Specify multiple pass-through environment variables
1248    ///
1249    /// See also [`crate::BinaryBenchmarkConfig::pass_through_env`].
1250    ///
1251    /// # Examples
1252    ///
1253    /// ```rust
1254    /// # use iai_callgrind::binary_benchmark_group;
1255    /// # binary_benchmark_group!(
1256    /// #    name = my_group;
1257    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1258    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1259    ///
1260    /// # fn main() {
1261    /// main!(
1262    ///     config = BinaryBenchmarkConfig::default().pass_through_envs(["HOME", "USER"]);
1263    ///     binary_benchmark_groups = my_group
1264    /// );
1265    /// # }
1266    /// ```
1267    pub fn pass_through_envs<K, T>(&mut self, envs: T) -> &mut Self
1268    where
1269        K: Into<OsString>,
1270        T: IntoIterator<Item = K>,
1271    {
1272        self.0
1273            .envs
1274            .extend(envs.into_iter().map(|k| (k.into(), None)));
1275        self
1276    }
1277
1278    /// If false, don't clear the environment variables before running the benchmark (Default: true)
1279    ///
1280    /// # Examples
1281    ///
1282    /// ```rust
1283    /// # use iai_callgrind::{binary_benchmark_group};
1284    /// # binary_benchmark_group!(
1285    /// #    name = my_group;
1286    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1287    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1288    ///
1289    /// # fn main() {
1290    /// main!(
1291    ///     config = BinaryBenchmarkConfig::default().env_clear(false);
1292    ///     binary_benchmark_groups = my_group
1293    /// );
1294    /// # }
1295    /// ```
1296    pub fn env_clear(&mut self, value: bool) -> &mut Self {
1297        self.0.env_clear = Some(value);
1298        self
1299    }
1300
1301    /// Set the directory of the benchmarked binary (Default: Unchanged)
1302    ///
1303    /// Unchanged means, in the case of running with the sandbox enabled, the root of the sandbox.
1304    /// In the case of running without sandboxing enabled, this'll be the directory which `cargo
1305    /// bench` sets. If running the benchmark within the sandbox, and the path is relative then this
1306    /// new directory must be contained in the sandbox.
1307    ///
1308    /// # Examples
1309    ///
1310    /// ```rust
1311    /// # use iai_callgrind::{binary_benchmark_group};
1312    /// # binary_benchmark_group!(
1313    /// #    name = my_group;
1314    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1315    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1316    ///
1317    /// # fn main() {
1318    /// main!(
1319    ///     config = BinaryBenchmarkConfig::default().current_dir("/tmp");
1320    ///     binary_benchmark_groups = my_group
1321    /// );
1322    /// # }
1323    /// ```
1324    ///
1325    /// and the following will change the current directory to `fixtures` assuming it is
1326    /// contained in the root of the sandbox
1327    ///
1328    /// ```rust
1329    /// # use iai_callgrind::{binary_benchmark_group};
1330    /// # binary_benchmark_group!(
1331    /// #    name = my_group;
1332    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1333    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Sandbox};
1334    ///
1335    /// # fn main() {
1336    /// main!(
1337    ///     config = BinaryBenchmarkConfig::default()
1338    ///         .sandbox(Sandbox::new(true))
1339    ///         .current_dir("fixtures");
1340    ///     binary_benchmark_groups = my_group
1341    /// );
1342    /// # }
1343    /// ```
1344    pub fn current_dir<T>(&mut self, value: T) -> &mut Self
1345    where
1346        T: Into<PathBuf>,
1347    {
1348        self.0.current_dir = Some(value.into());
1349        self
1350    }
1351
1352    /// Set the expected exit status [`ExitWith`] of a benchmarked binary
1353    ///
1354    /// Per default, the benchmarked binary is expected to succeed which is the equivalent of
1355    /// [`ExitWith::Success`]. But, if a benchmark is expected to fail, setting this option is
1356    /// required.
1357    ///
1358    /// # Examples
1359    ///
1360    /// If the benchmark is expected to fail with a specific exit code, for example `100`:
1361    ///
1362    /// ```rust
1363    /// # use iai_callgrind::{binary_benchmark_group};
1364    /// # binary_benchmark_group!(
1365    /// #    name = my_group;
1366    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1367    /// use iai_callgrind::{main, BinaryBenchmarkConfig, ExitWith};
1368    ///
1369    /// # fn main() {
1370    /// main!(
1371    ///     config = BinaryBenchmarkConfig::default().exit_with(ExitWith::Code(100));
1372    ///     binary_benchmark_groups = my_group
1373    /// );
1374    /// # }
1375    /// ```
1376    ///
1377    /// If a benchmark is expected to fail, but the exit code doesn't matter:
1378    ///
1379    /// ```rust
1380    /// # use iai_callgrind::{binary_benchmark_group};
1381    /// # binary_benchmark_group!(
1382    /// #    name = my_group;
1383    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1384    /// use iai_callgrind::{main, BinaryBenchmarkConfig, ExitWith};
1385    ///
1386    /// # fn main() {
1387    /// main!(
1388    ///     config = BinaryBenchmarkConfig::default().exit_with(ExitWith::Failure);
1389    ///     binary_benchmark_groups = my_group
1390    /// );
1391    /// # }
1392    /// ```
1393    pub fn exit_with<T>(&mut self, value: T) -> &mut Self
1394    where
1395        T: Into<__internal::InternalExitWith>,
1396    {
1397        self.0.exit_with = Some(value.into());
1398        self
1399    }
1400
1401    /// Add a configuration for a valgrind tool
1402    ///
1403    /// Valid configurations are [`crate::Callgrind`], [`crate::Cachegrind`], [`crate::Dhat`],
1404    /// [`crate::Memcheck`], [`crate::Helgrind`], [`crate::Drd`], [`crate::Massif`] and
1405    /// [`crate::Bbv`].
1406    ///
1407    /// # Examples
1408    ///
1409    /// Run DHAT in addition to callgrind.
1410    ///
1411    /// ```rust
1412    /// # use iai_callgrind::{binary_benchmark_group};
1413    /// # binary_benchmark_group!(
1414    /// #    name = my_group;
1415    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1416    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Dhat, ValgrindTool};
1417    ///
1418    /// # fn main() {
1419    /// main!(
1420    ///     config = BinaryBenchmarkConfig::default()
1421    ///         .tool(Dhat::default());
1422    ///     binary_benchmark_groups = my_group
1423    /// );
1424    /// # }
1425    /// ```
1426    pub fn tool<T>(&mut self, tool: T) -> &mut Self
1427    where
1428        T: Into<__internal::InternalTool>,
1429    {
1430        self.0.tools.update(tool.into());
1431        self
1432    }
1433
1434    /// Override previously defined configurations of valgrind tools
1435    ///
1436    /// See also [`crate::LibraryBenchmarkConfig::tool_override`] for more details.
1437    ///
1438    /// # Example
1439    ///
1440    /// The following will run `DHAT` and `Massif` (and the default callgrind) for all benchmarks
1441    /// in `main!` besides for `foo` which will just run `Memcheck` (and callgrind).
1442    ///
1443    /// ```rust
1444    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1445    /// use iai_callgrind::{
1446    ///     binary_benchmark, binary_benchmark_group, BinaryBenchmarkConfig, main, Memcheck, Dhat,
1447    ///     Massif
1448    /// };
1449    ///
1450    /// #[binary_benchmark]
1451    /// #[bench::some(
1452    ///     config = BinaryBenchmarkConfig::default()
1453    ///         .tool_override(Memcheck::default())
1454    /// )]
1455    /// fn bench_binary() -> iai_callgrind::Command {
1456    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
1457    /// }
1458    ///
1459    /// binary_benchmark_group!(
1460    ///     name = my_group;
1461    ///     benchmarks = bench_binary
1462    /// );
1463    ///
1464    /// # fn main() {
1465    /// main!(
1466    ///     config = BinaryBenchmarkConfig::default()
1467    ///         .tool(Dhat::default())
1468    ///         .tool(Massif::default());
1469    ///     binary_benchmark_groups = my_group
1470    /// );
1471    /// # }
1472    /// ```
1473    pub fn tool_override<T>(&mut self, tool: T) -> &mut Self
1474    where
1475        T: Into<__internal::InternalTool>,
1476    {
1477        self.0
1478            .tools_override
1479            .get_or_insert(__internal::InternalTools::default())
1480            .update(tool.into());
1481        self
1482    }
1483
1484    /// Configure benchmarks to run in a [`Sandbox`] (Default: false)
1485    ///
1486    /// If specified, we create a temporary directory in which the `setup` and `teardown` functions
1487    /// of the `#[binary_benchmark]` (`#[bench]`, `#[benches]`) and the [`Command`] itself are run.
1488    ///
1489    /// A good reason for using a temporary directory as workspace is, that the length of the path
1490    /// where a benchmark is executed may have an influence on the benchmark results. For example,
1491    /// running the benchmark in your repository `/home/me/my/repository` and someone else's
1492    /// repository located under `/home/someone/else/repository` may produce different results only
1493    /// because the length of the first path is shorter. To run benchmarks as deterministic as
1494    /// possible across different systems, the length of the path should be the same wherever the
1495    /// benchmark is executed. This crate ensures this property by using the tempfile crate which
1496    /// creates the temporary directory in `/tmp` with a random name of fixed length like
1497    /// `/tmp/.tmp12345678`. This ensures that the length of the directory will be the same on all
1498    /// unix hosts where the benchmarks are run.
1499    ///
1500    /// # Examples
1501    ///
1502    /// ```rust
1503    /// # use iai_callgrind::{binary_benchmark_group};
1504    /// # binary_benchmark_group!(
1505    /// #    name = my_group;
1506    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1507    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Sandbox};
1508    ///
1509    /// # fn main() {
1510    /// main!(
1511    ///     config = BinaryBenchmarkConfig::default().sandbox(Sandbox::new(true));
1512    ///     binary_benchmark_groups = my_group
1513    /// );
1514    /// # }
1515    /// ```
1516    pub fn sandbox<T>(&mut self, sandbox: T) -> &mut Self
1517    where
1518        T: Into<__internal::InternalSandbox>,
1519    {
1520        self.0.sandbox = Some(sandbox.into());
1521        self
1522    }
1523
1524    /// Configure the [`crate::OutputFormat`] of the terminal output of Iai-Callgrind
1525    ///
1526    /// # Examples
1527    ///
1528    /// ```rust
1529    /// use iai_callgrind::{main, BinaryBenchmarkConfig, OutputFormat};
1530    /// # use iai_callgrind::{binary_benchmark, binary_benchmark_group};
1531    /// # #[binary_benchmark]
1532    /// # fn some_func() -> iai_callgrind::Command { iai_callgrind::Command::new("some/path") }
1533    /// # binary_benchmark_group!(
1534    /// #    name = some_group;
1535    /// #    benchmarks = some_func
1536    /// # );
1537    /// # fn main() {
1538    /// main!(
1539    ///     config = BinaryBenchmarkConfig::default()
1540    ///         .output_format(OutputFormat::default()
1541    ///             .truncate_description(Some(200))
1542    ///         );
1543    ///     binary_benchmark_groups = some_group
1544    /// );
1545    /// # }
1546    pub fn output_format<T>(&mut self, output_format: T) -> &mut Self
1547    where
1548        T: Into<__internal::InternalOutputFormat>,
1549    {
1550        self.0.output_format = Some(output_format.into());
1551        self
1552    }
1553
1554    /// Execute the `setup` in parallel to the [`Command`].
1555    ///
1556    /// See also [`Command::setup_parallel`]
1557    ///
1558    /// # Examples
1559    ///
1560    /// ```rust
1561    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1562    /// use std::time::Duration;
1563    /// use std::net::{SocketAddr, TcpListener};
1564    /// use std::thread;
1565    /// use iai_callgrind::{
1566    ///     binary_benchmark_group, binary_benchmark, main, BinaryBenchmarkConfig, Command,
1567    ///     Delay, DelayKind
1568    /// };
1569    ///
1570    /// fn setup_tcp_server() {
1571    ///     thread::sleep(Duration::from_millis(300));
1572    ///     let _listener = TcpListener::bind("127.0.0.1:31000".parse::<SocketAddr>().unwrap()).unwrap();
1573    ///     thread::sleep(Duration::from_secs(1));
1574    /// }
1575    ///
1576    /// #[binary_benchmark]
1577    /// #[bench::delay(
1578    ///     setup = setup_tcp_server(),
1579    ///     config = BinaryBenchmarkConfig::default()
1580    ///         .setup_parallel(true)
1581    /// )]
1582    /// fn bench_binary() -> iai_callgrind::Command {
1583    ///     Command::new(env!("CARGO_BIN_EXE_my-echo"))
1584    ///         .delay(
1585    ///             Delay::new(
1586    ///                 DelayKind::TcpConnect("127.0.0.1:31000".parse::<SocketAddr>().unwrap()))
1587    ///                 .timeout(Duration::from_millis(500))
1588    ///         ).build()
1589    /// }
1590    ///
1591    /// binary_benchmark_group!(name = delay; benchmarks = bench_binary);
1592    /// # fn main() {
1593    /// main!(binary_benchmark_groups = delay);
1594    /// # }
1595    /// ```
1596    pub fn setup_parallel(&mut self, setup_parallel: bool) -> &mut Self {
1597        self.0.setup_parallel = Some(setup_parallel);
1598        self
1599    }
1600}
1601
1602impl BinaryBenchmarkGroup {
1603    /// Add a [`BinaryBenchmark`] to this group
1604    ///
1605    /// This can be a binary benchmark created with [`BinaryBenchmark::new`] or a
1606    /// [`crate::binary_benchmark`] attributed function addable with the
1607    /// [`crate::binary_benchmark_attribute`] macro.
1608    ///
1609    /// It is an error to add a [`BinaryBenchmark`] without having added a [`Bench`] to it.
1610    ///
1611    /// # Examples
1612    ///
1613    /// Add a [`BinaryBenchmark`] to this group
1614    ///
1615    /// ```rust
1616    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1617    /// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench, BinaryBenchmarkGroup};
1618    ///
1619    /// fn setup_my_group(group: &mut BinaryBenchmarkGroup) {
1620    ///     group.binary_benchmark(BinaryBenchmark::new("bench_binary")
1621    ///         .bench(Bench::new("foo")
1622    ///             .command(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
1623    ///                 .arg("foo")
1624    ///             )
1625    ///         )
1626    ///     );
1627    /// }
1628    ///
1629    /// binary_benchmark_group!(
1630    ///     name = my_group;
1631    ///     benchmarks = |group: &mut BinaryBenchmarkGroup| setup_my_group(group)
1632    /// );
1633    /// # fn main() {}
1634    /// ```
1635    ///
1636    /// Or, add a `#[binary_benchmark]` annotated function to this group:
1637    ///
1638    /// ```rust
1639    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1640    /// use iai_callgrind::{binary_benchmark, binary_benchmark_group, binary_benchmark_attribute};
1641    ///
1642    /// #[binary_benchmark]
1643    /// #[bench::foo("foo")]
1644    /// fn bench_binary(arg: &str) -> iai_callgrind::Command {
1645    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
1646    ///         .arg(arg)
1647    ///         .build()
1648    /// }
1649    ///
1650    /// binary_benchmark_group!(
1651    ///     name = my_group;
1652    ///     benchmarks = |group: &mut BinaryBenchmarkGroup| {
1653    ///         group
1654    ///             .binary_benchmark(binary_benchmark_attribute!(bench_binary))
1655    ///     }
1656    /// );
1657    /// # fn main() {}
1658    /// ```
1659    pub fn binary_benchmark<T>(&mut self, binary_benchmark: T) -> &mut Self
1660    where
1661        T: Into<BinaryBenchmark>,
1662    {
1663        self.binary_benchmarks.push(binary_benchmark.into());
1664        self
1665    }
1666
1667    /// Add multiple [`BinaryBenchmark`]s at once
1668    pub fn binary_benchmarks<I, T>(&mut self, binary_benchmarks: T) -> &mut Self
1669    where
1670        I: Into<BinaryBenchmark>,
1671        T: IntoIterator<Item = I>,
1672    {
1673        self.binary_benchmarks
1674            .extend(binary_benchmarks.into_iter().map(Into::into));
1675        self
1676    }
1677}
1678
1679impl Command {
1680    /// Create a new [`Command`] which is run under valgrind.
1681    ///
1682    /// Use
1683    /// [`env!("CARGO_BIN_EXE_<name>)`](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates)
1684    /// to provide the path to an executable of your project instead of `target/release/<name>`.
1685    ///
1686    /// This `Command` is a builder for the binary which is going to be benchmarked but is not
1687    /// executed right away. We simply gather all the information to be finally able to execute the
1688    /// command under valgrind, later (after we collected all the commands in this benchmark file).
1689    /// As opposed to [`std::process::Command`], the build is finalized with [`Command::build`].
1690    ///
1691    /// # Relative paths
1692    ///
1693    /// Relative paths are interpreted relative to the current directory and if not running the
1694    /// benchmarks in a [`Sandbox`], depends on where `cargo bench` sets the current directory.
1695    /// Usually, it's best to use [`Path::canonicalize`] to resolve the relative path to a binary in
1696    /// your project's directory. In case you're running the benchmark in a [`Sandbox`], the path is
1697    /// interpreted relative to the root directory of the `Sandbox`. Iai-Callgrind tries to resolve
1698    /// simple names like `Command::new("echo")` searching the `$PATH`. To disambiguate between
1699    /// simple names and relative paths, use `./`. For example `echo` is searched in the `$PATH` and
1700    /// `./echo` is interpreted relative.
1701    ///
1702    /// # Examples
1703    ///
1704    /// Assume the project's binary or one of your project's binaries name is `my-echo`:
1705    ///
1706    /// ```
1707    /// # macro_rules! env { ($m:tt) => {{ "/home/my_project/target/release/my-echo" }} }
1708    /// use iai_callgrind::Command;
1709    ///
1710    /// let command = Command::new(env!("CARGO_BIN_EXE_my-echo"));
1711    /// ```
1712    ///
1713    /// or use your system's echo from the `$PATH` with
1714    ///
1715    /// ```
1716    /// use iai_callgrind::Command;
1717    ///
1718    /// let command = Command::new("echo").arg("foo").build();
1719    /// ```
1720    pub fn new<T>(path: T) -> Self
1721    where
1722        T: AsRef<OsStr>,
1723    {
1724        Self(__internal::InternalCommand {
1725            path: PathBuf::from(path.as_ref()),
1726            ..Default::default()
1727        })
1728    }
1729
1730    /// Delay the execution of the [`Command`]
1731    ///
1732    /// This option allows to delay the [`Command`] execution till a certain event has happened.
1733    /// Supported events are:
1734    ///  - Timer expired
1735    ///  - File path exists
1736    ///  - TCP/UDP connect succeeded
1737    ///
1738    /// [`Delay`] can be used in combination with [`Command::setup_parallel`] to wait for an event
1739    /// that is triggered within the `setup()` function. E.g. the setup starts a server that is
1740    /// needed by the [`Command`].
1741    ///
1742    /// # Examples
1743    ///
1744    /// ```rust
1745    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1746    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, main, Command, Delay, DelayKind};
1747    ///
1748    /// fn start_server() {
1749    ///     // action to start the server, creates pid file
1750    ///     std::fs::File::create("/tmp/my-server.pid").unwrap();
1751    /// }
1752    ///
1753    /// #[binary_benchmark]
1754    /// #[bench::server(setup = start_server)]
1755    /// fn bench_binary() -> Command {
1756    ///     Command::new(env!("CARGO_BIN_EXE_my-echo"))
1757    ///         .setup_parallel(true)
1758    ///         .delay(Delay::new(DelayKind::PathExists("/tmp/my-server.pid".into())))
1759    ///         .build()
1760    /// }
1761    ///
1762    /// binary_benchmark_group!(name = my_group; benchmarks = bench_binary);
1763    /// # fn main() {
1764    /// main!(binary_benchmark_groups = my_group);
1765    /// # }
1766    /// ```
1767    pub fn delay<T: Into<Delay>>(&mut self, delay: T) -> &mut Self {
1768        self.0.delay = Some(delay.into().into());
1769        self
1770    }
1771
1772    /// Execute the `setup()` in parallel to the [`Command`].
1773    ///
1774    /// This option changes the execution flow in a way that the [`Command`] is executed parallel
1775    /// to the `setup` instead of waiting for the `setup` to complete.
1776    ///
1777    /// This can be combined with the usage of [`Delay`] to further control the timing when the
1778    /// [`Command`] is executed.
1779    ///
1780    /// # Examples
1781    ///
1782    /// ```rust
1783    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1784    /// use std::time::Duration;
1785    /// use std::net::{SocketAddr, TcpListener};
1786    /// use std::thread;
1787    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, main, Command, Delay, DelayKind};
1788    ///
1789    /// fn setup_tcp_server() {
1790    ///     thread::sleep(Duration::from_millis(300));
1791    ///     let _listener = TcpListener::bind("127.0.0.1:31000".parse::<SocketAddr>().unwrap()).unwrap();
1792    ///     thread::sleep(Duration::from_secs(1));
1793    /// }
1794    ///
1795    /// #[binary_benchmark]
1796    /// #[bench::delay(setup = setup_tcp_server())]
1797    /// fn bench_binary() -> iai_callgrind::Command {
1798    ///     Command::new(env!("CARGO_BIN_EXE_my-echo"))
1799    ///         .setup_parallel(true)
1800    ///         .delay(
1801    ///             Delay::new(
1802    ///                 DelayKind::TcpConnect("127.0.0.1:31000".parse::<SocketAddr>().unwrap()))
1803    ///                 .timeout(Duration::from_millis(500))
1804    ///         ).build()
1805    /// }
1806    ///
1807    /// binary_benchmark_group!(name = delay; benchmarks = bench_binary);
1808    /// # fn main() {
1809    /// main!(binary_benchmark_groups = delay);
1810    /// # }
1811    /// ```
1812    pub fn setup_parallel(&mut self, setup_parallel: bool) -> &mut Self {
1813        self.0.config.setup_parallel = Some(setup_parallel);
1814        self
1815    }
1816
1817    /// Adds an argument to pass to the [`Command`]
1818    ///
1819    /// This option works exactly the same way as [`std::process::Command::arg`]. To pass multiple
1820    /// arguments see [`Command::args`].
1821    ///
1822    /// # Examples
1823    ///
1824    /// ```rust
1825    /// # use iai_callgrind::main;
1826    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1827    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
1828    ///
1829    /// #[binary_benchmark]
1830    /// fn bench_binary() -> iai_callgrind::Command {
1831    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-echo"))
1832    ///         .arg("foo")
1833    ///         .build()
1834    /// }
1835    ///
1836    /// binary_benchmark_group!(
1837    ///     name = my_group;
1838    ///     benchmarks = bench_binary
1839    /// );
1840    /// # fn main() {
1841    /// # main!(binary_benchmark_groups = my_group);
1842    /// # }
1843    /// ```
1844    pub fn arg<T>(&mut self, arg: T) -> &mut Self
1845    where
1846        T: Into<OsString>,
1847    {
1848        self.0.args.push(arg.into());
1849        self
1850    }
1851
1852    /// Adds multiple arguments to pass to the [`Command`]
1853    ///
1854    /// This option works exactly the same way as [`std::process::Command::args`].
1855    ///
1856    /// # Examples
1857    ///
1858    /// The following will execute `my-echo foo`.
1859    ///
1860    /// ```rust
1861    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1862    /// # use iai_callgrind::main;
1863    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
1864    ///
1865    /// #[binary_benchmark]
1866    /// fn bench_binary() -> iai_callgrind::Command {
1867    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-echo"))
1868    ///         .arg("foo")
1869    ///         .build()
1870    /// }
1871    ///
1872    /// binary_benchmark_group!(
1873    ///     name = my_group;
1874    ///     benchmarks = bench_binary
1875    /// );
1876    /// # fn main() {
1877    /// # main!(binary_benchmark_groups = my_group);
1878    /// # }
1879    /// ```
1880    pub fn args<I, T>(&mut self, args: T) -> &mut Self
1881    where
1882        I: Into<OsString>,
1883        T: IntoIterator<Item = I>,
1884    {
1885        self.0.args.extend(args.into_iter().map(Into::into));
1886        self
1887    }
1888
1889    /// Configuration for the process's standard input ([`Stdin`])
1890    ///
1891    /// This method takes an [`Stdin`], [`Stdio`] and everything that implements `Into<Stdin>`. The
1892    /// [`Stdin`] enum mirrors most of the possibilities of [`std::process::Stdio`] but also some
1893    /// additional possibilities most notably [`Stdin::Setup`] (see there for more details).
1894    ///
1895    /// Per default, the stdin is not inherited from the parent and any attempt by the child process
1896    /// to read from the stdin stream will result in the stream immediately closing.
1897    ///
1898    /// The options you might be interested in the most are [`Stdin::File`], which mirrors the
1899    /// behaviour of [`std::process::Stdio`] if `Stdio` is a [`std::fs::File`], and
1900    /// [`Stdin::Setup`], which is special to `iai-callgrind` and lets you pipe the output of
1901    /// the `setup` function into the Stdin of this [`Command`]. If you need to delay the `Command`
1902    /// when using [`Stdin::Setup`], you can do so with [`Command::delay`].
1903    ///
1904    /// # Implementation details
1905    ///
1906    /// As the [`Command`] itself is not executed immediately, the [`std::process::Stdio`] is not
1907    /// created either. We only use the information from here to create the [`std::process::Stdio`]
1908    /// later after we collected all commands. Setting the Stdin to `Inherit` is discouraged and
1909    /// won't have the effect you might expect, since the benchmark runner (the parent) uses the
1910    /// Stdin for its own purposes and closes it before this [`Command`] is executed.
1911    ///
1912    /// # Examples
1913    ///
1914    /// Pipe the content of a file (`benches/fixture.txt`) into the stdin of this [`Command`]:
1915    ///
1916    /// ```rust
1917    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1918    /// # use iai_callgrind::main;
1919    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
1920    ///
1921    /// #[binary_benchmark]
1922    /// fn bench_binary() -> iai_callgrind::Command {
1923    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
1924    ///         .stdin(Stdio::File("benches/fixture.txt".into()))
1925    ///         .build()
1926    /// }
1927    ///
1928    /// binary_benchmark_group!(
1929    ///     name = my_group;
1930    ///     benchmarks = bench_binary
1931    /// );
1932    /// # fn main() {
1933    /// # main!(binary_benchmark_groups = my_group);
1934    /// # }
1935    /// ```
1936    ///
1937    /// Pipe the Stdout of setup into the Stdin of this [`Command`]:
1938    ///
1939    /// ```rust
1940    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1941    /// # use iai_callgrind::main;
1942    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdin, Pipe};
1943    ///
1944    /// fn setup_pipe() {
1945    ///     // All output to Stdout of this function will be piped into the Stdin of `my-exe`
1946    ///     println!("This string will be piped into the stdin of my-exe");
1947    /// }
1948    ///
1949    /// #[binary_benchmark(setup = setup_pipe())]
1950    /// fn bench_binary() -> iai_callgrind::Command {
1951    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
1952    ///         .stdin(Stdin::Setup(Pipe::Stdout))
1953    ///         .build()
1954    /// }
1955    ///
1956    /// binary_benchmark_group!(
1957    ///     name = my_group;
1958    ///     benchmarks = bench_binary
1959    /// );
1960    /// # fn main() {
1961    /// # main!(binary_benchmark_groups = my_group);
1962    /// # }
1963    pub fn stdin<T>(&mut self, stdin: T) -> &mut Self
1964    where
1965        T: Into<Stdin>,
1966    {
1967        self.0.stdin = Some(stdin.into());
1968        self
1969    }
1970
1971    /// Configuration for the [`Command`]s standard output (Stdout) handle.
1972    ///
1973    /// The output of benchmark commands and functions are usually captured by the benchmark runner.
1974    /// This can be changed for example with the `--nocapture` option or here. Any option specified
1975    /// here takes precedence over the changes which `--nocapture` makes to the Stdout of the
1976    /// command.
1977    ///
1978    /// # Examples
1979    ///
1980    /// To see the output of this [`Command`] regardless of `--nocapture` in the benchmark output
1981    ///
1982    /// ```rust
1983    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1984    /// # use iai_callgrind::main;
1985    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
1986    ///
1987    /// #[binary_benchmark]
1988    /// fn bench_binary() -> iai_callgrind::Command {
1989    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
1990    ///         .stdout(Stdio::Inherit)
1991    ///         .build()
1992    /// }
1993    ///
1994    /// binary_benchmark_group!(
1995    ///     name = my_group;
1996    ///     benchmarks = bench_binary
1997    /// );
1998    /// # fn main() {
1999    /// # main!(binary_benchmark_groups = my_group);
2000    /// # }
2001    /// ```
2002    ///
2003    /// To pipe the Stdout into a file `/tmp/benchmark.output`:
2004    ///
2005    /// ```rust
2006    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2007    /// # use iai_callgrind::main;
2008    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2009    /// use std::path::PathBuf;
2010    ///
2011    /// #[binary_benchmark]
2012    /// fn bench_binary() -> iai_callgrind::Command {
2013    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2014    ///         .stdout(Stdio::File("/tmp/benchmark.output".into()))
2015    ///         // or
2016    ///         .stdout(PathBuf::from("/tmp/benchmark.output"))
2017    ///         .build()
2018    /// }
2019    ///
2020    /// binary_benchmark_group!(
2021    ///     name = my_group;
2022    ///     benchmarks = bench_binary
2023    /// );
2024    /// # fn main() {
2025    /// # main!(binary_benchmark_groups = my_group);
2026    /// # }
2027    /// ```
2028    pub fn stdout<T>(&mut self, stdio: T) -> &mut Self
2029    where
2030        T: Into<Stdio>,
2031    {
2032        self.0.stdout = Some(stdio.into());
2033        self
2034    }
2035
2036    /// Configuration for the [`Command`]s standard error output (Stderr) handle.
2037    ///
2038    /// This option is similar to [`Command::stdout`] but configures the Stderr. See there for more
2039    /// details.
2040    ///
2041    /// # Examples
2042    ///
2043    /// To see the error output of this [`Command`] regardless of `--nocapture` in the benchmark
2044    /// output
2045    ///
2046    /// ```rust
2047    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2048    /// # use iai_callgrind::main;
2049    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2050    ///
2051    /// #[binary_benchmark]
2052    /// fn bench_binary() -> iai_callgrind::Command {
2053    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2054    ///         .stderr(Stdio::Inherit)
2055    ///         .build()
2056    /// }
2057    ///
2058    /// binary_benchmark_group!(
2059    ///     name = my_group;
2060    ///     benchmarks = bench_binary
2061    /// );
2062    /// # fn main() {
2063    /// # main!(binary_benchmark_groups = my_group);
2064    /// # }
2065    /// ```
2066    ///
2067    /// To pipe the Stderr into a file `/tmp/benchmark.output`:
2068    ///
2069    /// ```rust
2070    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2071    /// # use iai_callgrind::main;
2072    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2073    /// use std::path::PathBuf;
2074    ///
2075    /// #[binary_benchmark]
2076    /// fn bench_binary() -> iai_callgrind::Command {
2077    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2078    ///         .stderr(Stdio::File("/tmp/benchmark.output".into()))
2079    ///         // or
2080    ///         .stderr(PathBuf::from("/tmp/benchmark.output"))
2081    ///         .build()
2082    /// }
2083    ///
2084    /// binary_benchmark_group!(
2085    ///     name = my_group;
2086    ///     benchmarks = bench_binary
2087    /// );
2088    /// # fn main() {
2089    /// # main!(binary_benchmark_groups = my_group);
2090    /// # }
2091    /// ```
2092    pub fn stderr<T>(&mut self, stdio: T) -> &mut Self
2093    where
2094        T: Into<Stdio>,
2095    {
2096        self.0.stderr = Some(stdio.into());
2097        self
2098    }
2099
2100    /// Add an environment variable available for this [`Command`]
2101    ///
2102    /// These environment variables are available independently of the setting of
2103    /// [`BinaryBenchmarkConfig::env_clear`] and additive to environment variables added with
2104    /// [`BinaryBenchmarkConfig::env`].
2105    ///
2106    /// # Examples
2107    ///
2108    /// An example for a custom environment variable "FOO=BAR":
2109    ///
2110    /// ```rust
2111    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2112    /// # use iai_callgrind::main;
2113    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2114    ///
2115    /// #[binary_benchmark]
2116    /// fn bench_binary() -> iai_callgrind::Command {
2117    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2118    ///         .env("FOO", "BAR")
2119    ///         .build()
2120    /// }
2121    ///
2122    /// binary_benchmark_group!(
2123    ///     name = my_group;
2124    ///     benchmarks = bench_binary
2125    /// );
2126    /// # fn main() {
2127    /// # main!(binary_benchmark_groups = my_group);
2128    /// # }
2129    pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
2130    where
2131        K: Into<OsString>,
2132        V: Into<OsString>,
2133    {
2134        self.0.config.envs.push((key.into(), Some(value.into())));
2135        self
2136    }
2137
2138    /// Add multiple environment variables available for this [`Command`]
2139    ///
2140    /// See [`Command::env`] for more details.
2141    ///
2142    /// # Examples
2143    ///
2144    /// Add the custom environment variables "FOO=BAR" and `BAR=BAZ`:
2145    ///
2146    /// ```rust
2147    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2148    /// # use iai_callgrind::main;
2149    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2150    ///
2151    /// #[binary_benchmark]
2152    /// fn bench_binary() -> iai_callgrind::Command {
2153    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2154    ///         .envs([("FOO", "BAR"), ("BAR", "BAZ")])
2155    ///         .build()
2156    /// }
2157    ///
2158    /// binary_benchmark_group!(
2159    ///     name = my_group;
2160    ///     benchmarks = bench_binary
2161    /// );
2162    /// # fn main() {
2163    /// # main!(binary_benchmark_groups = my_group);
2164    /// # }
2165    pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
2166    where
2167        I: IntoIterator<Item = (K, V)>,
2168        K: Into<OsString>,
2169        V: Into<OsString>,
2170    {
2171        self.0
2172            .config
2173            .envs
2174            .extend(vars.into_iter().map(|(k, v)| (k.into(), Some(v.into()))));
2175        self
2176    }
2177
2178    /// Set the directory of the benchmarked binary (Default: Unchanged)
2179    ///
2180    /// See also [`BinaryBenchmarkConfig::current_dir`]
2181    ///
2182    /// # Examples
2183    ///
2184    /// To set the working directory of your [`Command`] to `/tmp`:
2185    ///
2186    /// ```rust
2187    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2188    /// # use iai_callgrind::main;
2189    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2190    ///
2191    /// #[binary_benchmark]
2192    /// fn bench_binary() -> iai_callgrind::Command {
2193    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2194    ///         .current_dir("/tmp")
2195    ///         .build()
2196    /// }
2197    ///
2198    /// binary_benchmark_group!(
2199    ///     name = my_group;
2200    ///     benchmarks = bench_binary
2201    /// );
2202    /// # fn main() {
2203    /// # main!(binary_benchmark_groups = my_group);
2204    /// # }
2205    /// ```
2206    ///
2207    /// and the following will change the current directory to `fixtures` located in the root of the
2208    /// [`BinaryBenchmarkConfig::sandbox`]
2209    ///
2210    /// ```rust
2211    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2212    /// # use iai_callgrind::main;
2213    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Sandbox, BinaryBenchmarkConfig};
2214    ///
2215    /// fn setup_sandbox() {
2216    ///     std::fs::create_dir("fixtures").unwrap();
2217    /// }
2218    ///
2219    /// #[binary_benchmark(
2220    ///     setup = setup_sandbox(),
2221    ///     config = BinaryBenchmarkConfig::default().sandbox(Sandbox::new(true))
2222    /// )]
2223    /// fn bench_binary() -> iai_callgrind::Command {
2224    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2225    ///         .current_dir("fixtures")
2226    ///         .build()
2227    /// }
2228    ///
2229    /// binary_benchmark_group!(
2230    ///     name = my_group;
2231    ///     benchmarks = bench_binary
2232    /// );
2233    /// # fn main() {
2234    /// # main!(binary_benchmark_groups = my_group);
2235    /// # }
2236    /// ```
2237    pub fn current_dir<T>(&mut self, value: T) -> &mut Self
2238    where
2239        T: Into<PathBuf>,
2240    {
2241        self.0.config.current_dir = Some(value.into());
2242        self
2243    }
2244
2245    /// Set the expected exit status [`ExitWith`] of this [`Command`]
2246    ///
2247    /// See also [`BinaryBenchmarkConfig::exit_with`]. This setting overwrites the setting of the
2248    /// [`BinaryBenchmarkConfig`].
2249    ///
2250    /// # Examples
2251    ///
2252    /// If the command is expected to exit with a specific code, for example `100`:
2253    ///
2254    /// ```rust
2255    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2256    /// # use iai_callgrind::main;
2257    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, ExitWith};
2258    ///
2259    /// #[binary_benchmark]
2260    /// fn bench_binary() -> iai_callgrind::Command {
2261    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2262    ///         .exit_with(ExitWith::Code(100))
2263    ///         .build()
2264    /// }
2265    ///
2266    /// binary_benchmark_group!(
2267    ///     name = my_group;
2268    ///     benchmarks = bench_binary
2269    /// );
2270    /// # fn main() {
2271    /// # main!(binary_benchmark_groups = my_group);
2272    /// # }
2273    /// ```
2274    ///
2275    /// If a command is expected to fail, but the exit code doesn't matter:
2276    ///
2277    /// ```rust
2278    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2279    /// # use iai_callgrind::main;
2280    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, ExitWith};
2281    ///
2282    /// #[binary_benchmark]
2283    /// fn bench_binary() -> iai_callgrind::Command {
2284    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2285    ///         .exit_with(ExitWith::Failure)
2286    ///         .build()
2287    /// }
2288    ///
2289    /// binary_benchmark_group!(
2290    ///     name = my_group;
2291    ///     benchmarks = bench_binary
2292    /// );
2293    /// # fn main() {
2294    /// # main!(binary_benchmark_groups = my_group);
2295    /// # }
2296    /// ```
2297    pub fn exit_with(&mut self, exit_with: ExitWith) -> &mut Self {
2298        self.0.config.exit_with = Some(exit_with.into());
2299        self
2300    }
2301
2302    /// Finalize and build this [`Command`]
2303    ///
2304    /// # Examples
2305    ///
2306    /// ```rust
2307    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2308    /// use iai_callgrind::Command;
2309    ///
2310    /// let command: Command = Command::new(env!("CARGO_BIN_EXE_my-exe"))
2311    ///     .arg("foo")
2312    ///     .build();
2313    /// ```
2314    #[must_use]
2315    pub fn build(&mut self) -> Self {
2316        self.clone()
2317    }
2318}
2319
2320impl From<&mut Command> for Command {
2321    fn from(value: &mut Command) -> Self {
2322        value.clone()
2323    }
2324}
2325
2326impl From<&Command> for Command {
2327    fn from(value: &Command) -> Self {
2328        value.clone()
2329    }
2330}
2331
2332impl Delay {
2333    /// Instantiate a [`Delay`] which will wait until a path exists ([`Path::exists`]).
2334    ///
2335    /// ```rust
2336    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2337    ///
2338    /// use iai_callgrind::{Command, Delay};
2339    ///
2340    /// let command = Command::new("echo").delay(Delay::from_path("/your/path/to/wait/for"));
2341    /// ```
2342    pub fn from_path<T>(path: T) -> Self
2343    where
2344        T: Into<PathBuf>,
2345    {
2346        Self(__internal::InternalDelay {
2347            kind: DelayKind::PathExists(path.into()),
2348            ..Default::default()
2349        })
2350    }
2351
2352    /// Instantiate a [`Delay`] which will wait until successful TCP connect
2353    /// ([`std::net::TcpStream::connect`]).
2354    ///
2355    /// ```rust
2356    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2357    ///
2358    /// use std::net::SocketAddr;
2359    ///
2360    /// use iai_callgrind::{Command, Delay};
2361    ///
2362    /// let command = Command::new("echo").delay(Delay::from_tcp_socket(
2363    ///     "127.0.0.1:31000".parse::<SocketAddr>().unwrap(),
2364    /// ));
2365    /// ```
2366    pub fn from_tcp_socket<T>(addr: T) -> Self
2367    where
2368        T: Into<SocketAddr>,
2369    {
2370        Self(__internal::InternalDelay {
2371            kind: DelayKind::TcpConnect(addr.into()),
2372            ..Default::default()
2373        })
2374    }
2375
2376    /// Instantiate a [`Delay`] which will wait until a UDP response is received after
2377    /// sending the UDP request. The poll duration is also used as the reconnect and request resend
2378    /// interval. ([`std::net::UdpSocket::bind`], [`std::net::UdpSocket::connect`],
2379    /// [`std::net::UdpSocket::recv`]).
2380    ///
2381    /// ```rust
2382    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2383    ///
2384    /// use std::net::SocketAddr;
2385    ///
2386    /// use iai_callgrind::{Command, Delay};
2387    ///
2388    /// let command = Command::new("echo").delay(Delay::from_udp_request(
2389    ///     "127.0.0.1:34000".parse::<SocketAddr>().unwrap(),
2390    ///     vec![1],
2391    /// ));
2392    /// ```
2393    pub fn from_udp_request<T, U>(addr: T, req: U) -> Self
2394    where
2395        T: Into<SocketAddr>,
2396        U: Into<Vec<u8>>,
2397    {
2398        Self(__internal::InternalDelay {
2399            kind: DelayKind::UdpResponse(addr.into(), req.into()),
2400            ..Default::default()
2401        })
2402    }
2403
2404    /// Instantiate a [`Delay`] waiting until an event has happened.
2405    ///
2406    /// The possible events are defined in [`DelayKind`].
2407    ///
2408    /// ```rust
2409    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2410    ///
2411    /// use std::time::Duration;
2412    ///
2413    /// use iai_callgrind::{Command, Delay, DelayKind};
2414    ///
2415    /// let command = Command::new("echo").delay(Delay::new(DelayKind::DurationElapse(
2416    ///     Duration::from_secs(15),
2417    /// )));
2418    /// ```
2419    pub fn new(kind: DelayKind) -> Self {
2420        Self(__internal::InternalDelay {
2421            kind,
2422            ..Default::default()
2423        })
2424    }
2425
2426    /// Update the [`Delay`] poll interval.
2427    ///
2428    /// The poll interval should be considered together with the [`Delay::timeout`], and ideally
2429    /// should have a value of `n * timeout duration`.
2430    ///
2431    /// In case the poll interval is set to a value `>=` timeout duration it is attempted to set
2432    /// the poll interval to a value of `timeout duration - 5ms`.
2433    ///
2434    /// ```rust
2435    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2436    ///
2437    /// use std::net::SocketAddr;
2438    /// use std::time::Duration;
2439    ///
2440    /// use iai_callgrind::{Command, Delay};
2441    ///
2442    /// let command = Command::new("echo").delay(
2443    ///     Delay::from_udp_request("127.0.0.1:34000".parse::<SocketAddr>().unwrap(), vec![1])
2444    ///         .poll(Duration::from_millis(150)),
2445    /// );
2446    /// ```
2447    #[must_use]
2448    pub fn poll<T: Into<Duration>>(mut self, duration: T) -> Self {
2449        self.0.poll = Some(duration.into());
2450        self
2451    }
2452
2453    /// Update the [`Delay`] timeout interval.
2454    ///
2455    /// The timeout duration should be considered together with the poll interval. For further
2456    /// details please refer to [`Delay::poll`]. The minimum timeout duration is `10ms`.
2457    ///
2458    /// ```rust
2459    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2460    ///
2461    /// use std::net::SocketAddr;
2462    /// use std::time::Duration;
2463    ///
2464    /// use iai_callgrind::{Command, Delay};
2465    /// let command = Command::new("echo").delay(
2466    ///     Delay::from_tcp_socket("127.0.0.1:31000".parse::<SocketAddr>().unwrap())
2467    ///         .timeout(Duration::from_secs(5)),
2468    /// );
2469    /// ```
2470    #[must_use]
2471    pub fn timeout<T: Into<Duration>>(mut self, duration: T) -> Self {
2472        self.0.timeout = Some(duration.into());
2473        self
2474    }
2475}
2476
2477impl<T> From<T> for Delay
2478where
2479    T: Into<Duration>,
2480{
2481    /// Instantiate a [`Delay`] which will wait until the duration has elapsed.
2482    ///
2483    /// ```rust
2484    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2485    ///
2486    /// use std::time::Duration;
2487    ///
2488    /// use iai_callgrind::{Command, Delay};
2489    ///
2490    /// let command = Command::new("echo").delay(Delay::from(Duration::from_secs(10)));
2491    /// ```
2492    fn from(duration: T) -> Self {
2493        Self(__internal::InternalDelay {
2494            kind: DelayKind::DurationElapse(duration.into()),
2495            ..Default::default()
2496        })
2497    }
2498}
2499
2500impl From<ExitWith> for __internal::InternalExitWith {
2501    fn from(value: ExitWith) -> Self {
2502        match value {
2503            ExitWith::Success => Self::Success,
2504            ExitWith::Failure => Self::Failure,
2505            ExitWith::Code(c) => Self::Code(c),
2506        }
2507    }
2508}
2509
2510impl From<&ExitWith> for __internal::InternalExitWith {
2511    fn from(value: &ExitWith) -> Self {
2512        match value {
2513            ExitWith::Success => Self::Success,
2514            ExitWith::Failure => Self::Failure,
2515            ExitWith::Code(c) => Self::Code(*c),
2516        }
2517    }
2518}
2519
2520impl Sandbox {
2521    /// Create a new `Sandbox` builder
2522    ///
2523    /// Per default, a [`Command`] is not run in a `Sandbox` because setting up a `Sandbox` usually
2524    /// involves some user interaction, for example copying fixtures into it with
2525    /// [`Sandbox::fixtures`].
2526    ///
2527    /// The temporary directory is only created immediately before the `setup` and the [`Command`]
2528    /// are executed.
2529    ///
2530    /// # Examples
2531    ///
2532    /// Enable the sandbox for all benchmarks
2533    ///
2534    /// ```rust
2535    /// use iai_callgrind::{BinaryBenchmarkConfig, Sandbox, main};
2536    /// # use iai_callgrind::binary_benchmark_group;
2537    /// # binary_benchmark_group!(name = my_group; benchmarks = |_group| {});
2538    /// # fn main() {
2539    /// main!(
2540    ///     config = BinaryBenchmarkConfig::default().sandbox(Sandbox::new(true));
2541    ///     binary_benchmark_groups = my_group
2542    /// );
2543    /// # }
2544    /// ```
2545    pub fn new(enabled: bool) -> Self {
2546        Self(__internal::InternalSandbox {
2547            enabled: Some(enabled),
2548            ..Default::default()
2549        })
2550    }
2551
2552    /// Specify the directories and/or files you want to copy into the root of the `Sandbox`
2553    ///
2554    /// The paths are interpreted relative to the workspace root as it is reported by `cargo`. In a
2555    /// multi-crate project this is the directory with the top-level `Cargo.toml`. Otherwise, it is
2556    /// simply the directory with your `Cargo.toml` file in it.
2557    ///
2558    /// # Examples
2559    ///
2560    /// Assuming you crate's binary is called `my-foo` taking a file path as the first argument and
2561    /// the fixtures directory is `$WORKSPACE_ROOT/benches/fixtures` containing a fixture
2562    /// `fix_1.txt`:
2563    ///
2564    /// ```rust
2565    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2566    /// # use iai_callgrind::{binary_benchmark_group, main};
2567    /// use iai_callgrind::{binary_benchmark, BinaryBenchmarkConfig, Sandbox};
2568    ///
2569    /// #[binary_benchmark]
2570    /// #[bench::fix_1(
2571    ///      args = ("fix_1.txt"),
2572    ///      config = BinaryBenchmarkConfig::default()
2573    ///          .sandbox(Sandbox::new(true)
2574    ///              .fixtures(["benches/fixtures/fix_1.txt"])
2575    ///         )
2576    /// )]
2577    /// fn bench_with_fixtures(path: &str) -> iai_callgrind::Command {
2578    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
2579    ///         .arg(path)
2580    ///         .build()
2581    /// }
2582    /// # binary_benchmark_group!(name = my_group; benchmarks = bench_with_fixtures);
2583    /// # fn main() { main!(binary_benchmark_groups = my_group); }
2584    /// ```
2585    pub fn fixtures<I, T>(&mut self, paths: T) -> &mut Self
2586    where
2587        I: Into<PathBuf>,
2588        T: IntoIterator<Item = I>,
2589    {
2590        self.0.fixtures.extend(paths.into_iter().map(Into::into));
2591        self
2592    }
2593}
2594
2595#[cfg(test)]
2596mod tests {
2597    use rstest::rstest;
2598
2599    use super::*;
2600
2601    #[rstest]
2602    #[case::empty("")]
2603    #[case::simple_invalid("-")]
2604    #[case::when_0_char_minus_1("a\x2f")]
2605    #[case::when_9_char_plus_1("a\x3a")]
2606    #[case::when_big_a_char_minus_1("\x40")]
2607    #[case::when_big_z_char_plus_1("\x5b")]
2608    #[case::when_low_a_char_minus_1("\x60")]
2609    #[case::when_low_z_char_plus_1("\x7b")]
2610    #[case::invalid_2nd("a-")]
2611    #[case::invalid_3rd("ab-")]
2612    #[case::all_invalid("---")]
2613    #[case::number_as_first("0")]
2614    // This would be a valid rust identifier, but we don't allow the whole set
2615    #[case::non_ascii_1st("µ")]
2616    #[case::non_ascii_2nd("aµ")]
2617    #[case::non_ascii_3rd("aaµ")]
2618    #[case::non_ascii_middle("aµa")]
2619    fn test_benchmark_id_validate_when_error(#[case] id: &str) {
2620        let id = BenchmarkId::new(id);
2621        assert!(id.validate().is_err());
2622    }
2623
2624    #[rstest]
2625    #[case::lowercase_a("a")]
2626    #[case::lowercase_b("b")]
2627    #[case::lowercase_m("m")]
2628    #[case::lowercase_y("y")]
2629    #[case::lowercase_z("z")]
2630    #[case::uppercase_a("A")]
2631    #[case::uppercase_b("B")]
2632    #[case::uppercase_n("N")]
2633    #[case::uppercase_y("Y")]
2634    #[case::uppercase_z("Z")]
2635    #[case::zero_2nd("a0")]
2636    #[case::one_2nd("a1")]
2637    #[case::eight_2nd("a8")]
2638    #[case::nine_2nd("a9")]
2639    #[case::number_middle("b4t")]
2640    #[case::underscore("_")]
2641    #[case::only_underscore("___")]
2642    #[case::underscore_last("a_")]
2643    #[case::mixed_all("auAEwer9__2xcd")]
2644    fn test_benchmark_id_validate(#[case] id: &str) {
2645        let id = BenchmarkId::new(id);
2646        assert!(id.validate().is_ok());
2647    }
2648
2649    #[rstest]
2650    #[case::empty("", "Invalid id: Cannot be empty")]
2651    #[case::non_ascii_first(
2652        "µ",
2653        "Invalid id 'µ': Encountered non-ascii character as first character"
2654    )]
2655    #[case::multibyte_middle(
2656        "aµ",
2657        "Invalid id 'aµ' at position 1: Encountered non-ascii character"
2658    )]
2659    #[case::non_ascii_middle("a-", "Invalid id 'a-' at position 1: Invalid character '-'")]
2660    #[case::invalid_first("-", "Invalid id '-': As first character is '-' not allowed")]
2661    fn test_benchmark_id_validate_error_message(#[case] id: &str, #[case] message: &str) {
2662        let id = BenchmarkId::new(id);
2663        assert_eq!(
2664            id.validate()
2665                .expect_err("Validation should return an error"),
2666            message
2667        );
2668    }
2669}