ommui_data 0.39.0

OMMUI data structures
Documentation
use crate::{error, Result};
use indexmap::IndexMap;
use log::{info, warn};
use ommui_data::filesystem::DeviceBuilder;
use ommui_data::ommui::LoadableDevice as _;
use ommui_data::samplic::LoadableDevice as _;
use ommui_data::LoadableDevice as _;
use snafu::ResultExt;
use std::path::PathBuf;
use structopt::StructOpt;
use uuid::Uuid;

#[derive(StructOpt, Debug)]
pub struct Arguments {
    #[structopt(parse(from_os_str), default_value = ".")]
    pub path: PathBuf,
    #[structopt(parse(from_os_str), long = "junit-file")]
    pub junit_file: Option<PathBuf>,
}

impl Arguments {
    pub fn run(&self, product_id: &str, company_id: &str) -> Result<()> {
        let mut results = TestResults::new();

        let name = "device::build";
        match DeviceBuilder::new(product_id, company_id)
            .base_system_path(&self.path)
            .build()
        {
            Ok(device) => {
                results.add_ok(name, name);
                let mut check = DeviceCheck {
                    results: &mut results,
                    device,
                    product_id: product_id.to_string(),
                    company_id: company_id.to_string(),
                };
                check.run_checks()?;
            }
            Err(e) => {
                results.add_failure(
                    name,
                    name,
                    &format!("{}", e),
                    &format!("{:?}", e),
                );
            }
        }

        if let Some(ref path) = self.junit_file {
            let file = std::fs::File::create(path).context(error::Io)?;
            let mut writer =
                quick_xml::Writer::new_with_indent(file, b' ', 4);
            results.build_dom().to_writer(&mut writer).unwrap();
        };

        Ok(())
    }
}

struct DeviceCheck<'a> {
    pub results: &'a mut TestResults,
    pub device: ommui_data::filesystem::Device,
    pub product_id: String,
    pub company_id: String,
}

impl DeviceCheck<'_> {
    fn run_checks(&mut self) -> Result<()> {
        self.check_load()?;

        for calibration_id in self
            .device
            .ommui()
            .load_calibration_index()
            .context(error::OmmuiData)?
        {
            self.check_calibration(&calibration_id)?;
        }

        for sampling_id in self
            .device
            .load_sampling_index()
            .context(error::OmmuiData)?
        {
            self.check_sampling(&sampling_id)?;
        }

        for profile_id in self
            .device
            .ommui()
            .load_system_profile_index()
            .context(error::OmmuiData)?
        {
            self.check_profile(profile_id)?;
        }

        Ok(())
    }

    fn check_load(&mut self) -> Result<()> {
        let name = "device::load";
        match self.device.load_device() {
            Ok(_) => {
                self.results.add_ok(name, name);
            }
            Err(e) => {
                self.results.add_failure(
                    name,
                    name,
                    &format!("{}", e),
                    &format!("{:?}", e),
                );
            }
        }
        Ok(())
    }

    fn check_calibration(&mut self, calibration_id: &str) -> Result<()> {
        let name = format!("calibration::{}", calibration_id);
        self.results.add_ok(&name, &name);
        // TODO: check more information
        Ok(())
    }

    fn check_sampling(&mut self, sampling_id: &str) -> Result<()> {
        let name = format!("sampling::{}", sampling_id);
        self.results.add_ok(&name, &name);
        // TODO: check more information
        Ok(())
    }

    fn check_profile(&mut self, profile_id: Uuid) -> Result<()> {
        let name = format!("profile::{}", profile_id);

        let profile = self
            .device
            .samplic()
            .load_system_profile(profile_id)
            .context(error::OmmuiData)?;

        let sampling = self
            .device
            .load_sampling(&profile.sampling_id)
            .context(error::OmmuiData)?;
        {
            let name = format!("{}::sampling_exists", name);
            self.results.add_ok(&name, &name);
        }
        for parameter_id in sampling.samplic.parameters.keys() {
            let name = format!(
                "{}::sampling::{}::parameter_exists::{}",
                name, profile.sampling_id, parameter_id
            );
            if profile.parameters.contains_key(parameter_id) {
                self.results.add_ok(&name, &name);
            } else {
                self.results.add_failure(
                    &name,
                    &name,
                    "Parameter does not exist",
                    &format!(
                        "Parameter {:?} of sampling {:?} \
                         is not present in the profile",
                        parameter_id, profile.sampling_id
                    ),
                );
            }
        }

        // TODO: check more information

        self.results.add_ok(&name, &name);
        Ok(())
    }
}

#[derive(Debug)]
struct TestFailure {
    failure_type: String,
    details: String,
}

#[derive(Debug)]
struct TestResult {
    pub classname: String,
    pub failure: Option<TestFailure>,
}

impl TestResult {
    fn build_ok(classname: &str) -> Self {
        TestResult {
            classname: classname.to_string(),
            failure: None,
        }
    }

    fn build_failure(classname: &str, failure: TestFailure) -> Self {
        TestResult {
            classname: classname.to_string(),
            failure: Some(failure),
        }
    }
}

#[derive(Debug)]
struct TestResults {
    pub tests: IndexMap<String, TestResult>,
}

impl TestResults {
    pub fn new() -> Self {
        TestResults {
            tests: IndexMap::new(),
        }
    }

    pub fn add_ok(&mut self, name: &str, classname: &str) {
        self.add(name, TestResult::build_ok(classname));
    }

    pub fn add_failure(
        &mut self,
        name: &str,
        classname: &str,
        failure_type: &str,
        failure_details: &str,
    ) {
        self.add(
            name,
            TestResult::build_failure(
                classname,
                TestFailure {
                    failure_type: failure_type.to_string(),
                    details: failure_details.to_string(),
                },
            ),
        );
    }

    pub fn add(&mut self, name: &str, result: TestResult) {
        match result.failure {
            None => info!("OK: {:?}", name),
            Some(ref failure) => {
                warn!("FAILED: {:?}\n{}", name, failure.details)
            }
        }
        self.tests.insert(name.to_string(), result);
    }

    pub fn build_dom(&self) -> minidom::Element {
        let mut element = minidom::Element::builder("testsuite")
            .attr("tests", self.tests.len())
            .build();
        for (name, test) in self.tests.iter() {
            let mut test_node = minidom::Element::builder("testcase")
                .attr("classname", &test.classname)
                .attr("name", name)
                .build();
            if let Some(ref details) = test.failure {
                let mut failure = minidom::Element::builder("failure")
                    .attr("type", &details.failure_type)
                    .build();
                failure.append_text_node(&details.details);
                test_node.append_child(failure);
            }
            element.append_child(test_node);
        }

        element
    }
}