clashlib/solution/
test_result.rs1pub enum CommandExit {
2 Ok,
3 Error,
4 Timeout,
5}
6
7#[derive(Debug, Clone)]
11pub enum TestResult {
12 Success,
16 UnableToRun { error_msg: String },
20 WrongOutput { stdout: String, stderr: String },
23 RuntimeError { stdout: String, stderr: String },
25 Timeout { stdout: String, stderr: String },
27}
28
29impl TestResult {
30 pub(crate) fn from_output(
31 expected: &str,
32 stdout: Vec<u8>,
33 stderr: Vec<u8>,
34 exit_status: CommandExit,
35 ) -> Self {
36 let stdout = String::from_utf8(stdout)
37 .unwrap_or_default()
38 .replace("\r\n", "\n")
39 .trim_end()
40 .to_string();
41 let stderr = String::from_utf8(stderr).unwrap_or_default();
42
43 match exit_status {
44 _ if stdout == expected.trim_end() => TestResult::Success,
45 CommandExit::Timeout => TestResult::Timeout { stdout, stderr },
46 CommandExit::Ok => TestResult::WrongOutput { stdout, stderr },
47 CommandExit::Error => TestResult::RuntimeError { stdout, stderr },
48 }
49 }
50
51 pub fn is_success(&self) -> bool {
54 matches!(self, TestResult::Success)
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn test_testresult_success() {
64 let result = TestResult::from_output("123", "123".into(), vec![], CommandExit::Ok);
65 assert!(matches!(result, TestResult::Success));
66 }
67
68 #[test]
69 fn test_testresult_success_with_trailing_whitespace() {
70 let result = TestResult::from_output("abc\n", "abc".into(), vec![], CommandExit::Ok);
71 assert!(matches!(result, TestResult::Success));
72 let result = TestResult::from_output("abc", "abc\r\n".into(), vec![], CommandExit::Ok);
73 assert!(matches!(result, TestResult::Success));
74 }
75
76 #[test]
77 fn test_testresult_success_normalized_line_endings() {
78 let result = TestResult::from_output("a\nb\nc", "a\r\nb\r\nc".into(), vec![], CommandExit::Ok);
79 assert!(matches!(result, TestResult::Success));
80 }
81
82 #[test]
83 fn test_testresult_success_on_timeout() {
84 let result = TestResult::from_output("123", "123".into(), vec![], CommandExit::Timeout);
85 assert!(
86 matches!(result, TestResult::Success),
87 "TestResult should be `Success` when stdout is correct even if execution timed out"
88 )
89 }
90
91 #[test]
92 fn test_testresult_success_on_runtime_error() {
93 let result = TestResult::from_output("123", "123".into(), vec![], CommandExit::Error);
94 assert!(
95 matches!(result, TestResult::Success),
96 "TestResult should be `Success` when stdout is correct even if a runtime error occurred"
97 )
98 }
99
100 #[test]
101 fn test_testresult_wrong_output() {
102 let result = TestResult::from_output("x\ny\nz", "yyy".into(), "zzz".into(), CommandExit::Ok);
103 match result {
104 TestResult::WrongOutput { stdout, stderr } => {
105 assert_eq!(stdout, "yyy");
106 assert_eq!(stderr, "zzz");
107 }
108 other => panic!("expected TestResult::WrongOutput but found {:?}", other),
109 }
110 }
111
112 #[test]
113 fn test_testresult_timed_out() {
114 let result = TestResult::from_output("xxx", "yyy".into(), "zzz".into(), CommandExit::Timeout);
115 match result {
116 TestResult::Timeout { stdout, stderr } => {
117 assert_eq!(stdout, "yyy");
118 assert_eq!(stderr, "zzz");
119 }
120 other => panic!("expected TestResult::Timeout but found {:?}", other),
121 }
122 }
123
124 #[test]
125 fn test_testresult_runtime_error() {
126 let result = TestResult::from_output("xxx", "yyy".into(), "zzz".into(), CommandExit::Error);
127 match result {
128 TestResult::RuntimeError { stdout, stderr } => {
129 assert_eq!(stdout, "yyy");
130 assert_eq!(stderr, "zzz");
131 }
132 other => panic!("expected TestResult::RuntimeError but found {:?}", other),
133 }
134 }
135}