fixture_data/
models.rs

1// Copyright (c) The nextest Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Data models for fixture information.
5
6use iddqd::{IdOrdItem, IdOrdMap, id_upcast};
7use nextest_metadata::{BuildPlatform, FilterMatch, RustBinaryId};
8
9#[derive(Clone, Debug)]
10pub struct TestSuiteFixture {
11    pub binary_id: RustBinaryId,
12    pub binary_name: &'static str,
13    pub build_platform: BuildPlatform,
14    pub test_cases: IdOrdMap<TestCaseFixture>,
15    properties: u64,
16}
17
18impl IdOrdItem for TestSuiteFixture {
19    type Key<'a> = &'a RustBinaryId;
20    fn key(&self) -> Self::Key<'_> {
21        &self.binary_id
22    }
23    id_upcast!();
24}
25
26impl TestSuiteFixture {
27    pub fn new(
28        binary_id: &'static str,
29        binary_name: &'static str,
30        build_platform: BuildPlatform,
31        test_cases: IdOrdMap<TestCaseFixture>,
32    ) -> Self {
33        Self {
34            binary_id: binary_id.into(),
35            binary_name,
36            build_platform,
37            test_cases,
38            properties: 0,
39        }
40    }
41
42    pub fn with_property(mut self, property: TestSuiteFixtureProperty) -> Self {
43        self.properties |= property as u64;
44        self
45    }
46
47    pub fn has_property(&self, property: TestSuiteFixtureProperty) -> bool {
48        self.properties & property as u64 != 0
49    }
50
51    pub fn assert_test_cases_match(&self, other: &IdOrdMap<TestNameAndFilterMatch<'_>>) {
52        if self.test_cases.len() != other.len() {
53            panic!(
54                "test cases mismatch: expected {} test cases, found {}; \
55                 expected: {self:#?}, actual: {other:#?}",
56                self.test_cases.len(),
57                other.len(),
58            );
59        }
60
61        for name_and_filter_match in other {
62            if let Some(test_case) = self.test_cases.get(name_and_filter_match.name) {
63                if test_case.status.is_ignored() == name_and_filter_match.filter_match.is_match() {
64                    panic!(
65                        "test case status mismatch for '{}': expected {:?}, found {:?}; \
66                         expected: {self:#?}, actual: {other:#?}",
67                        name_and_filter_match.name,
68                        test_case.status,
69                        name_and_filter_match.filter_match,
70                    );
71                }
72            } else {
73                panic!(
74                    "test case '{}' not found in test suite '{}'; \
75                     expected: {self:#?}, actual: {other:#?}",
76                    name_and_filter_match.name, self.binary_name,
77                );
78            }
79        }
80    }
81}
82
83#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
84#[repr(u64)]
85pub enum TestSuiteFixtureProperty {
86    NotInDefaultSet = 1,
87}
88
89#[derive(Clone, Debug)]
90pub struct TestCaseFixture {
91    pub name: &'static str,
92    pub status: TestCaseFixtureStatus,
93    properties: u64,
94}
95
96impl IdOrdItem for TestCaseFixture {
97    type Key<'a> = &'static str;
98    fn key(&self) -> Self::Key<'_> {
99        self.name
100    }
101    id_upcast!();
102}
103
104impl TestCaseFixture {
105    pub fn new(name: &'static str, status: TestCaseFixtureStatus) -> Self {
106        Self {
107            name,
108            status,
109            properties: 0,
110        }
111    }
112
113    pub fn with_property(mut self, property: TestCaseFixtureProperty) -> Self {
114        self.properties |= property as u64;
115        self
116    }
117
118    pub fn has_property(&self, property: TestCaseFixtureProperty) -> bool {
119        self.properties & property as u64 != 0
120    }
121}
122
123#[derive(Clone, Debug)]
124pub struct TestNameAndFilterMatch<'a> {
125    pub name: &'a str,
126    pub filter_match: FilterMatch,
127}
128
129impl<'a> IdOrdItem for TestNameAndFilterMatch<'a> {
130    type Key<'k>
131        = &'a str
132    where
133        Self: 'k;
134    fn key(&self) -> Self::Key<'_> {
135        self.name
136    }
137    id_upcast!();
138}
139
140// This isn't great, but it is the easiest way to compare an IdOrdMap of
141// TestFixture with an IdOrdMap of TestNameAndFilterMatch.
142impl PartialEq<TestNameAndFilterMatch<'_>> for TestCaseFixture {
143    fn eq(&self, other: &TestNameAndFilterMatch<'_>) -> bool {
144        self.name == other.name && self.status.is_ignored() != other.filter_match.is_match()
145    }
146}
147
148#[derive(Copy, Clone, Debug, Eq, PartialEq)]
149pub enum TestCaseFixtureStatus {
150    Pass,
151    Fail,
152    Flaky { pass_attempt: usize },
153    Leak,
154    LeakFail,
155    FailLeak,
156    Segfault,
157    IgnoredPass,
158    IgnoredFail,
159}
160
161impl TestCaseFixtureStatus {
162    pub fn is_ignored(self) -> bool {
163        matches!(
164            self,
165            TestCaseFixtureStatus::IgnoredPass | TestCaseFixtureStatus::IgnoredFail
166        )
167    }
168}
169
170#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
171#[repr(u64)]
172pub enum TestCaseFixtureProperty {
173    NeedsSameCwd = 1,
174    NotInDefaultSet = 2,
175    MatchesCdylib = 4,
176    MatchesTestMultiplyTwo = 8,
177    NotInDefaultSetUnix = 16,
178}