1use crate::config::Profile;
4use crate::{imap, pop3, smtp};
5use serde::{Deserialize, Serialize};
6use std::time::Instant;
7use tracing::{error, info};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10pub enum TestOutcome {
11 Pass,
12 Fail,
13 Skipped,
14}
15
16impl TestOutcome {
17 pub fn as_tag(self) -> &'static str {
18 match self {
19 TestOutcome::Pass => "[ PASS ]",
20 TestOutcome::Fail => "[ FAIL ]",
21 TestOutcome::Skipped => "[ SKIP ]",
22 }
23 }
24}
25
26#[derive(Debug, Default, Clone, Serialize, Deserialize)]
27pub struct TestResults {
28 pub smtp: Option<TestOutcome>,
29 pub imap: Option<TestOutcome>,
30 pub pop3: Option<TestOutcome>,
31 pub elapsed_ms: u128,
32}
33
34impl TestResults {
35 pub fn all_passed(&self) -> bool {
36 let xs = [self.smtp, self.imap, self.pop3];
37 let any_run = xs
38 .iter()
39 .any(|o| matches!(o, Some(TestOutcome::Pass | TestOutcome::Fail)));
40 any_run && xs.iter().all(|o| !matches!(o, Some(TestOutcome::Fail)))
41 }
42}
43
44pub fn run_tests(p: &Profile) -> TestResults {
46 let t0 = Instant::now();
47 let mut r = TestResults::default();
48
49 if p.smtp_enabled {
50 r.smtp = Some(run_one("SMTP", || smtp::run(p)));
51 } else {
52 r.smtp = Some(TestOutcome::Skipped);
53 }
54 if p.imap_enabled {
55 r.imap = Some(run_one("IMAP", || imap::run(p)));
56 } else {
57 r.imap = Some(TestOutcome::Skipped);
58 }
59 if p.pop_enabled {
60 r.pop3 = Some(run_one("POP3", || pop3::run(p)));
61 } else {
62 r.pop3 = Some(TestOutcome::Skipped);
63 }
64 r.elapsed_ms = t0.elapsed().as_millis();
65
66 info!("===== SUMMARY ({} ms) =====", r.elapsed_ms);
67 log_outcome("SMTP", r.smtp);
68 log_outcome("IMAP", r.imap);
69 log_outcome("POP3", r.pop3);
70 r
71}
72
73fn run_one<F>(name: &str, f: F) -> TestOutcome
74where
75 F: FnOnce() -> anyhow::Result<bool>,
76{
77 match f() {
78 Ok(true) => TestOutcome::Pass,
79 Ok(false) => TestOutcome::Fail,
80 Err(e) => {
81 error!("{name} aborted: {e:#}");
82 TestOutcome::Fail
83 }
84 }
85}
86
87fn log_outcome(name: &str, o: Option<TestOutcome>) {
88 match o {
89 Some(TestOutcome::Pass) => info!(" {} {name}", TestOutcome::Pass.as_tag()),
90 Some(TestOutcome::Fail) => error!(" {} {name}", TestOutcome::Fail.as_tag()),
91 Some(TestOutcome::Skipped) => info!(" {} {name}", TestOutcome::Skipped.as_tag()),
92 None => {}
93 }
94}