1<%-- 2 ~ Copyright (c) 2017 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<%@ taglib prefix='fmt' uri='http://java.sun.com/jsp/jstl/fmt' %> 20 21<html> 22 <!-- <link rel='stylesheet' href='/css/dashboard_main.css'> --> 23 <%@ include file='header.jsp' %> 24 <link type='text/css' href='/css/show_test_runs_common.css' rel='stylesheet'> 25 <link type='text/css' href='/css/test_results.css' rel='stylesheet'> 26 <link rel='stylesheet' href='/css/search_header.css'> 27 <script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script> 28 <script src='https://www.gstatic.com/external_hosted/moment/min/moment-with-locales.min.js'></script> 29 <script src='js/common.js'></script> 30 <script src='js/time.js'></script> 31 <script type='text/javascript'> 32 google.charts.load('current', {'packages':['table', 'corechart']}); 33 google.charts.setOnLoadCallback(drawStatsChart); 34 google.charts.setOnLoadCallback(drawCoverageCharts); 35 36 $(document).ready(function() { 37 38 $('select').material_select(); 39 40 $(".search-icon-wrapper").click(function() { 41 $(".search-wrapper").toggle(); 42 }); 43 44 var inputIdList = ["device", "branch"]; 45 46 $("#schBtn").click(function (evt) { 47 if($(this).hasClass('disabled')) return; 48 var queryParam = "?"; 49 $.each(inputIdList, function( index, value ) { 50 var selectId = value.charAt(0).toUpperCase() + value.slice(1) 51 var result = $("#search" + selectId).val(); 52 if ( !$.isEmptyObject(result) ) { 53 queryParam += value + "=" + result.trim() + "&"; 54 } 55 }); 56 var link = '${pageContext.request.contextPath}' + '/show_coverage_overview' + queryParam; 57 window.open(link, '_self'); 58 }); 59 60 var no_data_msg = "NO DATA"; 61 62 $('#coverageModalGraph').modal({ 63 width: '75%', 64 dismissible: true, // Modal can be dismissed by clicking outside of the modal 65 opacity: .5, // Opacity of modal background 66 inDuration: 300, // Transition in duration 67 outDuration: 200, // Transition out duration 68 startingTop: '4%', // Starting top style attribute 69 endingTop: '10%', // Ending top style attribute 70 ready: function(modal, trigger) { // Callback for Modal open. Modal and trigger parameters available. 71 var testname = modal.data('testname'); 72 $('#coverageModalTitle').text("Code Coverage Chart : " + testname); 73 var query = new google.visualization.Query('show_coverage_overview?pageType=datatable&testName=' + testname); 74 // Send the query with a callback function. 75 query.send(handleQueryResponse); 76 }, 77 complete: function() { 78 $('#coverage_combo_chart_div').empty(); 79 $('#coverage_line_chart_div').empty(); 80 $('#coverage_table_chart_div').empty(); 81 82 $("div.valign-wrapper > h2.center-align:contains('" + no_data_msg + "')").each(function(index){ 83 $(this).parent().remove(); 84 }); 85 $("span.indicator.badge.blue:contains('Graph')").each(function( index ) { 86 $(this).removeClass('blue'); 87 $(this).addClass('grey'); 88 }); 89 90 $('#dataTableLoading').show("slow"); 91 } // Callback for Modal close 92 }); 93 94 // Handle the query response. 95 function handleQueryResponse(response) { 96 $('#dataTableLoading').hide("slow"); 97 if (response.isError()) { 98 alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage()); 99 return; 100 } 101 // Draw the visualization. 102 var data = response.getDataTable(); 103 if (data.getNumberOfRows() == 0) { 104 var blankData = '<div class="valign-wrapper" style="height: 90%;">' 105 + '<h2 class="center-align" style="width: 100%;">' + no_data_msg + '</h2>' 106 + '</div>'; 107 $('#coverageModalTitle').after(blankData); 108 return; 109 } 110 data.sort([{column: 0}]); 111 112 var date_formatter = new google.visualization.DateFormat({ pattern: "yyyy-MM-dd" }); 113 date_formatter.format(data, 0); 114 115 var dataView = new google.visualization.DataView(data); 116 117 // Disable coveredLine and totalLine 118 dataView.hideColumns([1,2]); 119 120 var lineOptions = { 121 title: 'Source Code Line Coverage', 122 width: '100%', 123 height: 450, 124 curveType: 'function', 125 intervals: { 'color' : 'series-color' }, 126 interval: { 127 'fill': { 128 'style': 'area', 129 'curveType': 'function', 130 'fillOpacity': 0.2 131 }, 132 'bar': { 133 'style': 'bars', 134 'barWidth': 0, 135 'lineWidth': 1, 136 'pointSize': 3, 137 'fillOpacity': 1 138 }}, 139 legend: { position: 'bottom' }, 140 tooltip: { isHtml: true }, 141 fontName: 'Roboto', 142 titleTextStyle: { 143 color: '#757575', 144 fontSize: 16, 145 bold: false 146 }, 147 pointsVisible: true, 148 vAxis:{ 149 title: 'Code Coverage Ratio (%)', 150 titleTextStyle: { 151 color: '#424242', 152 fontSize: 12, 153 italic: false 154 }, 155 textStyle: { 156 fontSize: 12, 157 color: '#757575' 158 }, 159 }, 160 hAxis: { 161 title: 'Date', 162 format: 'yyyy-MM-dd', 163 minTextSpacing: 0, 164 showTextEvery: 1, 165 slantedText: true, 166 slantedTextAngle: 45, 167 textStyle: { 168 fontSize: 12, 169 color: '#757575' 170 }, 171 titleTextStyle: { 172 color: '#424242', 173 fontSize: 12, 174 italic: false 175 } 176 }, 177 }; 178 var lineChart = new google.visualization.LineChart(document.getElementById('coverage_line_chart_div')); 179 lineChart.draw(dataView, lineOptions); 180 181 var tableOptions = { 182 title: 'Covered/Total Source Code Line Count (SLOC)', 183 width: '95%', 184 // height: 350, 185 is3D: true 186 }; 187 var tableChart = new google.visualization.Table(document.getElementById('coverage_table_chart_div')); 188 tableChart.draw(data, tableOptions); 189 } 190 191 $('.collapsible').collapsible({ 192 accordion: false, 193 onOpen: function(el) { 194 var header = $( el[0].children[0] ); 195 var body = $( el[0].children[1] ); 196 var icon = header.children('.material-icons.expand-arrow'); 197 var testName = header.data('test'); 198 var timestamp = header.data('time'); 199 var url = '/api/test_run?test=' + testName + '×tamp=' + timestamp; 200 $.get(url).done(function(data) { 201 displayTestDetails(body, data, 16); 202 }).fail(function() { 203 icon.removeClass('rotate'); 204 }).always(function() { 205 header.removeClass('disabled'); 206 }); 207 }, // Callback for Collapsible open 208 onClose: function(el) { 209 console.log(el); 210 var body = $( el[0].children[1] ); 211 body.empty(); 212 } // Callback for Collapsible close 213 }); 214 215 $( "span.indicator.badge:contains('Coverage')" ).click(function (evt) { 216 var header = $(evt.currentTarget.parentElement); 217 var testName = header.data('test'); 218 var startTime = header.data('time'); 219 var url = '/show_coverage?testName=' + testName + '&startTime=' + startTime; 220 window.location.href = url; 221 return false; 222 }); 223 224 $( "span.indicator.badge:contains('Links')" ).click(function (evt) { 225 var header = $(evt.currentTarget.parentElement); 226 var logLinks = header.data('links'); 227 showLinks($('body'), logLinks); 228 return false; 229 }); 230 231 $( "span.indicator.badge:contains('Graph')" ).click(function (evt) { 232 var header = $(evt.currentTarget.parentElement); 233 var testname = header.data('test'); 234 $('#coverageModalGraph').data("testname", testname); 235 $('#coverageModalGraph').modal('open'); 236 $(evt.target).removeClass("grey"); 237 $(evt.target).addClass("blue"); 238 return false; 239 }); 240 }); 241 242 // draw test statistics chart 243 function drawStatsChart() { 244 var testStats = ${testStats}; 245 if (testStats.length < 1) { 246 return; 247 } 248 var resultNames = ${resultNamesJson}; 249 var rows = resultNames.map(function(res, i) { 250 nickname = res.replace('TEST_CASE_RESULT_', '').replace('_', ' ') 251 .trim().toLowerCase(); 252 return [nickname, parseInt(testStats[i])]; 253 }); 254 rows.unshift(['Result', 'Count']); 255 256 // Get CSS color definitions (or default to white) 257 var colors = resultNames.map(function(res) { 258 return $('.' + res).css('background-color') || 'white'; 259 }); 260 261 var data = google.visualization.arrayToDataTable(rows); 262 var options = { 263 is3D: false, 264 colors: colors, 265 fontName: 'Roboto', 266 fontSize: '14px', 267 legend: {position: 'labeled'}, 268 tooltip: {showColorCode: true, ignoreBounds: false}, 269 chartArea: {height: '80%', width: '90%'}, 270 pieHole: 0.4 271 }; 272 273 var chart = new google.visualization.PieChart(document.getElementById('pie-chart-stats')); 274 chart.draw(data, options); 275 } 276 277 // draw the coverage pie charts 278 function drawCoverageCharts() { 279 var coveredLines = ${coveredLines}; 280 var uncoveredLines = ${uncoveredLines}; 281 var rows = [ 282 ["Result", "Count"], 283 ["Covered Lines", coveredLines], 284 ["Uncovered Lines", uncoveredLines] 285 ]; 286 287 // Get CSS color definitions (or default to white) 288 var colors = [ 289 $('.TEST_CASE_RESULT_PASS').css('background-color') || 'white', 290 $('.TEST_CASE_RESULT_FAIL').css('background-color') || 'white' 291 ] 292 293 var data = google.visualization.arrayToDataTable(rows); 294 295 var optionsRaw = { 296 is3D: false, 297 colors: colors, 298 fontName: 'Roboto', 299 fontSize: '14px', 300 pieSliceText: 'value', 301 legend: {position: 'bottom'}, 302 chartArea: {height: '80%', width: '90%'}, 303 tooltip: {showColorCode: true, ignoreBounds: false, text: 'value'}, 304 pieHole: 0.4 305 }; 306 307 var optionsNormalized = { 308 is3D: false, 309 colors: colors, 310 fontName: 'Roboto', 311 fontSize: '14px', 312 legend: {position: 'bottom'}, 313 tooltip: {showColorCode: true, ignoreBounds: false, text: 'percentage'}, 314 chartArea: {height: '80%', width: '90%'}, 315 pieHole: 0.4 316 }; 317 318 var chart = new google.visualization.PieChart(document.getElementById('pie-chart-coverage-raw')); 319 chart.draw(data, optionsRaw); 320 321 chart = new google.visualization.PieChart(document.getElementById('pie-chart-coverage-normalized')); 322 chart.draw(data, optionsNormalized); 323 } 324 325 // refresh the page to see the runs matching the specified filter 326 function refresh() { 327 var link = '${pageContext.request.contextPath}' + 328 '/show_coverage_overview?' + search.args(); 329 if (${unfiltered}) { 330 link += '&unfiltered='; 331 } 332 window.open(link,'_self'); 333 } 334 335 </script> 336 337 <body> 338 <div class='wide container'> 339 <div id="filter-bar"> 340 <div class="row card search-bar expanded"> 341 <div class="header-wrapper"> 342 <h5 class="section-header"> 343 <b>Code Coverage</b> 344 </h5> 345 <div class="search-icon-wrapper"> 346 <i class="material-icons">search</i> 347 </div> 348 </div> 349 <div class="search-wrapper" ${empty branch and empty device ? 'style="display: none"' : ''}> 350 <div class="row"> 351 <div class="col s9"> 352 <div class="input-field col s4"> 353 <c:set var="branchVal" value='${fn:replace(branch, "\\\"", "")}'></c:set> 354 <select id="searchBranch"> 355 <option value="" <c:if test="${empty branch}">disabled selected</c:if> >Choose your branch</option> 356 <c:forEach items='${branchOptions}' var='branchOption'> 357 <option value="${branchOption}" ${branchVal == branchOption ? 'selected' : ''}>${branchOption}</option> 358 </c:forEach> 359 </select> 360 <label>Branch Select</label> 361 </div> 362 <div class="input-field col s4"> 363 <c:set var="deviceVal" value='${fn:replace(device, "\\\"", "")}'></c:set> 364 <select id="searchDevice"> 365 <option value="" <c:if test="${empty device}">disabled selected</c:if> >Choose your device</option> 366 <c:forEach items='${deviceOptions}' var='deviceOption'> 367 <option value="${deviceOption}" ${deviceVal == deviceOption ? 'selected' : ''}>${deviceOption}</option> 368 </c:forEach> 369 </select> 370 <label>Device Select</label> 371 </div> 372 <div class="col s4"></div> 373 </div> 374 <div class="refresh-wrapper col s3"> 375 <a id="schBtn" class="btn-floating btn-medium red waves-effect waves-light" style="margin-right: 30px;"> 376 <i class="medium material-icons">cached</i> 377 </a> 378 </div> 379 </div> 380 </div> 381 </div> 382 </div> 383 <div class='row'> 384 <div class='col s12'> 385 <div class='col s12 card center-align'> 386 <div id='legend-wrapper'> 387 <c:forEach items='${resultNames}' var='res'> 388 <div class='center-align legend-entry'> 389 <c:set var='trimmed' value='${fn:replace(res, "TEST_CASE_RESULT_", "")}'/> 390 <c:set var='nickname' value='${fn:replace(trimmed, "_", " ")}'/> 391 <label for='${res}'>${nickname}</label> 392 <div id='${res}' class='${res} legend-bubble'></div> 393 </div> 394 </c:forEach> 395 </div> 396 </div> 397 </div> 398 <div class='col s4 valign-wrapper'> 399 <!-- pie chart --> 400 <div class='pie-chart-wrapper col s12 valign center-align card'> 401 <h6 class='pie-chart-title'>Test Statistics</h6> 402 <div id='pie-chart-stats' class='pie-chart-div'></div> 403 </div> 404 </div> 405 <div class='col s4 valign-wrapper'> 406 <!-- pie chart --> 407 <div class='pie-chart-wrapper col s12 valign center-align card'> 408 <h6 class='pie-chart-title'>Line Coverage (Raw)</h6> 409 <div id='pie-chart-coverage-raw' class='pie-chart-div'></div> 410 </div> 411 </div> 412 <div class='col s4 valign-wrapper'> 413 <!-- pie chart --> 414 <div class='pie-chart-wrapper col s12 valign center-align card'> 415 <h6 class='pie-chart-title'>Line Coverage (Normalized)</h6> 416 <div id='pie-chart-coverage-normalized' class='pie-chart-div'></div> 417 </div> 418 </div> 419 </div> 420 <div class='col s12' id='test-results-container'> 421 <ul class="collapsible popout test-runs" data-collapsible="expandable"> 422 <c:forEach var="testRunEntity" items="${testRunEntityList}" varStatus="loop"> 423 <li class="test-run-container"> 424 <div data-test="<c:out value="${testRunEntity.testName}" />" data-time="<c:out value="${testRunEntity.startTimestamp}" />" data-links='${testRunEntity.jsonLogLinks}' class="collapsible-header test-run"> 425 <span class="test-run-metadata"> 426 <span class="test-run-label"> 427 <c:out value="${testRunEntity.testName}" /> 428 </span> 429 <br /> 430 <b>VTS Build: </b><c:out value="${testRunEntity.testBuildId}" /> 431 <br /> 432 <b>Host: </b><c:out value="${testRunEntity.hostName}" /> 433 <br /> 434 <c:out value="${testRunEntity.startDateTime}" /> - <c:out value="${testRunEntity.endDateTime}" />+0900 (<c:out value="${(testRunEntity.endTimestamp - testRunEntity.startTimestamp) / 1000}" />s) 435 </span> 436 <span class="indicator badge green" style="color: white;"> 437 <c:out value="${testRunEntity.passCount}" />/<c:out value="${testRunEntity.passCount + testRunEntity.passCount}" /> 438 </span> 439 440 <c:set var="coveredLineCnt" value="${codeCoverageEntityMap[testRunEntity.id].coveredLineCount}" /> 441 <c:set var="totalLineCnt" value="${codeCoverageEntityMap[testRunEntity.id].totalLineCount}" /> 442 <c:set var="covPct" 443 value="${(coveredLineCnt / totalLineCnt * 1000) / 10}"/> 444 445 <c:choose> 446 <c:when test = "${covPct <= 20}"> 447 <c:set var="badgeColor" value="red" /> 448 </c:when> 449 <c:when test = "${covPct >= 70}"> 450 <c:set var="badgeColor" value="green" /> 451 </c:when> 452 <c:otherwise> 453 <c:set var="badgeColor" value="orange" /> 454 </c:otherwise> 455 </c:choose> 456 457 <span class="indicator badge padded hoverable waves-effect <c:out value="${badgeColor}" />" style="color: white; margin-left: 1px;"> 458 Coverage: <c:out value="${coveredLineCnt}" />/<c:out value="${totalLineCnt}" /> 459 (<fmt:formatNumber value="${(coveredLineCnt / totalLineCnt * 1000) / 10}" type="number" pattern="#.##"/>%) 460 </span> 461 <span class="indicator badge padded hoverable waves-effect grey lighten-1" style="color: white; margin-left: 1px;">Links</span> 462 <span class="indicator badge padded hoverable waves-effect grey lighten-1" style="color: white; margin-left: 1px;">Graph</span> 463 <i class="material-icons expand-arrow">expand_more</i> 464 </div> 465 <div class="collapsible-body test-results row"></div> 466 </li> 467 </c:forEach> 468 </ul> 469 470 </div> 471 </div> 472 473 <!-- Coverage Graph Modal Structure --> 474 <div id="coverageModalGraph" class="modal modal-fixed-footer" style="width: 75%;"> 475 <div class="modal-content"> 476 <h4 id="coverageModalTitle">Code Coverage Chart</h4> 477 478 <div class="preloader-wrapper big active loaders"> 479 <div id="dataTableLoading" class="spinner-layer spinner-blue-only"> 480 <div class="circle-clipper left"> 481 <div class="circle"></div> 482 </div> 483 <div class="gap-patch"> 484 <div class="circle"></div> 485 </div> 486 <div class="circle-clipper right"> 487 <div class="circle"></div> 488 </div> 489 </div> 490 </div> 491 492 <!--Div that will hold the visualization graph --> 493 <div id="coverage_line_chart_div"></div> 494 <p></p> 495 <p></p> 496 <div id="coverage_table_chart_div" class="center-align"></div> 497 </div> 498 <div class="modal-footer"> 499 <a href="#!" class="modal-action modal-close waves-effect waves-green btn-flat ">Close</a> 500 </div> 501 </div> 502 503 <%@ include file="footer.jsp" %> 504 </body> 505</html> 506