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 + '&timestamp=' + 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