UNPKG

5.9 kBJavaScriptView Raw
1/*
2 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * Cloudkick licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18/*
19 * coverage and populateCoverage are taken from expresso
20 * <https://github.com/visionmedia/expresso> which is MIT licensed.
21 * Copyright(c) TJ Holowaychuk <tj@vision-media.ca>
22 */
23
24var path = require('path');
25var fs = require('fs');
26
27/*
28 * Convert a _$jscoverage object so it's JSON serializable
29 */
30function stringifyCoverage(cov) {
31 var i, files, file, fileObj, source;
32 var tmp = {}, coverageObj = {};
33
34 files = Object.keys(cov);
35
36 for (i = 0; i < files.length; i++) {
37 file = files[i];
38 fileObj = cov[file];
39 source = fileObj['source'];
40 delete fileObj['source'];
41
42 coverageObj[file] = {
43 'lines': fileObj,
44 'source': source
45 };
46 }
47
48 return JSON.stringify(coverageObj);
49}
50
51/**
52 * Total coverage for the given file data.
53 *
54 * @param {Array} data
55 * @return {Type}
56 */
57function coverage(data, type) {
58 var comparisionFunc;
59 var n = 0;
60
61 function isCovered(val) {
62 return (val > 0);
63 }
64
65 function isMissed(val) {
66 return !isCovered(val);
67 }
68
69 if (type === 'covered') {
70 comparisionFunc = isCovered;
71 }
72 else if (type === 'missed') {
73 comparisionFunc = isMissed;
74 }
75 else {
76 throw new Error('Invalid type: ' + type);
77 }
78
79 for (var i = 0, len = data.lines.length; i < len; ++i) {
80 if (data.lines[i] !== null && comparisionFunc(data.lines[i])) {
81 ++n;
82 }
83 }
84
85 return n;
86}
87
88function getEmptyResultObject() {
89 var results = {};
90
91 results.LOC = 0;
92 results.SLOC = 0;
93 results.totalFiles = 0;
94 results.totalHits = 0;
95 results.totalMisses = 0;
96 results.coverage = 0;
97 results.files = {};
98
99 return results;
100}
101
102/**
103 * @param {?Object} results Optional results object. If it's not provided it will be created.
104 * @param {?Object} cov coverage object.
105 */
106function populateCoverage(results, cov) {
107 var linesLen;
108
109 results = (!results) ? getEmptyResultObject() : results;
110
111 /* Aggregate data from all files */
112 for (var testfile in cov) {
113 for (var name in cov[testfile]) {
114 var file = cov[testfile][name], file_stats;
115
116 file_stats = results.files[name] || {
117 name: name,
118 htmlName: name.replace('.js', '.html').replace('.java', '.html').replace(/\/|\\/g, '_'),
119 totalHits: 0,
120 totalMisses: 0,
121 totalLines: 0,
122 lines: null,
123 source: null
124 };
125
126 if (file_stats.lines === null) {
127 file_stats.lines = file.lines;
128 }
129 else {
130 linesLen = file.lines.length;
131 for (var i = 0; i < linesLen; i++) {
132 if (file.lines[i] !== null) {
133 if (file_stats.lines[i] === null) {
134 file_stats.lines[i] = file.lines[i];
135 }
136 else {
137 file_stats.lines[i] += file.lines[i];
138 }
139 }
140 }
141 }
142
143 if (file_stats.source === null) {
144 file_stats.source = file.source;
145 }
146
147 results.files[name] = file_stats;
148 }
149 }
150
151 /* Calculate statistics */
152 for (var name in results.files) {
153 var file_stats = results.files[name];
154
155 // File level statistics
156 file_stats.totalHits = coverage(file_stats, 'covered');
157 file_stats.totalMisses = coverage(file_stats, 'missed');
158 file_stats.totalLines = file_stats.totalHits + file_stats.totalMisses;
159 file_stats.coverage = (file_stats.totalHits / file_stats.totalLines) * 100;
160 file_stats.coverage = (isNaN(file_stats.coverage)) ? 0 : file_stats.coverage.toFixed(2);
161 file_stats.LOC = file_stats.source.length;
162 file_stats.SLOC = file_stats.totalLines;
163 results.files[name] = file_stats;
164
165 // Global statistic update
166 results.totalHits += file_stats.totalHits;
167 results.totalMisses += file_stats.totalMisses;
168 results.totalFiles++;
169 results.LOC += file_stats.LOC;
170 results.SLOC += file_stats.SLOC;
171 }
172
173 /* Calculate covergage of tests */
174 results.coverage = (results.totalHits / results.SLOC) * 100;
175 results.coverage = results.coverage.toFixed(2);
176
177 return results;
178}
179
180/**
181 * Read multiple coverage files and return aggregated coverage.
182 */
183function aggregateCoverage(files) {
184 var i, len, file, content, results;
185 var resultsObj = getEmptyResultObject();
186
187 for (i = 0, len = files.length; i < len; i++) {
188 file = files[i];
189 content = JSON.parse(fs.readFileSync(file).toString());
190 resultsObj = populateCoverage(resultsObj, content);
191 }
192
193 return resultsObj;
194}
195
196
197function installCoverageHandler() {
198 var pid = process.pid;
199 var coverageDirectory = process.env['COVERAGE_DIRECTORY'];
200 var coveragePath = path.join(coverageDirectory, pid + '.json');
201
202 function writeCoverage() {
203 var coverage = {};
204
205 if (typeof _$jscoverage === 'object') {
206 coverage[pid] = JSON.parse(stringifyCoverage(_$jscoverage));
207
208 try {
209 fs.writeFileSync(coveragePath, JSON.stringify(coverage), 'utf8');
210 }
211 catch (e) {}
212 }
213
214 process.exit();
215 }
216
217 if (coverageDirectory) {
218 process.on('SIGUSR2', writeCoverage);
219 }
220}
221
222exports.stringifyCoverage = stringifyCoverage;
223exports.populateCoverage = populateCoverage;
224exports.aggregateCoverage = aggregateCoverage;
225exports.installCoverageHandler = installCoverageHandler;