1 <%-- 2 ~ Copyright (c) 2016 Google Inc. All Rights Reserved. 3 ~ 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); you 5 ~ may not use this file except in compliance with the License. You may 6 ~ obtain a copy of the License at 7 ~ 8 ~ http://www.apache.org/licenses/LICENSE-2.0 9 ~ 10 ~ Unless required by applicable law or agreed to in writing, software 11 ~ distributed under the License is distributed on an "AS IS" BASIS, 12 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 ~ implied. See the License for the specific language governing 14 ~ permissions and limitations under the License. 15 --%> 16 <%@ page contentType='text/html;charset=UTF-8' language='java' %> 17 <%@ taglib prefix='fn' uri='http://java.sun.com/jsp/jstl/functions' %> 18 <%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%> 19 20 <html> 21 <%@ include file="header.jsp" %> 22 <link rel="stylesheet" href="/css/show_coverage.css"> 23 <script src="https://apis.google.com/js/api.js" type="text/javascript"></script> 24 <body> 25 <script type="text/javascript"> 26 var coverageVectors = ${coverageVectors}; 27 $(document).ready(function() { 28 // Initialize AJAX for CORS 29 $.ajaxSetup({ 30 xhrFields : { 31 withCredentials: true 32 } 33 }); 34 35 // Initialize auth2 client and scope for requests to Gerrit 36 gapi.load('auth2', function() { 37 var auth2 = gapi.auth2.init({ 38 client_id: ${clientId}, 39 scope: ${gerritScope} 40 }); 41 auth2.then(displayEntries); 42 }); 43 }); 44 45 /* Open a window to Gerrit so that user can login. 46 Minimize the previously clicked entry. 47 */ 48 var gerritLogin = function(element) { 49 window.open(${gerritURI}, "Ratting", "toolbar=0,status=0"); 50 element.click(); 51 } 52 53 /* Loads source code for a particular entry and displays it with 54 coverage information as the accordion entry expands. 55 */ 56 var onClick = function() { 57 // Remove source code from the accordion entry that was open before 58 var self = $(this); 59 var prev = self.parent().siblings('li.active'); 60 if (prev.length > 0) { 61 prev.find('.table-container').empty(); 62 } 63 var url = self.parent().attr('url'); 64 var i = self.parent().attr('index'); 65 var container = self.parent().find('.table-container'); 66 container.html('<div class="center-align">Loading...</div>'); 67 if (self.parent().hasClass('active')) { 68 // Remove the code from display 69 container.empty(); 70 } else { 71 /* Fetch and display the code. 72 Note: a coverageVector may be shorter than sourceContents due 73 to non-executable (i.e. comments or language-specific syntax) 74 lines in the code. Trailing source lines that have no 75 coverage information are assumed to be non-executable. 76 */ 77 $.ajax({ 78 url: url, 79 dataType: 'text' 80 }).promise().done(function(src) { 81 src = atob(src); 82 if (!src) return; 83 srcLines = src.split('\n'); 84 covered = 0; 85 total = 0; 86 var table = $('<table class="table"></table>'); 87 var rows = srcLines.forEach(function(line, j) { 88 var count = coverageVectors[i][j]; 89 var row = $('<tr></tr>'); 90 if (typeof count == 'undefined' || count < 0) { 91 count = "--"; 92 } else if (count == 0) { 93 row.addClass('uncovered'); 94 total += 1; 95 } else { 96 row.addClass('covered'); 97 total += 1; 98 } 99 row.append('<td class="count">' + String(count) + '</td>'); 100 row.append('<td class="line_no">' + String(j+1) + '</td>'); 101 code = $('<td class="code"></td>'); 102 code.text(String(line)); 103 code.appendTo(row); 104 row.appendTo(table); 105 }); 106 container.empty(); 107 container.append(table); 108 }).fail(function(error) { 109 if (error.status == 0) { // origin error, refresh cookie 110 container.empty(); 111 container.html('<div class="center-align">' + 112 '<span class="login-button">' + 113 'Click to authorize Gerrit access' + 114 '</span></div>'); 115 container.find('.login-button').click(function() { 116 gerritLogin(self); 117 }); 118 } else { 119 container.html('<div class="center-align">' + 120 'Not found.</div>'); 121 } 122 }); 123 } 124 } 125 126 /* Appends a row to the display with test name and aggregated coverage 127 information. On expansion, source code is loaded with coverage 128 highlighted by calling 'onClick'. 129 */ 130 var displayEntries = function() { 131 var sourceFilenames = ${sourceFiles}; 132 var sectionMap = ${sectionMap}; 133 var gerritURI = ${gerritURI}; 134 var projects = ${projects}; 135 var commits = ${commits}; 136 var indicators = ${indicators}; 137 Object.keys(sectionMap).forEach(function(section) { 138 var indices = sectionMap[section]; 139 var html = String(); 140 indices.forEach(function(i) { 141 var url = gerritURI + '/projects/' + 142 encodeURIComponent(projects[i]) + '/commits/' + 143 encodeURIComponent(commits[i]) + '/files/' + 144 encodeURIComponent(sourceFilenames[i]) + 145 '/content'; 146 html += '<li url="' + url + '" index="' + i + '">' + 147 '<div class="collapsible-header">' + 148 '<i class="material-icons">library_books</i>' + 149 '<b>' + projects[i] + '/</b>' + 150 sourceFilenames[i] + indicators[i] + '</div>'; 151 html += '<div class="collapsible-body row">' + 152 '<div class="html-container">' + 153 '<div class="table-container"></div>' + 154 '</div></div></li>'; 155 }); 156 if (html) { 157 html = '<h4 class="section-title"><b>Coverage:</b> ' + 158 section + '</h4><ul class="collapsible popout" ' + 159 'data-collapsible="accordion">' + html + '</ul>'; 160 $('#coverage-container').append(html); 161 } 162 }); 163 $('.collapsible.popout').collapsible({ 164 accordion : true 165 }).find('.collapsible-header').click(onClick); 166 } 167 </script> 168 <div id='coverage-container' class='wide container'> 169 </div> 170 <%@ include file="footer.jsp" %> 171 </body> 172 </html> 173