test-collector 0.1.0

A library for setting up integration test with start up and tearndown
Documentation
use std::any::Any;
use std::panic;
use std::panic::AssertUnwindSafe;
use std::time::Instant;
use futures::FutureExt;
use test_collector_utils::IntegrationTestMeta;
use crate::{TestEnvironment, TestResult, TestResults};
use crate::logger::{log_error_static_info, log_error_test, log_static_info, log_test};

pub struct TestRunner<T: TestEnvironment> {
    test_environment: T,
}

impl<T: TestEnvironment> TestRunner<T> {
    pub fn new(test_environment: T) -> TestRunner<T> {
        TestRunner {
            test_environment
        }
    }

    pub fn run_safe(mut self) -> TestResults {
        log_static_info(format_args!("Next step is to start test environment"));
        let spin_up_started_at = Instant::now();
        self.test_environment = self.test_environment.start();
        let start_up_duration = spin_up_started_at.elapsed();
        log_static_info(format_args!("Test environment was started within {:?}", start_up_duration));
        log_static_info(format_args!("***"));

        log_static_info(format_args!("Next step is to run tests"));
        let tests_started_at = Instant::now();
        let (success_tests, failed_tests) = self.run_tests();
        let tests_duration = tests_started_at.elapsed();
        log_static_info(format_args!("All tests finished within {:?}", tests_duration));
        log_static_info(format_args!("***"));

        log_static_info(format_args!("Next step is to stop test environment"));
        let teardown_started_at = Instant::now();
        self.test_environment = self.test_environment.stop();
        let stop_duration = teardown_started_at.elapsed();
        log_static_info(format_args!("Test environment was stopped, within {:?}", stop_duration));
        log_static_info(format_args!("***"));

        let overall_duration = spin_up_started_at.elapsed();
        log_static_info(format_args!("Overall duration {:?}", overall_duration));

        return TestResults {
            success_tests,
            failed_tests,
            start_up_duration,
            tests_duration,
            stop_duration,
        };
    }

    pub fn run(self) {
        let result = self.run_safe();
        let success_test_number = result.success_tests.len();
        let failed_test_number =result.failed_tests.len();
        log_static_info(format_args!("Successful test {}. Failed tests {}",
                                     success_test_number,
                                     failed_test_number,
        ));
        for test in result.success_tests {
            log_static_info(format_args!("Test [{}] ....... PASSED", test.name));
        }
        for test in result.failed_tests {
            log_error_static_info(format_args!("Test [{}] ....... FAILED", test.name));
        }
        if failed_test_number > 0 {
            panic!("Some tests are Failing");
        }
    }

    fn run_tests(&self) -> (Vec<TestResult>, Vec<TestResult>) {
        let number_of_tests = inventory::iter::<IntegrationTestMeta>.into_iter().count();
        log_static_info(format_args!("Found {} tests", number_of_tests));
        let mut successful_tests: Vec<TestResult> = Vec::new();
        let mut failed_tests: Vec<TestResult> = Vec::new();
        for test in inventory::iter::<IntegrationTestMeta> {
            let result = self.run_test(test);
            if result.success {
                successful_tests.push(result);
            } else {
                failed_tests.push(result);
            }
        }
        (successful_tests, failed_tests)
    }

    fn run_test(&self, test: &IntegrationTestMeta) -> TestResult {
        let test_started = Instant::now();
        log_test(format_args!("Running Test: [{}]", test.name));
        let result = self.run_test_safe(test);
        let test_duration = test_started.elapsed();
        let success = match result {
            Ok(_) => {
                log_test(format_args!("Test [{}] PASSED. Duration {:?}", test.name, test_duration));
                true
            }
            Err(e) => {
                let error: Result<Box<&'static str>, Box<dyn Any + Send>> = e.downcast();
                if error.is_ok() {
                    let error: Box<&'static str> = error.unwrap();
                    log_error_test(format_args!("Test [{}] finished with ERROR. Duration {:?} \n {:?}",
                                                test.name, test_duration, error));
                } else {
                    log_error_test(format_args!("Test [{}] FAILED. Duration {:?}",
                                                test.name, test_duration));
                }

                false
            }
        };
        TestResult {
            name: test.name.clone(),
            success,
            duration: test_duration,
        }
    }

    fn run_test_safe(&self, test: &IntegrationTestMeta) -> Result<(), Box<dyn Any + Send>> {
        if test.sync_fn.is_some() {
            panic::catch_unwind(test.sync_fn.unwrap())
        } else {
            let async_test = (test.async_fn.as_ref().unwrap())();
            let catch_panic_wrapper = AssertUnwindSafe(async_test).catch_unwind();
            self.test_environment.block_on(catch_panic_wrapper)
        }
    }
}