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
18#[derive(Clone, Debug, Default)]
20pub struct ListCommand {
21 cargo_path: Option<Box<Utf8Path>>,
22 manifest_path: Option<Box<Utf8Path>>,
23 current_dir: Option<Box<Utf8Path>>,
24 args: Vec<Box<str>>,
25}
26
27impl ListCommand {
28 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn cargo_path(&mut self, path: impl Into<Utf8PathBuf>) -> &mut Self {
38 self.cargo_path = Some(path.into().into());
39 self
40 }
41
42 pub fn manifest_path(&mut self, path: impl Into<Utf8PathBuf>) -> &mut Self {
44 self.manifest_path = Some(path.into().into());
45 self
46 }
47
48 pub fn current_dir(&mut self, path: impl Into<Utf8PathBuf>) -> &mut Self {
50 self.current_dir = Some(path.into().into());
51 self
52 }
53
54 pub fn add_arg(&mut self, arg: impl Into<String>) -> &mut Self {
56 self.args.push(arg.into().into());
57 self
58 }
59
60 pub fn add_args(&mut self, args: impl IntoIterator<Item = impl Into<String>>) -> &mut Self {
62 for arg in args {
63 self.add_arg(arg.into());
64 }
65 self
66 }
67
68 pub fn cargo_command(&self) -> Command {
71 let cargo_path: PathBuf = self.cargo_path.as_ref().map_or_else(
72 || std::env::var_os("CARGO").map_or("cargo".into(), PathBuf::from),
73 |path| PathBuf::from(path.as_std_path()),
74 );
75
76 let mut command = Command::new(cargo_path);
77 if let Some(path) = &self.manifest_path.as_deref() {
78 command.args(["--manifest-path", path.as_str()]);
79 }
80 if let Some(current_dir) = &self.current_dir.as_deref() {
81 command.current_dir(current_dir);
82 }
83
84 command.args(["nextest", "list", "--message-format=json"]);
85
86 command.args(self.args.iter().map(|s| s.as_ref()));
87 command
88 }
89
90 pub fn exec(&self) -> Result<TestListSummary, CommandError> {
92 let mut command = self.cargo_command();
93 let output = command.output().map_err(CommandError::Exec)?;
94
95 if !output.status.success() {
96 let exit_code = output.status.code();
98 let stderr = output.stderr;
99 return Err(CommandError::CommandFailed { exit_code, stderr });
100 }
101
102 serde_json::from_slice(&output.stdout).map_err(CommandError::Json)
104 }
105
106 pub fn exec_binaries_only(&self) -> Result<BinaryListSummary, CommandError> {
109 let mut command = self.cargo_command();
110 command.arg("--list-type=binaries-only");
111 let output = command.output().map_err(CommandError::Exec)?;
112
113 if !output.status.success() {
114 let exit_code = output.status.code();
116 let stderr = output.stderr;
117 return Err(CommandError::CommandFailed { exit_code, stderr });
118 }
119
120 serde_json::from_slice(&output.stdout).map_err(CommandError::Json)
122 }
123}
124
125#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
127#[serde(rename_all = "kebab-case")]
128#[non_exhaustive]
129pub struct TestListSummary {
130 pub rust_build_meta: RustBuildMetaSummary,
132
133 pub test_count: usize,
135
136 pub rust_suites: BTreeMap<RustBinaryId, RustTestSuiteSummary>,
139}
140
141impl TestListSummary {
142 pub fn new(rust_build_meta: RustBuildMetaSummary) -> Self {
144 Self {
145 rust_build_meta,
146 test_count: 0,
147 rust_suites: BTreeMap::new(),
148 }
149 }
150 pub fn parse_json(json: impl AsRef<str>) -> Result<Self, serde_json::Error> {
152 serde_json::from_str(json.as_ref())
153 }
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
158#[serde(rename_all = "kebab-case")]
159pub enum BuildPlatform {
160 Target,
162
163 Host,
165}
166
167impl fmt::Display for BuildPlatform {
168 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169 match self {
170 Self::Target => write!(f, "target"),
171 Self::Host => write!(f, "host"),
172 }
173 }
174}
175
176#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
180#[serde(rename_all = "kebab-case")]
181pub struct RustTestBinarySummary {
182 pub binary_id: RustBinaryId,
184
185 pub binary_name: String,
187
188 pub package_id: String,
192
193 pub kind: RustTestBinaryKind,
195
196 pub binary_path: Utf8PathBuf,
198
199 pub build_platform: BuildPlatform,
202}
203
204#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
209#[serde(transparent)]
210pub struct RustTestBinaryKind(pub Cow<'static, str>);
211
212impl RustTestBinaryKind {
213 #[inline]
215 pub fn new(kind: impl Into<Cow<'static, str>>) -> Self {
216 Self(kind.into())
217 }
218
219 #[inline]
221 pub const fn new_const(kind: &'static str) -> Self {
222 Self(Cow::Borrowed(kind))
223 }
224
225 pub fn as_str(&self) -> &str {
227 &self.0
228 }
229
230 pub const LIB: Self = Self::new_const("lib");
232
233 pub const TEST: Self = Self::new_const("test");
235
236 pub const BENCH: Self = Self::new_const("bench");
238
239 pub const BIN: Self = Self::new_const("bin");
241
242 pub const EXAMPLE: Self = Self::new_const("example");
244
245 pub const PROC_MACRO: Self = Self::new_const("proc-macro");
247}
248
249impl fmt::Display for RustTestBinaryKind {
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 write!(f, "{}", self.0)
252 }
253}
254
255#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
257#[serde(rename_all = "kebab-case")]
258pub struct BinaryListSummary {
259 pub rust_build_meta: RustBuildMetaSummary,
261
262 pub rust_binaries: BTreeMap<RustBinaryId, RustTestBinarySummary>,
264}
265
266#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
270#[serde(transparent)]
271pub struct RustBinaryId(SmolStr);
272
273impl fmt::Display for RustBinaryId {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 f.write_str(&self.0)
276 }
277}
278
279impl RustBinaryId {
280 #[inline]
282 pub fn new(id: &str) -> Self {
283 Self(id.into())
284 }
285
286 pub fn from_parts(package_name: &str, kind: &RustTestBinaryKind, target_name: &str) -> Self {
330 let mut id = package_name.to_owned();
331 if kind == &RustTestBinaryKind::LIB || kind == &RustTestBinaryKind::PROC_MACRO {
333 } else if kind == &RustTestBinaryKind::TEST {
335 id.push_str("::");
338 id.push_str(target_name);
339 } else {
340 write!(id, "::{kind}/{target_name}").unwrap();
344 }
345
346 Self(id.into())
347 }
348
349 #[inline]
351 pub fn as_str(&self) -> &str {
352 &self.0
353 }
354
355 #[inline]
357 pub fn len(&self) -> usize {
358 self.0.len()
359 }
360
361 #[inline]
363 pub fn is_empty(&self) -> bool {
364 self.0.is_empty()
365 }
366
367 #[inline]
369 pub fn components(&self) -> RustBinaryIdComponents<'_> {
370 RustBinaryIdComponents::new(self)
371 }
372}
373
374impl<S> From<S> for RustBinaryId
375where
376 S: AsRef<str>,
377{
378 #[inline]
379 fn from(s: S) -> Self {
380 Self(s.as_ref().into())
381 }
382}
383
384impl Ord for RustBinaryId {
385 fn cmp(&self, other: &RustBinaryId) -> Ordering {
386 self.components().cmp(&other.components())
391 }
392}
393
394impl PartialOrd for RustBinaryId {
395 fn partial_cmp(&self, other: &RustBinaryId) -> Option<Ordering> {
396 Some(self.cmp(other))
397 }
398}
399
400#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
406pub struct RustBinaryIdComponents<'a> {
407 pub package_name: &'a str,
409
410 pub binary_name_and_kind: RustBinaryIdNameAndKind<'a>,
412}
413
414impl<'a> RustBinaryIdComponents<'a> {
415 fn new(id: &'a RustBinaryId) -> Self {
416 let mut parts = id.as_str().splitn(2, "::");
417
418 let package_name = parts
419 .next()
420 .expect("splitn(2) returns at least 1 component");
421 let binary_name_and_kind = if let Some(suffix) = parts.next() {
422 let mut parts = suffix.splitn(2, '/');
423
424 let part1 = parts
425 .next()
426 .expect("splitn(2) returns at least 1 component");
427 if let Some(binary_name) = parts.next() {
428 RustBinaryIdNameAndKind::NameAndKind {
429 kind: part1,
430 binary_name,
431 }
432 } else {
433 RustBinaryIdNameAndKind::NameOnly { binary_name: part1 }
434 }
435 } else {
436 RustBinaryIdNameAndKind::None
437 };
438
439 Self {
440 package_name,
441 binary_name_and_kind,
442 }
443 }
444}
445
446#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
450pub enum RustBinaryIdNameAndKind<'a> {
451 None,
453
454 NameOnly {
456 binary_name: &'a str,
458 },
459
460 NameAndKind {
462 kind: &'a str,
464
465 binary_name: &'a str,
467 },
468}
469
470#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
476#[serde(transparent)]
477pub struct TestCaseName(SmolStr);
478
479impl fmt::Display for TestCaseName {
480 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481 f.write_str(&self.0)
482 }
483}
484
485impl TestCaseName {
486 #[inline]
488 pub fn new(name: &str) -> Self {
489 Self(name.into())
490 }
491
492 #[inline]
494 pub fn as_str(&self) -> &str {
495 &self.0
496 }
497
498 #[inline]
500 pub fn as_bytes(&self) -> &[u8] {
501 self.0.as_bytes()
502 }
503
504 #[inline]
506 pub fn len(&self) -> usize {
507 self.0.len()
508 }
509
510 #[inline]
512 pub fn is_empty(&self) -> bool {
513 self.0.is_empty()
514 }
515
516 #[inline]
518 pub fn contains(&self, pattern: &str) -> bool {
519 self.0.contains(pattern)
520 }
521
522 #[inline]
541 pub fn components(&self) -> std::str::Split<'_, &str> {
542 self.0.split("::")
543 }
544
545 #[inline]
562 pub fn module_path_and_name(&self) -> (Option<&str>, &str) {
563 match self.0.rsplit_once("::") {
564 Some((module_path, name)) => (Some(module_path), name),
565 None => (None, &self.0),
566 }
567 }
568}
569
570impl AsRef<str> for TestCaseName {
571 #[inline]
572 fn as_ref(&self) -> &str {
573 &self.0
574 }
575}
576
577#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
579#[serde(rename_all = "kebab-case")]
580pub struct RustBuildMetaSummary {
581 pub target_directory: Utf8PathBuf,
583
584 #[serde(default, skip_serializing_if = "Option::is_none")]
594 pub build_directory: Option<Utf8PathBuf>,
595
596 pub base_output_directories: BTreeSet<Utf8PathBuf>,
598
599 pub non_test_binaries: BTreeMap<String, BTreeSet<RustNonTestBinarySummary>>,
601
602 #[serde(default)]
608 pub build_script_out_dirs: BTreeMap<String, Utf8PathBuf>,
609
610 #[serde(default)]
618 pub build_script_info: Option<BTreeMap<String, BuildScriptInfoSummary>>,
619
620 pub linked_paths: BTreeSet<Utf8PathBuf>,
622
623 #[serde(default)]
627 pub platforms: Option<BuildPlatformsSummary>,
628
629 #[serde(default)]
633 pub target_platforms: Vec<PlatformSummary>,
634
635 #[serde(default)]
640 pub target_platform: Option<String>,
641}
642
643#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
648#[serde(rename_all = "kebab-case")]
649pub struct BuildScriptInfoSummary {
650 #[serde(default)]
653 pub envs: BTreeMap<String, String>,
654}
655
656#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
659#[serde(rename_all = "kebab-case")]
660pub struct RustNonTestBinarySummary {
661 pub name: String,
663
664 pub kind: RustNonTestBinaryKind,
666
667 pub path: Utf8PathBuf,
669}
670
671#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
673#[serde(rename_all = "kebab-case")]
674pub struct BuildPlatformsSummary {
675 pub host: HostPlatformSummary,
677
678 pub targets: Vec<TargetPlatformSummary>,
682}
683
684#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
686#[serde(rename_all = "kebab-case")]
687pub struct HostPlatformSummary {
688 pub platform: PlatformSummary,
690
691 pub libdir: PlatformLibdirSummary,
693}
694
695#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
697#[serde(rename_all = "kebab-case")]
698pub struct TargetPlatformSummary {
699 pub platform: PlatformSummary,
701
702 pub libdir: PlatformLibdirSummary,
706}
707
708#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
710#[serde(tag = "status", rename_all = "kebab-case")]
711pub enum PlatformLibdirSummary {
712 Available {
714 path: Utf8PathBuf,
716 },
717
718 Unavailable {
720 reason: PlatformLibdirUnavailable,
722 },
723}
724
725#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
731pub struct PlatformLibdirUnavailable(pub Cow<'static, str>);
732
733impl PlatformLibdirUnavailable {
734 pub const RUSTC_FAILED: Self = Self::new_const("rustc-failed");
736
737 pub const RUSTC_OUTPUT_ERROR: Self = Self::new_const("rustc-output-error");
740
741 pub const OLD_SUMMARY: Self = Self::new_const("old-summary");
744
745 pub const NOT_IN_ARCHIVE: Self = Self::new_const("not-in-archive");
748
749 pub const fn new_const(reason: &'static str) -> Self {
751 Self(Cow::Borrowed(reason))
752 }
753
754 pub fn new(reason: impl Into<Cow<'static, str>>) -> Self {
756 Self(reason.into())
757 }
758
759 pub fn as_str(&self) -> &str {
761 &self.0
762 }
763}
764
765#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
770#[serde(transparent)]
771pub struct RustNonTestBinaryKind(pub Cow<'static, str>);
772
773impl RustNonTestBinaryKind {
774 #[inline]
776 pub fn new(kind: impl Into<Cow<'static, str>>) -> Self {
777 Self(kind.into())
778 }
779
780 #[inline]
782 pub const fn new_const(kind: &'static str) -> Self {
783 Self(Cow::Borrowed(kind))
784 }
785
786 pub fn as_str(&self) -> &str {
788 &self.0
789 }
790
791 pub const DYLIB: Self = Self::new_const("dylib");
794
795 pub const BIN_EXE: Self = Self::new_const("bin-exe");
797}
798
799impl fmt::Display for RustNonTestBinaryKind {
800 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
801 write!(f, "{}", self.0)
802 }
803}
804
805#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
809#[serde(rename_all = "kebab-case")]
810pub struct RustTestSuiteSummary {
811 pub package_name: String,
813
814 #[serde(flatten)]
816 pub binary: RustTestBinarySummary,
817
818 pub cwd: Utf8PathBuf,
820
821 #[serde(default = "listed_status")]
826 pub status: RustTestSuiteStatusSummary,
827
828 #[serde(rename = "testcases")]
830 pub test_cases: BTreeMap<TestCaseName, RustTestCaseSummary>,
831}
832
833fn listed_status() -> RustTestSuiteStatusSummary {
834 RustTestSuiteStatusSummary::LISTED
835}
836
837#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
841#[serde(transparent)]
842pub struct RustTestSuiteStatusSummary(pub Cow<'static, str>);
843
844impl RustTestSuiteStatusSummary {
845 #[inline]
847 pub fn new(kind: impl Into<Cow<'static, str>>) -> Self {
848 Self(kind.into())
849 }
850
851 #[inline]
853 pub const fn new_const(kind: &'static str) -> Self {
854 Self(Cow::Borrowed(kind))
855 }
856
857 pub fn as_str(&self) -> &str {
859 &self.0
860 }
861
862 pub const LISTED: Self = Self::new_const("listed");
865
866 pub const SKIPPED: Self = Self::new_const("skipped");
871
872 pub const SKIPPED_DEFAULT_FILTER: Self = Self::new_const("skipped-default-filter");
876}
877
878#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
882#[serde(rename_all = "kebab-case")]
883pub struct RustTestCaseSummary {
884 pub kind: Option<RustTestKind>,
889
890 pub ignored: bool,
894
895 pub filter_match: FilterMatch,
899}
900
901#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
905#[serde(transparent)]
906pub struct RustTestKind(pub Cow<'static, str>);
907
908impl RustTestKind {
909 #[inline]
911 pub fn new(kind: impl Into<Cow<'static, str>>) -> Self {
912 Self(kind.into())
913 }
914
915 #[inline]
917 pub const fn new_const(kind: &'static str) -> Self {
918 Self(Cow::Borrowed(kind))
919 }
920
921 pub fn as_str(&self) -> &str {
923 &self.0
924 }
925
926 pub const TEST: Self = Self::new_const("test");
928
929 pub const BENCH: Self = Self::new_const("bench");
931}
932
933#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
935#[serde(rename_all = "kebab-case", tag = "status")]
936pub enum FilterMatch {
937 Matches,
939
940 Mismatch {
942 reason: MismatchReason,
944 },
945}
946
947impl FilterMatch {
948 pub fn is_match(&self) -> bool {
950 matches!(self, FilterMatch::Matches)
951 }
952}
953
954#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
956#[serde(rename_all = "kebab-case")]
957#[non_exhaustive]
958pub enum MismatchReason {
959 NotBenchmark,
961
962 Ignored,
964
965 String,
967
968 Expression,
970
971 Partition,
973
974 RerunAlreadyPassed,
976
977 DefaultFilter,
981}
982
983impl MismatchReason {
984 pub const ALL_VARIANTS: &'static [Self] = &[
989 Self::NotBenchmark,
990 Self::Ignored,
991 Self::String,
992 Self::Expression,
993 Self::Partition,
994 Self::RerunAlreadyPassed,
995 Self::DefaultFilter,
996 ];
997}
998
999impl fmt::Display for MismatchReason {
1000 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1001 match self {
1002 MismatchReason::NotBenchmark => write!(f, "is not a benchmark"),
1003 MismatchReason::Ignored => write!(f, "does not match the run-ignored option"),
1004 MismatchReason::String => write!(f, "does not match the provided string filters"),
1005 MismatchReason::Expression => {
1006 write!(f, "does not match the provided expression filters")
1007 }
1008 MismatchReason::Partition => write!(f, "is in a different partition"),
1009 MismatchReason::RerunAlreadyPassed => write!(f, "already passed"),
1010 MismatchReason::DefaultFilter => {
1011 write!(f, "is filtered out by the profile's default-filter")
1012 }
1013 }
1014 }
1015}
1016
1017#[cfg(feature = "proptest1")]
1020mod proptest_impls {
1021 use super::*;
1022 use proptest::prelude::*;
1023
1024 impl Arbitrary for RustBinaryId {
1025 type Parameters = ();
1026 type Strategy = BoxedStrategy<Self>;
1027
1028 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
1029 any::<String>().prop_map(|s| RustBinaryId::new(&s)).boxed()
1030 }
1031 }
1032
1033 impl Arbitrary for TestCaseName {
1034 type Parameters = ();
1035 type Strategy = BoxedStrategy<Self>;
1036
1037 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
1038 any::<String>().prop_map(|s| TestCaseName::new(&s)).boxed()
1039 }
1040 }
1041
1042 impl Arbitrary for MismatchReason {
1043 type Parameters = ();
1044 type Strategy = BoxedStrategy<Self>;
1045
1046 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
1047 proptest::sample::select(MismatchReason::ALL_VARIANTS).boxed()
1048 }
1049 }
1050}
1051
1052#[cfg(test)]
1053mod tests {
1054 use super::*;
1055 use test_case::test_case;
1056
1057 #[test_case(r#"{
1058 "target-directory": "/foo",
1059 "base-output-directories": [],
1060 "non-test-binaries": {},
1061 "linked-paths": []
1062 }"#, RustBuildMetaSummary {
1063 target_directory: "/foo".into(),
1064 build_directory: None,
1065 base_output_directories: BTreeSet::new(),
1066 non_test_binaries: BTreeMap::new(),
1067 build_script_out_dirs: BTreeMap::new(),
1068 build_script_info: None,
1069 linked_paths: BTreeSet::new(),
1070 target_platform: None,
1071 target_platforms: vec![],
1072 platforms: None,
1073 }; "no target platform")]
1074 #[test_case(r#"{
1075 "target-directory": "/foo",
1076 "base-output-directories": [],
1077 "non-test-binaries": {},
1078 "linked-paths": [],
1079 "target-platform": "x86_64-unknown-linux-gnu"
1080 }"#, RustBuildMetaSummary {
1081 target_directory: "/foo".into(),
1082 build_directory: None,
1083 base_output_directories: BTreeSet::new(),
1084 non_test_binaries: BTreeMap::new(),
1085 build_script_out_dirs: BTreeMap::new(),
1086 build_script_info: None,
1087 linked_paths: BTreeSet::new(),
1088 target_platform: Some("x86_64-unknown-linux-gnu".to_owned()),
1089 target_platforms: vec![],
1090 platforms: None,
1091 }; "single target platform specified")]
1092 fn test_deserialize_old_rust_build_meta(input: &str, expected: RustBuildMetaSummary) {
1093 let build_meta: RustBuildMetaSummary =
1094 serde_json::from_str(input).expect("input deserialized correctly");
1095 assert_eq!(
1096 build_meta, expected,
1097 "deserialized input matched expected output"
1098 );
1099 }
1100
1101 #[test]
1102 fn test_binary_id_ord() {
1103 let empty = RustBinaryId::new("");
1104 let foo = RustBinaryId::new("foo");
1105 let bar = RustBinaryId::new("bar");
1106 let foo_name1 = RustBinaryId::new("foo::name1");
1107 let foo_name2 = RustBinaryId::new("foo::name2");
1108 let bar_name = RustBinaryId::new("bar::name");
1109 let foo_bin_name1 = RustBinaryId::new("foo::bin/name1");
1110 let foo_bin_name2 = RustBinaryId::new("foo::bin/name2");
1111 let bar_bin_name = RustBinaryId::new("bar::bin/name");
1112 let foo_proc_macro_name = RustBinaryId::new("foo::proc_macro/name");
1113 let bar_proc_macro_name = RustBinaryId::new("bar::proc_macro/name");
1114
1115 let sorted_ids = [
1117 empty,
1118 bar,
1119 bar_name,
1120 bar_bin_name,
1121 bar_proc_macro_name,
1122 foo,
1123 foo_name1,
1124 foo_name2,
1125 foo_bin_name1,
1126 foo_bin_name2,
1127 foo_proc_macro_name,
1128 ];
1129
1130 for (i, id) in sorted_ids.iter().enumerate() {
1131 for (j, other_id) in sorted_ids.iter().enumerate() {
1132 let expected = i.cmp(&j);
1133 assert_eq!(
1134 id.cmp(other_id),
1135 expected,
1136 "comparing {id:?} to {other_id:?} gave {expected:?}"
1137 );
1138 }
1139 }
1140 }
1141
1142 #[test]
1144 fn mismatch_reason_all_variants_is_complete() {
1145 fn check_exhaustive(reason: MismatchReason) {
1147 match reason {
1148 MismatchReason::NotBenchmark
1149 | MismatchReason::Ignored
1150 | MismatchReason::String
1151 | MismatchReason::Expression
1152 | MismatchReason::Partition
1153 | MismatchReason::RerunAlreadyPassed
1154 | MismatchReason::DefaultFilter => {}
1155 }
1156 }
1157
1158 for &reason in MismatchReason::ALL_VARIANTS {
1159 check_exhaustive(reason);
1160 }
1161
1162 assert_eq!(MismatchReason::ALL_VARIANTS.len(), 7);
1164 }
1165}