1use crate::CommandError;
5use camino::{Utf8Path, Utf8PathBuf};
6use serde::{Deserialize, Serialize};
7use smol_str::SmolStr;
8use std::{
9 borrow::Cow,
10 cmp::Ordering,
11 collections::{BTreeMap, BTreeSet},
12 fmt::{self, Write as _},
13 path::PathBuf,
14 process::Command,
15};
16use target_spec::summaries::PlatformSummary;
17
18pub const GLOBAL_TEST_GROUP: &str = "@global";
23
24#[derive(Clone, Debug, Default)]
26pub struct ListCommand {
27 cargo_path: Option<Box<Utf8Path>>,
28 manifest_path: Option<Box<Utf8Path>>,
29 current_dir: Option<Box<Utf8Path>>,
30 args: Vec<Box<str>>,
31}
32
33impl ListCommand {
34 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn cargo_path(&mut self, path: impl Into<Utf8PathBuf>) -> &mut Self {
44 self.cargo_path = Some(path.into().into());
45 self
46 }
47
48 pub fn manifest_path(&mut self, path: impl Into<Utf8PathBuf>) -> &mut Self {
50 self.manifest_path = Some(path.into().into());
51 self
52 }
53
54 pub fn current_dir(&mut self, path: impl Into<Utf8PathBuf>) -> &mut Self {
56 self.current_dir = Some(path.into().into());
57 self
58 }
59
60 pub fn add_arg(&mut self, arg: impl Into<String>) -> &mut Self {
62 self.args.push(arg.into().into());
63 self
64 }
65
66 pub fn add_args(&mut self, args: impl IntoIterator<Item = impl Into<String>>) -> &mut Self {
68 for arg in args {
69 self.add_arg(arg.into());
70 }
71 self
72 }
73
74 pub fn cargo_command(&self) -> Command {
77 let cargo_path: PathBuf = self.cargo_path.as_ref().map_or_else(
78 || std::env::var_os("CARGO").map_or("cargo".into(), PathBuf::from),
79 |path| PathBuf::from(path.as_std_path()),
80 );
81
82 let mut command = Command::new(cargo_path);
83 if let Some(path) = &self.manifest_path.as_deref() {
84 command.args(["--manifest-path", path.as_str()]);
85 }
86 if let Some(current_dir) = &self.current_dir.as_deref() {
87 command.current_dir(current_dir);
88 }
89
90 command.args(["nextest", "list", "--message-format=json"]);
91
92 command.args(self.args.iter().map(|s| s.as_ref()));
93 command
94 }
95
96 pub fn exec(&self) -> Result<TestListSummary, CommandError> {
98 let mut command = self.cargo_command();
99 let output = command.output().map_err(CommandError::Exec)?;
100
101 if !output.status.success() {
102 let exit_code = output.status.code();
104 let stderr = output.stderr;
105 return Err(CommandError::CommandFailed { exit_code, stderr });
106 }
107
108 serde_json::from_slice(&output.stdout).map_err(CommandError::Json)
110 }
111
112 pub fn exec_binaries_only(&self) -> Result<BinaryListSummary, CommandError> {
115 let mut command = self.cargo_command();
116 command.arg("--list-type=binaries-only");
117 let output = command.output().map_err(CommandError::Exec)?;
118
119 if !output.status.success() {
120 let exit_code = output.status.code();
122 let stderr = output.stderr;
123 return Err(CommandError::CommandFailed { exit_code, stderr });
124 }
125
126 serde_json::from_slice(&output.stdout).map_err(CommandError::Json)
128 }
129}
130
131#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
133#[serde(rename_all = "kebab-case")]
134#[non_exhaustive]
135pub struct TestListSummary {
136 pub rust_build_meta: RustBuildMetaSummary,
138
139 pub test_count: usize,
141
142 pub rust_suites: BTreeMap<RustBinaryId, RustTestSuiteSummary>,
145}
146
147impl TestListSummary {
148 pub fn new(rust_build_meta: RustBuildMetaSummary) -> Self {
150 Self {
151 rust_build_meta,
152 test_count: 0,
153 rust_suites: BTreeMap::new(),
154 }
155 }
156 pub fn parse_json(json: impl AsRef<str>) -> Result<Self, serde_json::Error> {
158 serde_json::from_str(json.as_ref())
159 }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
164#[serde(rename_all = "kebab-case")]
165pub enum BuildPlatform {
166 Target,
168
169 Host,
171}
172
173impl fmt::Display for BuildPlatform {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 match self {
176 Self::Target => write!(f, "target"),
177 Self::Host => write!(f, "host"),
178 }
179 }
180}
181
182#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
186#[serde(rename_all = "kebab-case")]
187pub struct RustTestBinarySummary {
188 pub binary_id: RustBinaryId,
190
191 pub binary_name: String,
193
194 pub package_id: String,
198
199 pub kind: RustTestBinaryKind,
201
202 pub binary_path: Utf8PathBuf,
204
205 pub build_platform: BuildPlatform,
208}
209
210#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
215#[serde(transparent)]
216pub struct RustTestBinaryKind(pub Cow<'static, str>);
217
218impl RustTestBinaryKind {
219 #[inline]
221 pub fn new(kind: impl Into<Cow<'static, str>>) -> Self {
222 Self(kind.into())
223 }
224
225 #[inline]
227 pub const fn new_const(kind: &'static str) -> Self {
228 Self(Cow::Borrowed(kind))
229 }
230
231 pub fn as_str(&self) -> &str {
233 &self.0
234 }
235
236 pub const LIB: Self = Self::new_const("lib");
238
239 pub const TEST: Self = Self::new_const("test");
241
242 pub const BENCH: Self = Self::new_const("bench");
244
245 pub const BIN: Self = Self::new_const("bin");
247
248 pub const EXAMPLE: Self = Self::new_const("example");
250
251 pub const PROC_MACRO: Self = Self::new_const("proc-macro");
253}
254
255impl fmt::Display for RustTestBinaryKind {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 write!(f, "{}", self.0)
258 }
259}
260
261#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
263#[serde(rename_all = "kebab-case")]
264pub struct BinaryListSummary {
265 pub rust_build_meta: RustBuildMetaSummary,
267
268 pub rust_binaries: BTreeMap<RustBinaryId, RustTestBinarySummary>,
270}
271
272#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
276#[serde(transparent)]
277pub struct RustBinaryId(SmolStr);
278
279impl fmt::Display for RustBinaryId {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 f.write_str(&self.0)
282 }
283}
284
285impl RustBinaryId {
286 #[inline]
288 pub fn new(id: &str) -> Self {
289 Self(id.into())
290 }
291
292 pub fn from_parts(package_name: &str, kind: &RustTestBinaryKind, target_name: &str) -> Self {
336 let mut id = package_name.to_owned();
337 if kind == &RustTestBinaryKind::LIB || kind == &RustTestBinaryKind::PROC_MACRO {
339 } else if kind == &RustTestBinaryKind::TEST {
341 id.push_str("::");
344 id.push_str(target_name);
345 } else {
346 write!(id, "::{kind}/{target_name}").unwrap();
350 }
351
352 Self(id.into())
353 }
354
355 #[inline]
357 pub fn as_str(&self) -> &str {
358 &self.0
359 }
360
361 #[inline]
363 pub fn len(&self) -> usize {
364 self.0.len()
365 }
366
367 #[inline]
369 pub fn is_empty(&self) -> bool {
370 self.0.is_empty()
371 }
372
373 #[inline]
375 pub fn components(&self) -> RustBinaryIdComponents<'_> {
376 RustBinaryIdComponents::new(self)
377 }
378}
379
380impl<S> From<S> for RustBinaryId
381where
382 S: AsRef<str>,
383{
384 #[inline]
385 fn from(s: S) -> Self {
386 Self(s.as_ref().into())
387 }
388}
389
390impl Ord for RustBinaryId {
391 fn cmp(&self, other: &RustBinaryId) -> Ordering {
392 self.components().cmp(&other.components())
397 }
398}
399
400impl PartialOrd for RustBinaryId {
401 fn partial_cmp(&self, other: &RustBinaryId) -> Option<Ordering> {
402 Some(self.cmp(other))
403 }
404}
405
406#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
412pub struct RustBinaryIdComponents<'a> {
413 pub package_name: &'a str,
415
416 pub binary_name_and_kind: RustBinaryIdNameAndKind<'a>,
418}
419
420impl<'a> RustBinaryIdComponents<'a> {
421 fn new(id: &'a RustBinaryId) -> Self {
422 let mut parts = id.as_str().splitn(2, "::");
423
424 let package_name = parts
425 .next()
426 .expect("splitn(2) returns at least 1 component");
427 let binary_name_and_kind = if let Some(suffix) = parts.next() {
428 let mut parts = suffix.splitn(2, '/');
429
430 let part1 = parts
431 .next()
432 .expect("splitn(2) returns at least 1 component");
433 if let Some(binary_name) = parts.next() {
434 RustBinaryIdNameAndKind::NameAndKind {
435 kind: part1,
436 binary_name,
437 }
438 } else {
439 RustBinaryIdNameAndKind::NameOnly { binary_name: part1 }
440 }
441 } else {
442 RustBinaryIdNameAndKind::None
443 };
444
445 Self {
446 package_name,
447 binary_name_and_kind,
448 }
449 }
450}
451
452#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
456pub enum RustBinaryIdNameAndKind<'a> {
457 None,
459
460 NameOnly {
462 binary_name: &'a str,
464 },
465
466 NameAndKind {
468 kind: &'a str,
470
471 binary_name: &'a str,
473 },
474}
475
476#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
482#[serde(transparent)]
483pub struct TestCaseName(SmolStr);
484
485impl fmt::Display for TestCaseName {
486 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487 f.write_str(&self.0)
488 }
489}
490
491impl TestCaseName {
492 #[inline]
494 pub fn new(name: &str) -> Self {
495 Self(name.into())
496 }
497
498 #[inline]
500 pub fn as_str(&self) -> &str {
501 &self.0
502 }
503
504 #[inline]
506 pub fn as_bytes(&self) -> &[u8] {
507 self.0.as_bytes()
508 }
509
510 #[inline]
512 pub fn len(&self) -> usize {
513 self.0.len()
514 }
515
516 #[inline]
518 pub fn is_empty(&self) -> bool {
519 self.0.is_empty()
520 }
521
522 #[inline]
524 pub fn contains(&self, pattern: &str) -> bool {
525 self.0.contains(pattern)
526 }
527
528 #[inline]
547 pub fn components(&self) -> std::str::Split<'_, &str> {
548 self.0.split("::")
549 }
550
551 #[inline]
568 pub fn module_path_and_name(&self) -> (Option<&str>, &str) {
569 match self.0.rsplit_once("::") {
570 Some((module_path, name)) => (Some(module_path), name),
571 None => (None, &self.0),
572 }
573 }
574}
575
576impl AsRef<str> for TestCaseName {
577 #[inline]
578 fn as_ref(&self) -> &str {
579 &self.0
580 }
581}
582
583#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
585#[serde(rename_all = "kebab-case")]
586pub struct RustBuildMetaSummary {
587 pub target_directory: Utf8PathBuf,
589
590 #[serde(default, skip_serializing_if = "Option::is_none")]
600 pub build_directory: Option<Utf8PathBuf>,
601
602 pub base_output_directories: BTreeSet<Utf8PathBuf>,
604
605 pub non_test_binaries: BTreeMap<String, BTreeSet<RustNonTestBinarySummary>>,
607
608 #[serde(default)]
614 pub build_script_out_dirs: BTreeMap<String, Utf8PathBuf>,
615
616 #[serde(default)]
624 pub build_script_info: Option<BTreeMap<String, BuildScriptInfoSummary>>,
625
626 pub linked_paths: BTreeSet<Utf8PathBuf>,
628
629 #[serde(default)]
633 pub platforms: Option<BuildPlatformsSummary>,
634
635 #[serde(default)]
639 pub target_platforms: Vec<PlatformSummary>,
640
641 #[serde(default)]
646 pub target_platform: Option<String>,
647}
648
649#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
654#[serde(rename_all = "kebab-case")]
655pub struct BuildScriptInfoSummary {
656 #[serde(default)]
659 pub envs: BTreeMap<String, String>,
660}
661
662#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
665#[serde(rename_all = "kebab-case")]
666pub struct RustNonTestBinarySummary {
667 pub name: String,
669
670 pub kind: RustNonTestBinaryKind,
672
673 pub path: Utf8PathBuf,
675}
676
677#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
679#[serde(rename_all = "kebab-case")]
680pub struct BuildPlatformsSummary {
681 pub host: HostPlatformSummary,
683
684 pub targets: Vec<TargetPlatformSummary>,
688}
689
690#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
692#[serde(rename_all = "kebab-case")]
693pub struct HostPlatformSummary {
694 pub platform: PlatformSummary,
696
697 pub libdir: PlatformLibdirSummary,
699}
700
701#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
703#[serde(rename_all = "kebab-case")]
704pub struct TargetPlatformSummary {
705 pub platform: PlatformSummary,
707
708 pub libdir: PlatformLibdirSummary,
712}
713
714#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
716#[serde(tag = "status", rename_all = "kebab-case")]
717pub enum PlatformLibdirSummary {
718 Available {
720 path: Utf8PathBuf,
722 },
723
724 Unavailable {
726 reason: PlatformLibdirUnavailable,
728 },
729}
730
731#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
737pub struct PlatformLibdirUnavailable(pub Cow<'static, str>);
738
739impl PlatformLibdirUnavailable {
740 pub const RUSTC_FAILED: Self = Self::new_const("rustc-failed");
742
743 pub const RUSTC_OUTPUT_ERROR: Self = Self::new_const("rustc-output-error");
746
747 pub const OLD_SUMMARY: Self = Self::new_const("old-summary");
750
751 pub const NOT_IN_ARCHIVE: Self = Self::new_const("not-in-archive");
754
755 pub const fn new_const(reason: &'static str) -> Self {
757 Self(Cow::Borrowed(reason))
758 }
759
760 pub fn new(reason: impl Into<Cow<'static, str>>) -> Self {
762 Self(reason.into())
763 }
764
765 pub fn as_str(&self) -> &str {
767 &self.0
768 }
769}
770
771#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
776#[serde(transparent)]
777pub struct RustNonTestBinaryKind(pub Cow<'static, str>);
778
779impl RustNonTestBinaryKind {
780 #[inline]
782 pub fn new(kind: impl Into<Cow<'static, str>>) -> Self {
783 Self(kind.into())
784 }
785
786 #[inline]
788 pub const fn new_const(kind: &'static str) -> Self {
789 Self(Cow::Borrowed(kind))
790 }
791
792 pub fn as_str(&self) -> &str {
794 &self.0
795 }
796
797 pub const DYLIB: Self = Self::new_const("dylib");
800
801 pub const BIN_EXE: Self = Self::new_const("bin-exe");
803}
804
805impl fmt::Display for RustNonTestBinaryKind {
806 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
807 write!(f, "{}", self.0)
808 }
809}
810
811#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
815#[serde(rename_all = "kebab-case")]
816pub struct RustTestSuiteSummary {
817 pub package_name: String,
819
820 #[serde(flatten)]
822 pub binary: RustTestBinarySummary,
823
824 pub cwd: Utf8PathBuf,
826
827 #[serde(default = "listed_status")]
832 pub status: RustTestSuiteStatusSummary,
833
834 #[serde(rename = "testcases")]
836 pub test_cases: BTreeMap<TestCaseName, RustTestCaseSummary>,
837}
838
839fn listed_status() -> RustTestSuiteStatusSummary {
840 RustTestSuiteStatusSummary::LISTED
841}
842
843#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
847#[serde(transparent)]
848pub struct RustTestSuiteStatusSummary(pub Cow<'static, str>);
849
850impl RustTestSuiteStatusSummary {
851 #[inline]
853 pub fn new(kind: impl Into<Cow<'static, str>>) -> Self {
854 Self(kind.into())
855 }
856
857 #[inline]
859 pub const fn new_const(kind: &'static str) -> Self {
860 Self(Cow::Borrowed(kind))
861 }
862
863 pub fn as_str(&self) -> &str {
865 &self.0
866 }
867
868 pub const LISTED: Self = Self::new_const("listed");
871
872 pub const SKIPPED: Self = Self::new_const("skipped");
877
878 pub const SKIPPED_DEFAULT_FILTER: Self = Self::new_const("skipped-default-filter");
882}
883
884#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
888#[serde(rename_all = "kebab-case")]
889pub struct RustTestCaseSummary {
890 pub kind: Option<RustTestKind>,
895
896 pub ignored: bool,
900
901 pub filter_match: FilterMatch,
905}
906
907#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
911#[serde(transparent)]
912pub struct RustTestKind(pub Cow<'static, str>);
913
914impl RustTestKind {
915 #[inline]
917 pub fn new(kind: impl Into<Cow<'static, str>>) -> Self {
918 Self(kind.into())
919 }
920
921 #[inline]
923 pub const fn new_const(kind: &'static str) -> Self {
924 Self(Cow::Borrowed(kind))
925 }
926
927 pub fn as_str(&self) -> &str {
929 &self.0
930 }
931
932 pub const TEST: Self = Self::new_const("test");
934
935 pub const BENCH: Self = Self::new_const("bench");
937}
938
939#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
941#[serde(rename_all = "kebab-case", tag = "status")]
942pub enum FilterMatch {
943 Matches,
945
946 Mismatch {
948 reason: MismatchReason,
950 },
951}
952
953impl FilterMatch {
954 pub fn is_match(&self) -> bool {
956 matches!(self, FilterMatch::Matches)
957 }
958}
959
960#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
962#[serde(rename_all = "kebab-case")]
963#[non_exhaustive]
964pub enum MismatchReason {
965 NotBenchmark,
967
968 Ignored,
970
971 String,
973
974 Expression,
976
977 Partition,
979
980 RerunAlreadyPassed,
982
983 DefaultFilter,
987}
988
989impl MismatchReason {
990 pub const ALL_VARIANTS: &'static [Self] = &[
995 Self::NotBenchmark,
996 Self::Ignored,
997 Self::String,
998 Self::Expression,
999 Self::Partition,
1000 Self::RerunAlreadyPassed,
1001 Self::DefaultFilter,
1002 ];
1003}
1004
1005impl fmt::Display for MismatchReason {
1006 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1007 match self {
1008 MismatchReason::NotBenchmark => write!(f, "is not a benchmark"),
1009 MismatchReason::Ignored => write!(f, "does not match the run-ignored option"),
1010 MismatchReason::String => write!(f, "does not match the provided string filters"),
1011 MismatchReason::Expression => {
1012 write!(f, "does not match the provided expression filters")
1013 }
1014 MismatchReason::Partition => write!(f, "is in a different partition"),
1015 MismatchReason::RerunAlreadyPassed => write!(f, "already passed"),
1016 MismatchReason::DefaultFilter => {
1017 write!(f, "is filtered out by the profile's default-filter")
1018 }
1019 }
1020 }
1021}
1022
1023#[cfg(feature = "proptest1")]
1026mod proptest_impls {
1027 use super::*;
1028 use proptest::prelude::*;
1029
1030 impl Arbitrary for RustBinaryId {
1031 type Parameters = ();
1032 type Strategy = BoxedStrategy<Self>;
1033
1034 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
1035 any::<String>().prop_map(|s| RustBinaryId::new(&s)).boxed()
1036 }
1037 }
1038
1039 impl Arbitrary for TestCaseName {
1040 type Parameters = ();
1041 type Strategy = BoxedStrategy<Self>;
1042
1043 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
1044 any::<String>().prop_map(|s| TestCaseName::new(&s)).boxed()
1045 }
1046 }
1047
1048 impl Arbitrary for MismatchReason {
1049 type Parameters = ();
1050 type Strategy = BoxedStrategy<Self>;
1051
1052 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
1053 proptest::sample::select(MismatchReason::ALL_VARIANTS).boxed()
1054 }
1055 }
1056}
1057
1058#[cfg(test)]
1059mod tests {
1060 use super::*;
1061 use test_case::test_case;
1062
1063 #[test_case(r#"{
1064 "target-directory": "/foo",
1065 "base-output-directories": [],
1066 "non-test-binaries": {},
1067 "linked-paths": []
1068 }"#, RustBuildMetaSummary {
1069 target_directory: "/foo".into(),
1070 build_directory: None,
1071 base_output_directories: BTreeSet::new(),
1072 non_test_binaries: BTreeMap::new(),
1073 build_script_out_dirs: BTreeMap::new(),
1074 build_script_info: None,
1075 linked_paths: BTreeSet::new(),
1076 target_platform: None,
1077 target_platforms: vec![],
1078 platforms: None,
1079 }; "no target platform")]
1080 #[test_case(r#"{
1081 "target-directory": "/foo",
1082 "base-output-directories": [],
1083 "non-test-binaries": {},
1084 "linked-paths": [],
1085 "target-platform": "x86_64-unknown-linux-gnu"
1086 }"#, RustBuildMetaSummary {
1087 target_directory: "/foo".into(),
1088 build_directory: None,
1089 base_output_directories: BTreeSet::new(),
1090 non_test_binaries: BTreeMap::new(),
1091 build_script_out_dirs: BTreeMap::new(),
1092 build_script_info: None,
1093 linked_paths: BTreeSet::new(),
1094 target_platform: Some("x86_64-unknown-linux-gnu".to_owned()),
1095 target_platforms: vec![],
1096 platforms: None,
1097 }; "single target platform specified")]
1098 fn test_deserialize_old_rust_build_meta(input: &str, expected: RustBuildMetaSummary) {
1099 let build_meta: RustBuildMetaSummary =
1100 serde_json::from_str(input).expect("input deserialized correctly");
1101 assert_eq!(
1102 build_meta, expected,
1103 "deserialized input matched expected output"
1104 );
1105 }
1106
1107 #[test]
1108 fn test_binary_id_ord() {
1109 let empty = RustBinaryId::new("");
1110 let foo = RustBinaryId::new("foo");
1111 let bar = RustBinaryId::new("bar");
1112 let foo_name1 = RustBinaryId::new("foo::name1");
1113 let foo_name2 = RustBinaryId::new("foo::name2");
1114 let bar_name = RustBinaryId::new("bar::name");
1115 let foo_bin_name1 = RustBinaryId::new("foo::bin/name1");
1116 let foo_bin_name2 = RustBinaryId::new("foo::bin/name2");
1117 let bar_bin_name = RustBinaryId::new("bar::bin/name");
1118 let foo_proc_macro_name = RustBinaryId::new("foo::proc_macro/name");
1119 let bar_proc_macro_name = RustBinaryId::new("bar::proc_macro/name");
1120
1121 let sorted_ids = [
1123 empty,
1124 bar,
1125 bar_name,
1126 bar_bin_name,
1127 bar_proc_macro_name,
1128 foo,
1129 foo_name1,
1130 foo_name2,
1131 foo_bin_name1,
1132 foo_bin_name2,
1133 foo_proc_macro_name,
1134 ];
1135
1136 for (i, id) in sorted_ids.iter().enumerate() {
1137 for (j, other_id) in sorted_ids.iter().enumerate() {
1138 let expected = i.cmp(&j);
1139 assert_eq!(
1140 id.cmp(other_id),
1141 expected,
1142 "comparing {id:?} to {other_id:?} gave {expected:?}"
1143 );
1144 }
1145 }
1146 }
1147
1148 #[test]
1150 fn mismatch_reason_all_variants_is_complete() {
1151 fn check_exhaustive(reason: MismatchReason) {
1153 match reason {
1154 MismatchReason::NotBenchmark
1155 | MismatchReason::Ignored
1156 | MismatchReason::String
1157 | MismatchReason::Expression
1158 | MismatchReason::Partition
1159 | MismatchReason::RerunAlreadyPassed
1160 | MismatchReason::DefaultFilter => {}
1161 }
1162 }
1163
1164 for &reason in MismatchReason::ALL_VARIANTS {
1165 check_exhaustive(reason);
1166 }
1167
1168 assert_eq!(MismatchReason::ALL_VARIANTS.len(), 7);
1170 }
1171}