4 <style type='text/css'>
13 font-family: Courier, monospace;
16 #directories, #codeviewer
19 background-color: white;
37 padding: 1px 0px 1px 8px;
39 list-style-type: none;
44 /* 8px is to match the width of downArrow and rightArrow because files don't have an image before them. */
45 padding: 0px 0px 0px 8px;
53 display: inline-block;
60 display: inline-block;
67 display: inline-block;
71 <script type='text/javascript'>
73 // This is the contents of the images left of directories.
74 var downArrow = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QYHFioBcCRLNAAAAJhJREFUGNOFzrEKwjAURuHTJHUspekDZGmhdlawCEqoUDK19O1cnX0dFxcnQXyIOAhKSsUzXr4Lf3Q8nf3leuNXy8IgBmeRYh5IAYOziDxLsdv1LNo3K/IsRQAcdhu0TgKgdUJnG4A3ipVk7NoAjV2LkvKLAKrSUBcGgLowVKX5PASTe2eJF4re2XCcn3R/PKcnH3nvPX96AbbuOGLOrLT1AAAAAElFTkSuQmCC';
75 var rightArrow = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QYHFiojpUQK0AAAAKZJREFUGNNjmL1k/f+379//xweYlXXMGo6dvcDw/x8jg7y0OAMzMzMDOmBiYGBg+PP7P8OO/ccYOqctZLh2+x52RTDw9t0nhtlLNjDMWbqB4d2HD9gVwcDVW/cYumcsZXj56i0DAwMDAws2RdqqCgwBHo4MIiKCmIqEBPgYgrycGbTVFVE0sTAwMDCwMjMyONqYMbjYWjCwsmL6jmH24rX/37zBH04AHAxpneY+98AAAAAASUVORK5CYII=';
77 function getHeatBackgroundColor(hits, maxHits)
80 return 'white'; // Non-code lines are white.
82 return 'orange'; // Unexecuted lines are orange.
84 // Executed lines are between red and green.
85 var relativeHeat = Math.floor(hits / maxHits * 255);
86 return 'rgb(' + relativeHeat + ',' + (255 - relativeHeat) + ', 0)';
90 function getCoverageBackgroundColor(coverage)
92 var value = Math.floor(coverage * 255);
93 return 'rgb(' + (255 - value) + ',' + value + ', 0)';
96 function expandClicked(event)
98 var children = this.parentNode.lastChild;
99 if (children.style.display === '') {
100 children.style.display = 'none';
101 this.src = rightArrow;
103 children.style.display = '';
104 this.src = downArrow;
108 function processFile(fileData, contents)
110 var lines = contents.split('\n');
111 var hits = new Array();
112 var branchesNumerator = new Array();
113 var branchesDenominator = new Array();
115 for (var i = 0; i < lines.length; i++) {
117 branchesNumerator[i] = -1;
118 branchesDenominator[i] = -1;
121 for (var i = 0; i < fileData.hitLines.length; i++)
122 hits[fileData.hitLines[i] - 1] = fileData.hits[i];
124 for (var i = 0; i < fileData.branchLines.length; i++) {
125 branchesNumerator[fileData.branchLines[i] - 1] = fileData.branchesTaken[i];
126 branchesDenominator[fileData.branchLines[i] - 1] = fileData.branchesPossible[i];
129 var table = document.createElement('table');
131 for (var i = 0; i < lines.length; i++) {
132 var row = document.createElement('tr');
134 var branchesColumn = document.createElement('td');
135 if (branchesNumerator[i] != -1)
136 branchesColumn.appendChild(document.createTextNode('(' + branchesNumerator[i] + '/' + branchesDenominator[i] + ')'));
138 var hitsColumn = document.createElement('td');
140 hitsColumn.appendChild(document.createTextNode(hits[i]));
142 var textColumn = document.createElement('td');
143 textColumn.style.background = getHeatBackgroundColor(hits[i], fileData.maxHeat);
144 textColumn.style.className = 'textColumn';
145 textColumn.appendChild(document.createTextNode(lines[i]));
147 row.appendChild(branchesColumn);
148 row.appendChild(hitsColumn);
149 row.appendChild(textColumn);
150 table.appendChild(row);
156 function fileClicked(event)
158 var xhr = new XMLHttpRequest();
159 xhr.onreadystatechange = function()
161 if (xhr.readyState === XMLHttpRequest.DONE) {
162 var codeviewer = document.getElementById('codeviewer');
163 codeviewer.replaceChild(processFile(xhr.fileData, xhr.responseText), codeviewer.firstChild);
166 xhr.fileData = event.target.fileData;
167 xhr.open('GET', '../../' + event.target.fileData.filename.substring(1), true);
169 event.stopPropagation();
172 function makeGraphs(dirOrFile)
174 var codeCoverage = document.createElement('div');
175 codeCoverage.className = 'codeCoverage';
176 var codeCoveragePercent = dirOrFile.totalLines ? Math.floor(dirOrFile.totalHitLines / dirOrFile.totalLines * 100) + '%' : '-';
177 var codeCoverageText = codeCoveragePercent + ' (' + dirOrFile.totalHitLines + '/' + dirOrFile.totalLines + ')';
178 codeCoverage.appendChild(document.createTextNode(codeCoverageText));
179 codeCoverage.style.backgroundColor = getCoverageBackgroundColor(dirOrFile.coverage);
181 var branchCoverage = document.createElement('div');
182 branchCoverage.className = 'branchCoverage';
183 var branchCoveragePercent = dirOrFile.totalBranchesPossible ? Math.floor(dirOrFile.totalBranchesTaken / dirOrFile.totalBranchesPossible * 100) + '%' : '-';
184 branchCoverage.appendChild(document.createTextNode(branchCoveragePercent + ' (' + dirOrFile.totalBranchesTaken + '/' + dirOrFile.totalBranchesPossible + ')'));
185 branchCoverage.style.backgroundColor = getCoverageBackgroundColor(dirOrFile.branchCoverage);
187 var graphsContainer = document.createElement('div');
188 graphsContainer.className = 'graphsContainer';
189 graphsContainer.appendChild(codeCoverage);
190 graphsContainer.appendChild(branchCoverage);
191 return graphsContainer;
194 function makeFileListItem(fileData, filename)
196 var li = document.createElement('li');
197 li.className = 'file';
198 var a = document.createElement('a');
199 a.appendChild(document.createTextNode(filename));
201 a.addEventListener('click', fileClicked.bind(a));
202 a.fileData = fileData;
204 li.appendChild(makeGraphs(fileData));
208 function makeDirectoryListItem(dir, dirName)
210 var li = document.createElement('li');
211 var children = document.createElement('ul');
213 // Recursively add all sorted subdirectories and files.
214 var fileNames = dir.files ? Object.keys(dir.files).sort() : [];
215 var subdirNames = dir.subdirs ? Object.keys(dir.subdirs).sort() : [];
216 for (var i = 0; i < subdirNames.length; i++) {
217 var subdir = subdirNames[i];
218 children.appendChild(makeDirectoryListItem(dir.subdirs[subdir], subdir));
220 for (var i = 0; i < fileNames.length; i++) {
221 var file = fileNames[i];
222 children.appendChild(makeFileListItem(dir.files[file], file, dir.maxHeat, dir.totalHeat));
225 var img = document.createElement('img');
226 img.addEventListener('click', expandClicked.bind(img));
228 // These four directories are expanded by default.
229 if (dirName === '' || dirName === 'Source' || dirName === 'Tools' || dirName === 'WebKitBuild') {
231 children.style.display = '';
233 img.src = rightArrow;
234 children.style.display = 'none';
238 li.appendChild(document.createTextNode(dirName));
239 li.appendChild(makeGraphs(dir));
240 li.appendChild(children);
244 // Collect total coverage for a directory and its subdirectories.
245 function collectDirectoryTotals(directory)
247 directory.totalBranchesPossible = 0;
248 directory.totalBranchesTaken = 0;
249 directory.totalHitLines = 0;
250 directory.totalLines = 0;
251 directory.totalHeat = 0;
252 directory.maxHeat = 0;
253 if (directory.subdirs) {
254 for (var subdirName in directory.subdirs) {
255 var subdir = directory.subdirs[subdirName];
257 collectDirectoryTotals(subdir);
259 directory.totalBranchesPossible += subdir.totalBranchesPossible;
260 directory.totalBranchesTaken += subdir.totalBranchesTaken;
261 directory.totalHitLines += subdir.totalHitLines;
262 directory.totalLines += subdir.totalLines;
263 directory.totalHeat += subdir.totalHeat;
264 directory.maxHeat = Math.max(directory.maxHeat, subdir.maxHeat);
267 if (directory.files) {
268 for (var fileName in directory.files) {
269 var file = directory.files[fileName];
271 file.totalBranchesPossible = 0;
272 file.totalBranchesTaken = 0;
273 file.totalHitLines = 0;
274 file.totalLines = file.hitLines.length;
277 for (var i = 0; i < file.branchesPossible.length; i++) {
278 file.totalBranchesPossible += file.branchesPossible[i];
279 file.totalBranchesTaken += file.branchesTaken[i];
281 for (var i = 0; i < file.hits.length; i++) {
282 file.totalHeat += file.hits[i];
284 file.totalHitLines++;
287 directory.totalBranchesPossible += file.totalBranchesPossible;
288 directory.totalBranchesTaken += file.totalBranchesTaken;
289 directory.totalHitLines += file.totalHitLines;
290 directory.totalLines += file.totalLines;
291 directory.totalHeat += file.totalHeat;
292 directory.maxHeat = Math.max(directory.maxHeat, file.maxHeat);
295 directory.coverage = directory.totalHitLines / directory.totalLines;
296 directory.branchCoverage = directory.totalBranchesPossible ? directory.totalBranchesTaken / directory.totalBranchesPossible : 1;
299 function addFileToDirectory(filename, filedata, directory)
301 var slashIndex = filename.indexOf('/', 1);
302 if (slashIndex === -1) {
303 if (!directory.files)
304 directory.files = {};
305 directory.files[filename.substring(1)] = filedata;
307 if (!directory.subdirs)
308 directory.subdirs = {};
309 var subdirName = filename.substring(1, slashIndex);
310 if (!directory.subdirs[subdirName])
311 directory.subdirs[subdirName] = {};
312 addFileToDirectory(filename.substring(slashIndex), filedata, directory.subdirs[subdirName]);
316 function updateReport(data)
318 var rootDirectory = {};
319 for (var i = 0; i < data.length; i++)
320 addFileToDirectory(data[i].filename, data[i], rootDirectory);
322 collectDirectoryTotals(rootDirectory);
324 var report = document.createElement('div');
325 var codeCoverageHeader = document.createElement('div');
326 codeCoverageHeader.className = 'codeCoverage';
327 codeCoverageHeader.appendChild(document.createTextNode('Code Coverage'));
328 var branchCoverageHeader = document.createElement('div');
329 branchCoverageHeader.className = 'branchCoverage';
330 branchCoverageHeader.appendChild(document.createTextNode('Branch Coverage'));
331 var ul = document.createElement('ul');
332 ul.appendChild(makeDirectoryListItem(rootDirectory, ''));
334 report.appendChild(codeCoverageHeader);
335 report.appendChild(branchCoverageHeader);
336 report.appendChild(document.createTextNode('Directories'));
337 report.appendChild(ul);
339 var directories = document.getElementById('directories');
340 directories.replaceChild(report, directories.firstChild);
343 function bodyLoaded()
345 updateReport(JSON.parse(document.getElementById('json').textContent));
350 <body onload='bodyLoaded();'>
351 <div id='directories'>
354 <div id='codeviewer'>
356 <script id='json' type='application/json'>%CoverageDataJSON%</script>