junitify 0.1.18

Takes cargo test JSON and transform to JUnit XML
//     junitify - Takes cargo test JSON and transform to JUnit XML
//
//         The MIT License (MIT)
//
//      Copyright (c) KoresFramework (https://gitlab.com/Kores/)
//      Copyright (c) contributors
//
//      Permission is hereby granted, free of charge, to any person obtaining a copy
//      of this software and associated documentation files (the "Software"), to deal
//      in the Software without restriction, including without limitation the rights
//      to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//      copies of the Software, and to permit persons to whom the Software is
//      furnished to do so, subject to the following conditions:
//
//      The above copyright notice and this permission notice shall be included in
//      all copies or substantial portions of the Software.
//
//      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//      LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//      OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//      THE SOFTWARE.
use crate::test_parser::TestStatus;
use crate::{ParsedTestSuite, StringJoiner};
use colored::{ColoredString, Colorize};

pub(crate) fn print_report(suites: &Vec<ParsedTestSuite>) {
    for suite in suites {
        println!(
            "{}",
            format!(
                "'{}' test (executed in {}s):",
                suite.suite_name, suite.exec_time
            )
            .green()
        );

        // ---------------------- HEADER ----------------------
        let s: String = StringJoiner::new_colored(
            Some(" - ".bright_yellow()),
            Some(". ".blue().bold()),
            Some(".".blue()),
        )
        .append_colored_string(format!("tests: {}", suite.test_count).bright_yellow())
        .append_colored_string(format!("passed: {}", suite.passed).bright_green())
        .append_colored_string(format!("measured: {}", suite.measured).bright_yellow())
        .append_colored_string(format!("failures: {}", suite.failed).red())
        .append_colored_string(format!("errors: {}", suite.errors).red())
        .append_colored_string(format!("skipped: {}", suite.ignored).cyan())
        .append_colored_string(format!("filtered: {}", suite.filtered_out).cyan())
        .append_colored_string(format!("allowed fail: {}", suite.allowed_fail).cyan())
        .into();

        println!(" {}", s);

        // ---------------------- HEADER ----------------------

        let expected_run = if suite.exec_time <= 0.0f64 || suite.tests.len() == 0 {
            None
        } else {
            Some(suite.exec_time / (suite.tests.len() as f64))
        };

        for test in &suite.tests {
            let append_short_name = test.full_name != test.name;
            let s: String =
                StringJoiner::new_colored(Some("  -".bright_yellow()), Some("\n   ".white()), None)
                    .append_colored_string(if append_short_name {
                        format!("-- {} ({})", test.full_name, test.name).yellow()
                    } else {
                        format!("-- {}", test.full_name).bright_yellow()
                    })
                    .option_append_colored_string(test.module.as_ref().map(|v| {
                        format!(
                            " - Module: {}",
                            colorize_for_status(&test.status, format!("{}", v))
                        )
                        .bright_yellow()
                    }))
                    .option_append_colored_string(test.exec_time.as_ref().map(|v| {
                        format!(" - Execution Time: {}", colorize_for_run(&expected_run, *v))
                            .bright_yellow()
                    }))
                    .append_colored_string(
                        format!(
                            " - Status: {}",
                            colorize_for_status(&test.status, format!("{:?}", test.status))
                        )
                        .bright_yellow(),
                    )
                    .into();

            println!("{}", s);
            // -------------------- OUTPUT

            if let Some(ref out) = test.std_out {
                let mut s = StringJoiner::new_colored(
                    Some("    ---".white()),
                    Some("\n       ".white()),
                    None,
                );

                let lines = out.matches("\n").count();
                for line in out.lines().take(10) {
                    s = s.append_colored_string(colorize_for_status(&test.status, line.to_string()))
                }

                if lines > 10 {
                    s = s.append_colored_string(colorize_for_status(
                        &test.status,
                        format!("...... {} more lines hidden.", lines - 10),
                    ))
                }

                let s: String = s.into();
                println!("{}", s);
            }
        }
    }
}

fn colorize_for_status(status: &TestStatus, text: String) -> ColoredString {
    match status {
        TestStatus::Failed => text.bright_red(),
        TestStatus::Ok => text.bright_green(),
        TestStatus::Ignored => text.bright_green(),
    }
}

fn colorize_for_run(expected: &Option<f64>, current: f64) -> ColoredString {
    if let Some(expected) = expected {
        if current > (*expected * 1.5f64) {
            format!("{}", current).red()
        } else if current > *expected {
            format!("{}", current).bright_yellow()
        } else {
            format!("{}", current).bright_blue()
        }
    } else {
        format!("{}", current).bright_blue()
    }
}