'use strict' ; import * as vscode from 'vscode' ; import * as fs from 'fs' ; import * as path from 'path' ; import { Configuration } from './configuration' ; import { ICoverageData , IRawLinesCoverageData , IRawFunctionsCoverageData , IRawBranchesCoverageData , ILoadResult , loadMany } from './loader' ; import { LOG } from './logger' ; import { UriWatcher } from './uriWatcher' ; import { ISourceMapConsumers , ISourceMapConsumer , IOriginalPosition , getSource , getSourceMapConsumers } from './sourceMapFinder' ; const log = LOG ( 'DataBank' ) ; export interface ISummary { absolutePath : string ; lines : { found : number ; hit : number ; } ; branches : { found : number ; hit : number ; } ; } interface IData { [ uri ] : ICoverageData ; } export class DataBank { private _onDidChange = new vscode . EventEmitter < void > ( ) ; public onDidChange = this . _onDidChange . event ; private _config : Configuration ; private _data : { [ uri ] : ICoverageData ; } ; private _watcher : UriWatcher ; private _onProcessingEmitter = new vscode . EventEmitter < boolean > ( ) ; public onProcessingChange = this . _onProcessingEmitter . event ; constructor ( config ) { this . _config = config ; this . _data = Object . create ( null ) ; this . _watcher = new UriWatcher ( "**/*.info" , this . _config . paths , ( ) => this . _updateData ( ) ) ; this . _updateData ( ) ; } public dispose ( ) { this . _watcher . dispose ( ) ; } public getSummary ( ) { return Object . keys ( this . _data ) . map ( ( key ) => { let entry = this . _data [ key ] ; return { absolutePath : entry . uri . fsPath , lines : { found : entry . lines . found , hit : entry . lines . hit } , branches : { found : entry . branches . found , hit : entry . branches . hit } } ; } ) ; } public get ( uri ) { return this . _data [ uri . toString ( ) ] || null ; } public isEmpty ( ) { return ( Object . keys ( this . _data ) . length === 0 ) ; } private _isProcessing = false ; get isProcessing ( ) { return this . _isProcessing ; } set isProcessing ( value ) { this . _isProcessing = value ; this . _onProcessingEmitter . fire ( value ) ; } private async _updateData ( ) { try { this . isProcessing = true ; const results = await loadMany ( this . _config . paths ) ; const accumulated = DataBank . _merge ( results ) ; let data = null ; if ( ! this . _config . sourceMaps ) { data = accumulated ; } else { const sourcemaps = await getSourceMapConsumers ( Object . keys ( accumulated ) . map ( key => accumulated [ key ] . uri ) ) ; data = processSourceMaps ( accumulated , sourcemaps ) ; } this . _data = data ; this . _onDidChange . fire ( void 0 ) ; this . isProcessing = false ; } catch ( err ) { log . error ( err ) ; this . isProcessing = false ; } } private static _merge ( results ) { let accumulated = Object . create ( null ) ; results . forEach ( ( result ) => { if ( result . data ) { result . data . forEach ( ( fileData ) => { log . debug ( 'Received coverage data for ' + fileData . uri . fsPath ) ; accumulated [ fileData . uri . toString ( ) ] = fileData ; } ) ; } } ) ; return accumulated ; } } function forEach < T extends { line : number ; } > ( arr , sourcemap , callback ) { arr . forEach ( ( item ) => { let source = getSource ( sourcemap , item . line ) ; if ( ! source || ! source . source ) { return ; } callback ( item , source ) ; } ) ; } function processSourceMaps ( data , sourcemaps ) { let collector = new CoverageCollector ( ) ; Object . keys ( data ) . forEach ( ( key ) => { let entry = data [ key ] ; let sourcemap = sourcemaps [ entry . uri . toString ( ) ] ; if ( ! sourcemap ) { log . warn ( 'No sourcemap found for ' + entry . uri . fsPath ) ; return ; } forEach ( entry . lines . details , sourcemap , ( item , source ) => { collector . addLine ( entry . uri , source . source , { line : source . line , hit : item . hit } ) ; } ) ; forEach ( entry . functions . details , sourcemap , ( item , source ) => { collector . addFunction ( entry . uri , source . source , { line : source . line , hit : item . hit , name : item . name , } ) ; } ) ; forEach ( entry . branches . details , sourcemap , ( item , source ) => { collector . addBranches ( entry . uri , source . source , { line : source . line , block : item . block , branch : item . branch , taken : item . taken } ) ; } ) ; } ) ; return collector . finalize ( ) ; } class File implements ICoverageData { uri : vscode . Uri ; title : string ; lines : IRawLinesCoverageData ; functions : IRawFunctionsCoverageData ; branches : IRawBranchesCoverageData ; constructor ( uri ) { log . debug ( 'Received mapped coverage data for ' + uri . fsPath ) ; this . uri = uri ; this . title = '' ; this . lines = { found : 0 , hit : 0 , details : [ ] } ; this . functions = { found : 0 , hit : 0 , details : [ ] } ; this . branches = { found : 0 , hit : 0 , details : [ ] } ; } addLine ( data ) { this . lines . found ++ ; if ( data . hit > 0 ) { this . lines . hit ++ ; } this . lines . details . push ( data ) ; } addFunction ( data ) { this . functions . found ++ ; if ( data . hit > 0 ) { this . functions . hit ++ ; } this . functions . details . push ( data ) ; } addBranches ( data ) { this . branches . found ++ ; if ( data . taken > 0 ) { this . branches . hit ++ ; } this . branches . details . push ( data ) ; } } class CoverageCollector { private _files : { [ uri ] : File ; } ; constructor ( ) { this . _files = Object . create ( null ) ; } public finalize ( ) { return this . _files ; } private _getOrCreate ( uri ) { let key = uri . toString ( ) ; if ( ! this . _files [ key ] ) { this . _files [ key ] = new File ( uri ) ; } return this . _files [ key ] ; } private _getOrCreateFrom ( generatedUri , source ) { let uri = vscode . Uri . file ( path . join ( path . dirname ( generatedUri . fsPath ) , source ) ) ; return this . _getOrCreate ( uri ) ; } addLine ( generatedUri , source , data ) { this . _getOrCreateFrom ( generatedUri , source ) . addLine ( data ) ; } addFunction ( generatedUri , source , data ) { this . _getOrCreateFrom ( generatedUri , source ) . addFunction ( data ) ; } addBranches ( generatedUri , source , data ) { this . _getOrCreateFrom ( generatedUri , source ) . addBranches ( data ) ; } }