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
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate vlq;
extern crate xml;
extern crate globset;
extern crate lcov_parser;
extern crate memmap;

pub mod debug;
pub mod settings;

mod lcov;
mod lines;
mod load;
mod model;
mod range;
mod source_map;
mod vlq_decode;
mod util;

use std::path::Path;

use lines::calculate_executable_line_mappings;
use lines::calculate_line_coverage;
use lines::FileCoverage;
use lines::ManyCoverage;
use model::{PuppeteerData, SourceMap};
use settings::Settings;
use source_map::*;
use std::collections::HashMap;
use std::collections::HashSet;
use std::env::args;
use std::io::Write;
use std::io;

pub fn process_source_map(settings: &Settings, data: PuppeteerData) -> Option<Vec<FileCoverage>> {
    if let Some(source_mapping_url) = data.get_source_mapping_url() {
        let source_path = data.url.replace(&settings.public_url_base, &settings.dist_path);

        let source_mapping_url = source_mapping_url.replace("//# sourceMappingURL=", "");
        let source_mapping_path = Path::new(&source_path)
            .parent()
            .unwrap()
            .join(source_mapping_url);

        println!("Processing source map {}", source_mapping_path.to_string_lossy());

        let source_mapping_path = Path::new(&source_mapping_path);
        if source_mapping_path.exists() {
            let source_map: SourceMap = util::deserialize_object(source_mapping_path).unwrap();

            let references = process_references(&settings, &source_map);

            let file_refs = references.iter().map(|s| s.file_path.clone()).collect();
            let line_refs = calculate_executable_line_mappings(&source_map, references);
            let mut file_coverage =
                calculate_line_coverage(data.ranges, line_refs, file_refs, data.text.as_str());

            if let Some(ref reify_against_lcov) = settings.reify_against_lcov {
                file_coverage = {
                    eprintln!("Reifying against LCOV file");

                    let mut file_hash_map: HashMap<_,_> = file_coverage.into_iter().map(|v| (v.path.clone(), v)).collect();

                    for line_data in lcov::LcovFilesLines::new(&util::fast_read(&reify_against_lcov).unwrap()) {

                        let our_coverage = file_hash_map.get_mut(&line_data.file_path);
                        our_coverage.map(|our_coverage| {
                            let new_lines : HashSet<_> = line_data.lines.into_iter().collect();

                            our_coverage.lines.retain(|v| new_lines.contains(&v.line_number));
                        });
                    }

                    file_hash_map.into_iter().map(|(_k,v)| v).collect()
                }
            }

            Some(file_coverage)
        } else {
            None
        }
    } else {
        None
    }
}

pub fn run<P: AsRef<Path>, W: Write>(settings: Settings, json_path: Vec<P>, writer: Option<W>) {
    let values = load::load_items(json_path);
    let processed: Vec<_> = values
        .into_iter()
        .map(|value| process_source_map(&settings, value))
        .flat_map(|value| value.into_iter())
        .flat_map(|value| value.into_iter())
        .collect();

    let many_coverage = ManyCoverage { files: processed };

    if let Some(writer) = writer {
        many_coverage.write_xml(writer);
    } else {
        let stdout = io::stdout();
        let handle = stdout.lock();

        many_coverage.write_xml(handle);
    }
}

#[cfg(test)]
mod test {
    use std::path::Path;
}