2121const fs = require ( 'fs' ) ,
2222 xml2js = require ( 'xml2js' ) ,
2323 crypto = require ( 'crypto' ) ,
24- assert = require ( 'assert' ) . strict ,
2524 parser = new xml2js . Parser ( ) ,
2625 path = require ( 'path' ) ;
27- var timestamp , cb ;
26+ var timestamp ;
2827
2928var token = process . env . COVERALLS_TOKEN ;
3029
@@ -33,14 +32,14 @@ var token = process.env.COVERALLS_TOKEN;
3332 * Loads file containing source code, returns a hash and line count
3433 * @param {String } path - Path to the source code file.
3534 * @returns {Object } key `Hash` contains MD5 digest string of file; `count` contains number of lines in source file
36- * @todo Make asynchronous
3735 */
3836function md5 ( path ) {
39- var hash = crypto . createHash ( 'md5' ) ; // Creating hash object
40- var buf = fs . readFileSync ( path , 'utf-8' ) ; // Read in file
41- var count = buf . split ( / \r \n | \r | \n / ) . length ; // Count the number of lines
42- hash . update ( buf , 'utf-8' ) ; // Update hash
43- return { hash : hash . digest ( 'hex' ) , count : count } ;
37+ const hash = crypto . createHash ( 'md5' ) ; // Creating hash object
38+ const buf = fs . readFileSync ( path , 'utf-8' ) ; // Read in file
39+ const count = buf . split ( / \r \n | \r | \n / ) . length ; // Count the number of lines
40+ hash . update ( buf , 'utf-8' ) ; // Update hash
41+
42+ return { hash : hash . digest ( 'hex' ) , count : count } ;
4443}
4544
4645
@@ -50,42 +49,41 @@ function md5(path) {
5049 * @param {Array } classList - An array of class objects from the loaded XML file.
5150 * @param {String } srcPath - The root path of the code repository.
5251 * @param {String } sha - The commit SHA for this coverage test.
53- * @param {function } callback - The callback function to run when complete. Takes object containing array of source
54- * code files and their code coverage
5552 * @returns {Object }
5653 * @todo Generalize path default
57- * @fixme Doesn't work with python's coverage
5854 */
59- function formatCoverage ( classList , srcPath , sha ) {
60- var job = { } ;
61- var sourceFiles = [ ] ;
62- var digest ;
63- srcPath = typeof srcPath != "undefined" ? srcPath : process . env . HOMEPATH ; // default to home dir
64- // For each class, create file object containing array of lines covered and add to sourceFile array
65- classList . forEach ( async c => {
66- let file = { } ; // Initialize file object
67- let fullPath = c . $ . filename . startsWith ( srcPath ) ? c . $ . filename : path . join ( srcPath , c . $ . filename ) ;
68- digest = md5 ( fullPath ) ; // Create digest and line count for file
69- let lines = new Array ( digest . count ) . fill ( null ) ; // Initialize line array the size of source code file
70- c . lines [ 0 ] . line . forEach ( ln => {
71- let n = Number ( ln . $ . number ) ;
72- if ( n <= digest . count ) { lines [ n ] = Number ( ln . $ . hits ) }
73- } ) ;
74- // create source file object
75- file . name = c . $ . filename ;
76- file . source_digest = digest . hash ;
77- file . coverage = lines ; // file.coverage[0] == line 1
78- sourceFiles . push ( file ) ;
79- } ) ;
55+ async function formatCoverage ( classList , srcPath , sha ) {
56+ var job = { } ;
57+ var sourceFiles = [ ] ;
58+ var digest ;
59+ srcPath = typeof srcPath != 'undefined' ? srcPath : process . env . REPO_PATH ; // default to home dir
60+ // For each class, create file object containing array of lines covered and add to sourceFile array
61+ await Promise . all ( classList . map ( async c => {
62+ let file = { } ; // Initialize file object
63+ let fullPath = c . $ . filename . startsWith ( srcPath ) ? c . $ . filename : path . join ( srcPath , c . $ . filename ) ;
64+ digest = md5 ( fullPath ) ; // Create digest and line count for file
65+ let lines = new Array ( digest . count ) . fill ( null ) ; // Initialize line array the size of source code file
66+ c . lines [ 0 ] . line . forEach ( ln => {
67+ let n = Number ( ln . $ . number ) ;
68+ if ( n <= digest . count ) {
69+ lines [ n ] = Number ( ln . $ . hits ) ;
70+ }
71+ } ) ;
72+ // create source file object
73+ file . name = c . $ . filename ;
74+ file . source_digest = digest . hash ;
75+ file . coverage = lines ; // file.coverage[0] == line 1
76+ sourceFiles . push ( file ) ;
77+ } ) ) ;
8078
81- job . repo_token = token ; // env secret token?
82- job . service_name = `coverage/${ process . env . USERDOMAIN } ` ;
83- // The associated pull request ID of the build. Used for updating the status and/or commenting.
84- job . service_pull_request = '' ;
85- job . source_files = sourceFiles ;
86- job . commit_sha = sha ;
87- job . run_at = timestamp ; // "2013-02-18 00:52:48 -0800"
88- cb ( job ) ;
79+ job . repo_token = token ; // env secret token
80+ job . service_name = `coverage/${ process . env . USERDOMAIN } ` ;
81+ // The associated pull request ID of the build. Used for updating the status and/or commenting.
82+ job . service_pull_request = '' ;
83+ job . source_files = sourceFiles ;
84+ job . commit_sha = sha ;
85+ job . run_at = timestamp ; // "2013-02-18 00:52:48 -0800"
86+ return job ;
8987}
9088
9189/**
@@ -95,44 +93,42 @@ function formatCoverage(classList, srcPath, sha) {
9593 * @param {String } sha - The commit SHA for this coverage test
9694 * @param {String } repo - The repo to which the commit belongs
9795 * @param {Array } submodules - A list of submodules for separating coverage into
98- * @param {function } callback - The callback function to run when complete
9996 * @see {@link https://github.com/cobertura/cobertura/wiki|Cobertura Wiki }
10097 */
101- function coverage ( path , repo , sha , submodules , callback ) {
102- cb = callback ; // @fixme Making callback global feels hacky
103- fs . readFile ( path , function ( err , data ) { // Read in XML file
104- if ( err ) { throw err } // @fixme deal with file not found errors
105- parser . parseString ( data , function ( err , result ) { // Parse XML
106- // Extract root code path
107- const rootPath = ( result . coverage . sources [ 0 ] . source [ 0 ] || process . env . REPO_PATH ) . replace ( / [ \/ | \\ ] + $ / , '' )
108- assert ( rootPath . endsWith ( process . env . REPO_NAME ) , 'Incorrect source code repository' )
109- timestamp = new Date ( result . coverage . $ . timestamp * 1000 ) ; // Convert UNIX timestamp to Date object
110- let classes = [ ] ; // Initialize classes array
98+ function coverage ( path , repo , sha , submodules ) {
99+ return fs . promises . readFile ( path ) // Read in XML file
100+ . then ( parser . parseStringPromise ) // Parse XML
101+ . then ( result => {
102+ // Extract root code path
103+ const rootPath = ( result . coverage . sources [ 0 ] . source [ 0 ] || process . env . REPO_PATH )
104+ . replace ( / [ \/ | \\ ] + $ / , '' ) ;
105+ timestamp = new Date ( result . coverage . $ . timestamp * 1000 ) ; // Convert UNIX timestamp to Date object
106+ let classes = [ ] ; // Initialize classes array
111107
112- const packages = result . coverage . packages [ 0 ] . package ;
113- packages . forEach ( pkg => { classes . push ( pkg . classes [ 0 ] . class ) } ) ; // Get all classes
114- classes = classes . reduce ( ( acc , val ) => acc . concat ( val ) , [ ] ) ; // Flatten
108+ const packages = result . coverage . packages [ 0 ] . package ;
109+ packages . forEach ( pkg => { classes . push ( pkg . classes [ 0 ] . class ) ; } ) ; // Get all classes
110+ classes = classes . reduce ( ( acc , val ) => acc . concat ( val ) , [ ] ) ; // Flatten
115111
116- // The submodules
117- const byModule = { 'main' : [ ] } ;
118- submodules . forEach ( ( x ) => { byModule [ x ] = [ ] ; } ) ; // initialize submodules
112+ // The submodules
113+ const byModule = { 'main' : [ ] } ;
114+ submodules . forEach ( ( x ) => { byModule [ x ] = [ ] ; } ) ; // initialize submodules
119115
120- // Sort into piles
121- byModule [ 'main' ] = classes . filter ( function ( e ) {
122- if ( e . $ . filename . search ( / ( t e s t s \\ | _ .* t e s t | d o c s \\ ) / i) !== - 1 ) { return false ; } // Filter out tests and docs
123- if ( ! Array . isArray ( e . lines [ 0 ] . line ) ) { return false ; } // Filter out files with no functional lines
124- for ( let submodule of submodules ) {
125- if ( e . $ . filename . startsWith ( submodule ) ) {
126- byModule [ submodule ] . push ( e ) ; return false ;
127- }
128- }
129- return true ;
116+ // Sort into piles
117+ byModule [ 'main' ] = classes . filter ( function ( e ) {
118+ if ( e . $ . filename . search ( / ( t e s t s \\ | _ .* t e s t | d o c s \\ ) / i) !== - 1 ) return false ; // Filter out tests and docs
119+ if ( ! Array . isArray ( e . lines [ 0 ] . line ) ) return false ; // Filter out files with no functional lines
120+ for ( let submodule of submodules ) {
121+ if ( e . $ . filename . startsWith ( submodule ) ) {
122+ byModule [ submodule ] . push ( e ) ;
123+ return false ;
124+ }
125+ }
126+ return true ;
127+ } ) ;
128+ // Select module
129+ let modules = byModule [ repo ] || byModule [ 'main' ] ;
130+ return formatCoverage ( modules , rootPath , sha ) ;
130131 } ) ;
131- // Select module
132- let modules = byModule [ repo ] || byModule [ 'main' ] ;
133- formatCoverage ( modules , rootPath , callback ) ;
134- } ) ;
135- } ) ;
136132}
137133
138134
0 commit comments