1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

extern crate chrono;
extern crate ansi_term;
extern crate time;

pub mod logger {
    use chrono::prelude::Local;
    use ansi_term::Colour::{Green, Red, Cyan, Yellow, Purple};
    pub enum LogType {
        PASS,
        FAIL,
        INFO,
        WARN
    }
    pub struct Logger {
        _pass: i32,
        _fail: i32,
        _warn: i32,
        _info: i32
    }
    impl Logger {
        pub fn new () -> Logger {
            Logger {
                _pass: 0,
                _fail: 0,
                _warn: 0,
                _info: 0
            }
        }
        pub fn log (&mut self, log_type: LogType, message: String) {
            let mark: _ = match log_type {
                LogType::PASS => {
                    self._pass += 1;
                    Green.paint("PASS")
                },
                LogType::FAIL => {
                    self._fail += 1;
                    Red.paint("FAIL")
                },
                LogType::INFO => {
                    self._info += 1;
                    Cyan.paint("INFO")
                },
                LogType::WARN => {
                    self._warn += 1;
                    Yellow.paint("WARN")
                }
            };
            println!("{} - {}: {}", mark, Purple.paint(Local::now().date().to_string()), message);
        }
        pub fn pass (&mut self, message: String) {
            self.log(LogType::PASS, message);
        }
        pub fn fail (&mut self, message: String) {
            self.log(LogType::FAIL, message);
        }
        pub fn info (&mut self, message: String) {
            self.log(LogType::INFO, message);
        }
        pub fn warn (&mut self, message: String) {
            self.log(LogType::WARN, message);
        }
        pub fn get_num_pass (&self) -> i32 {
            self._pass
        }
        pub fn get_num_fail (&self) -> i32 {
            self._fail
        }
        pub fn get_num_warn (&self) -> i32 {
            self._warn
        }
        pub fn get_num_info (&self) -> i32 {
            self._info
        }
    }
}

pub mod test_case {
    use time;
    use logger::Logger;
    pub enum TestCaseStatus {
        PASSED,
        FAILED,
        SKIPPED,
        UNKNOWN
    }
    pub struct TestCase {
        pub title: &'static str,
        pub criteria: &'static str,
        pub exec: Box<Fn (&mut Logger) -> TestCaseStatus>
    }
    impl TestCase {
        pub fn new (title: &'static str, criteria: &'static str, exec: Box<Fn (&mut Logger) -> TestCaseStatus>) -> TestCase {
            TestCase {
                title: title,
                criteria: criteria,
                exec: exec
            }
        }
    }
    pub struct TestCaseResults {
        title: &'static str,
        criteria: &'static str,
        duration: i32,
        status: TestCaseStatus
    }
    pub fn run_test (test: TestCase) -> Vec<TestCaseResults> {
        println!("Test: {} ({})", test.title, test.criteria);
        let mut logger: Logger = Logger::new();
        let starting_time: i32 = time::now().tm_nsec; // TODO: get starting time
        let status: TestCaseStatus = (test.exec)(&mut logger);
        let ending_time: i32 =  time::now().tm_nsec; // TODO: get ending time
        println!("{} PASS  {} FAIL  {} WARN  {} INFO",
            logger.get_num_pass(),
            logger.get_num_fail(),
            logger.get_num_warn(),
            logger.get_num_info()
        );
        let mark: &str = match status {
            TestCaseStatus::PASSED  => "✅",
            TestCaseStatus::FAILED  => "❌",
            TestCaseStatus::SKIPPED => "❗",
            TestCaseStatus::UNKNOWN => "⁉️",
        };
        println!("{} ... {}", test.criteria, mark);
        vec![TestCaseResults {
            title: test.title,
            criteria: test.criteria,
            duration: (ending_time - starting_time) / 1000,
            status: status
        }]
    }
    pub fn run_tests (tests: Vec<TestCase>) -> Vec<TestCaseResults> {
        let mut results: Vec<TestCaseResults> = vec![];
        for test in tests {
            for item in run_test(test) {
                results.push(item);
            }
        }
        return results;
    }
    pub trait Testable {
        fn tests (self) -> Vec<TestCase>;
    }
    pub fn run_tests_from_class <T: Testable> (test_class: T) -> Vec<TestCaseResults> {
        return run_tests(test_class.tests());
    }
    pub fn run_tests_from_classes <T: Testable> () -> Vec<TestCaseResults> {
        // TODO: Implement
        return Vec::new();
    }
    pub fn statify (stats: &Vec<TestCaseResults>) -> bool {
        let (mut total_count, mut total_duration): (i32, i32) = (0, 0);
        let (mut pass, mut fail, mut skip, mut unknown): (i32, i32, i32, i32) = (0, 0, 0, 0);
        println!("\n---\n");
        for stat in stats.iter() {
            match stat.status {
                TestCaseStatus::PASSED  => pass += 1,
                TestCaseStatus::FAILED  => fail += 1,
                TestCaseStatus::SKIPPED => skip += 1,
                TestCaseStatus::UNKNOWN => unknown += 1
            }
            let formatted_message: String = match stat.status {
                TestCaseStatus::PASSED  => format!("{}", stat.criteria),
                TestCaseStatus::FAILED  => format!("{}", stat.criteria),
                TestCaseStatus::SKIPPED => format!("{}", stat.criteria),
                TestCaseStatus::UNKNOWN => format!("{}", stat.criteria)
            };
            total_count += 1;
            total_duration += stat.duration;
            println!("{} ({} Nanosecond(s)): {}", stat.title, stat.duration, formatted_message);
        }
        println!("\nRan {} Test Case(s) in {} Nanosecond(s)", total_count, total_duration);
        println!("{} Passed  {} Failed  {} Skipped  {} Unknown", pass, fail, skip, unknown);
        return (fail + unknown) > 0;
    }
}