Home | History | Annotate | Download | only in jsp
      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 type='text/css' href='/css/show_table.css' rel='stylesheet'>
     23   <script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>
     24   <script src='https://www.gstatic.com/external_hosted/moment/min/moment-with-locales.min.js'></script>
     25   <script type='text/javascript'>
     26       google.charts.load('current', {'packages':['table', 'corechart']});
     27       google.charts.setOnLoadCallback(drawGridTable);
     28       google.charts.setOnLoadCallback(activateLogLinks);
     29       google.charts.setOnLoadCallback(drawProfilingTable);
     30       google.charts.setOnLoadCallback(drawPieChart);
     31       google.charts.setOnLoadCallback(function() {
     32           $('.gradient').removeClass('gradient');
     33       });
     34 
     35       $(document).ready(function() {
     36           function verify() {
     37               var oneChecked = ($('#presubmit').prop('checked') ||
     38                                 $('#postsubmit').prop('checked'));
     39               if (!oneChecked) {
     40                   $('#refresh').addClass('disabled');
     41               } else {
     42                   $('#refresh').removeClass('disabled');
     43               }
     44           }
     45           $('#presubmit').prop('checked', ${showPresubmit} || false)
     46                          .change(verify);
     47           $('#postsubmit').prop('checked', ${showPostsubmit} || false)
     48                           .change(verify);
     49           $('#refresh').click(refresh);
     50           $('#help-icon').click(function() {
     51               $('#help-modal').openModal();
     52           });
     53           $('#input-box').keypress(function(e) {
     54               if (e.which == 13) {
     55                   refresh();
     56               }
     57           });
     58 
     59           // disable buttons on load
     60           if (!${hasNewer}) {
     61               $('#newer-button').toggleClass('disabled');
     62           }
     63           if (!${hasOlder}) {
     64               $('#older-button').toggleClass('disabled');
     65           }
     66           $('#newer-button').click(prev);
     67           $('#older-button').click(next);
     68       });
     69 
     70       // Actives the log links to display the log info modal when clicked.
     71       function activateLogLinks() {
     72           $('.info-btn').click(function(e) {
     73               showLog(${logInfoMap}[$(this).data('col')]);
     74           });
     75       }
     76 
     77       /** Displays a modal window with the specified log entries.
     78        *
     79        * @param logEntries Array of string arrays. Each entry in the outer array
     80        *                   must contain (1) name string, and (2) url string.
     81        */
     82       function showLog(logEntries) {
     83           if (!logEntries || logEntries.length == 0) return;
     84 
     85           var logList = $('<ul class="collection"></ul>');
     86           var entries = logEntries.reduce(function(acc, entry) {
     87               if (!entry || entry.length == 0) return acc;
     88               var link = '<a href="' + entry[1] + '"';
     89               link += 'class="collection-item">' + entry[0] + '</li>';
     90               return acc + link;
     91           }, '');
     92           logList.html(entries);
     93           var infoContainer = $('#info-modal>.modal-content>.info-container');
     94           infoContainer.empty();
     95           logList.appendTo(infoContainer);
     96           $('#info-modal').openModal();
     97       }
     98 
     99       // refresh the page to see the selected test types (pre-/post-submit)
    100       function refresh() {
    101           if($(this).hasClass('disabled')) return;
    102           var link = '${pageContext.request.contextPath}' +
    103               '/show_table?testName=${testName}';
    104           var presubmit = $('#presubmit').prop('checked');
    105           var postsubmit = $('#postsubmit').prop('checked');
    106           if (presubmit) {
    107               link += '&showPresubmit=';
    108           }
    109           if (postsubmit) {
    110               link += '&showPostsubmit=';
    111           }
    112           if (${unfiltered} && postsubmit && presubmit) {
    113               link += '&unfiltered=';
    114           }
    115           var searchString = $('#input-box').val();
    116           if (searchString) {
    117               link += '&search=' + encodeURIComponent(searchString);
    118           }
    119           window.open(link,'_self');
    120       }
    121 
    122       // view older data
    123       function next() {
    124           if($(this).hasClass('disabled')) return;
    125           var endTime = ${startTime};
    126           var link = '${pageContext.request.contextPath}' +
    127               '/show_table?testName=${testName}&endTime=' + endTime;
    128           if ($('#presubmit').prop('checked')) {
    129               link += '&showPresubmit=';
    130           }
    131           if ($('#postsubmit').prop('checked')) {
    132               link += '&showPostsubmit=';
    133           }
    134           if (${unfiltered}) {
    135               link += '&unfiltered=';
    136           }
    137           var searchString = '${searchString}';
    138           if (searchString) {
    139               link += '&search=' + encodeURIComponent(searchString);
    140           }
    141           window.open(link,'_self');
    142       }
    143 
    144       // view newer data
    145       function prev() {
    146           if($(this).hasClass('disabled')) return;
    147           var startTime = ${endTime};
    148           var link = '${pageContext.request.contextPath}' +
    149               '/show_table?testName=${testName}&startTime=' + startTime;
    150           if ($('#presubmit').prop('checked')) {
    151               link += '&showPresubmit=';
    152           }
    153           if ($('#postsubmit').prop('checked')) {
    154               link += '&showPostsubmit=';
    155           }
    156           if (${unfiltered}) {
    157               link += '&unfiltered=';
    158           }
    159           var searchString = '${searchString}';
    160           if (searchString) {
    161               link += '&search=' + encodeURIComponent(searchString);
    162           }
    163           window.open(link,'_self');
    164         }
    165 
    166       // table for profiling data
    167       function drawProfilingTable() {
    168 
    169       }
    170 
    171       // to draw pie chart
    172       function drawPieChart() {
    173           var topBuildResultCounts = ${topBuildResultCounts};
    174           if (topBuildResultCounts.length < 1) {
    175               return;
    176           }
    177           var resultNames = ${resultNamesJson};
    178           var rows = resultNames.map(function(res, i) {
    179               nickname = res.replace('TEST_CASE_RESULT_', '').replace('_', ' ')
    180                          .trim().toLowerCase();
    181               return [nickname, parseInt(topBuildResultCounts[i])];
    182           });
    183           rows.unshift(['Result', 'Count']);
    184 
    185           // Get CSS color definitions (or default to white)
    186           var colors = resultNames.map(function(res) {
    187               return $('.' + res).css('background-color') || 'white';
    188           });
    189 
    190           var data = google.visualization.arrayToDataTable(rows);
    191           var options = {
    192               is3D: false,
    193               colors: colors,
    194               fontName: 'Roboto',
    195               fontSize: '14px',
    196               legend: 'none',
    197               tooltip: {showColorCode: true, ignoreBounds: true},
    198               chartArea: {height: '90%'}
    199           };
    200 
    201           var chart = new google.visualization.PieChart(document.getElementById('pie-chart-div'));
    202           chart.draw(data, options);
    203       }
    204 
    205       // table for grid data
    206       function drawGridTable() {
    207           var data = new google.visualization.DataTable();
    208 
    209           // Add column headers.
    210           headerRow = ${headerRow};
    211           headerRow.forEach(function(d, i) {
    212               var classNames = 'table-header-content';
    213               if (i == 0) classNames += ' table-header-legend';
    214               data.addColumn('string', '<span class="' + classNames + '">' +
    215                              d + '</span>');
    216           });
    217 
    218           var timeGrid = ${timeGrid};
    219           var durationGrid = ${durationGrid};
    220           var summaryGrid = ${summaryGrid};
    221           var resultsGrid = ${resultsGrid};
    222 
    223           // Format time grid to a formatted date
    224           timeGrid = timeGrid.map(function(row) {
    225               return row.map(function(cell, j) {
    226                   if (j == 0) return cell;
    227                   var time = moment(cell/1000);
    228                   // If today, don't display the date
    229                   if (time.isSame(moment(), 'd')) {
    230                       return time.format('H:mm:ssZZ');
    231                   } else {
    232                       return time.format('M/D/YY H:mm:ssZZ');
    233                   }
    234               });
    235           });
    236 
    237           // Format duration grid to HH:mm:ss.SSS
    238           durationGrid = durationGrid.map(function(row) {
    239               return row.map(function(cell, j) {
    240                   if (j == 0) return cell;
    241                   return moment.utc(cell/1000).format("HH:mm:ss.SSS");
    242               });
    243           });
    244 
    245           // add rows to the data.
    246           data.addRows(timeGrid);
    247           data.addRows(durationGrid);
    248           data.addRows(summaryGrid);
    249           data.addRows(resultsGrid);
    250 
    251           var table = new google.visualization.Table(document.getElementById('grid-table-div'));
    252           var classNames = {
    253               headerRow : 'table-header',
    254               headerCell : 'table-header-cell'
    255           };
    256           var options = {
    257               showRowNumber: false,
    258               alternatingRowStyle: true,
    259               allowHtml: true,
    260               frozenColumns: 1,
    261               cssClassNames: classNames,
    262               sort: 'disable'
    263           };
    264           table.draw(data, options);
    265       }
    266   </script>
    267 
    268   <body>
    269     <div class='wide container'>
    270       <div class='row'>
    271         <div class='col s12'>
    272           <div class='card' id='filter-wrapper'>
    273             <div id='search-icon-wrapper'>
    274               <i class='material-icons' id='search-icon'>search</i>
    275             </div>
    276             <div class='input-field' id='search-wrapper'>
    277               <input value='${searchString}' type='text' id='input-box'>
    278               <label for='input-box'>Search for test results</label>
    279             </div>
    280             <div id='help-icon-wrapper'>
    281               <i class='material-icons' id='help-icon'>help</i>
    282             </div>
    283             <div id='build-type-div' class='right'>
    284               <input type='checkbox' id='presubmit' />
    285               <label for='presubmit'>Presubmit</label>
    286               <input type='checkbox' id='postsubmit' />
    287               <label for='postsubmit'>Postsubmit</label>
    288               <a id='refresh' class='btn-floating btn-medium red right waves-effect waves-light'>
    289                 <i class='medium material-icons'>cached</i>
    290               </a>
    291             </div>
    292           </div>
    293         </div>
    294         <div class='col s7'>
    295           <div class='col s12 card center-align'>
    296             <div id='legend-wrapper'>
    297               <c:forEach items='${resultNames}' var='res'>
    298                 <div class='center-align legend-entry'>
    299                   <c:set var='trimmed' value='${fn:replace(res, "TEST_CASE_RESULT_", "")}'/>
    300                   <c:set var='nickname' value='${fn:replace(trimmed, "_", " ")}'/>
    301                   <label for='${res}'>${nickname}</label>
    302                   <div id='${res}' class='${res} legend-bubble'></div>
    303                 </div>
    304               </c:forEach>
    305             </div>
    306           </div>
    307           <div id='profiling-container' class='col s12'>
    308             <c:choose>
    309               <c:when test='${empty profilingPointNames}'>
    310                 <div id='error-div' class='center-align card'><h5>${error}</h5></div>
    311               </c:when>
    312               <c:otherwise>
    313                 <ul id='profiling-body' class='collapsible' data-collapsible='accordion'>
    314                   <li>
    315                     <div class='collapsible-header'><i class='material-icons'>timeline</i>Profiling Graphs</div>
    316                     <div class='collapsible-body'>
    317                       <ul id='profiling-list' class='collection'>
    318                         <c:forEach items='${profilingPointNames}' var='pt'>
    319                           <c:set var='profPointArgs' value='testName=${testName}&profilingPoint=${pt}'/>
    320                           <c:set var='timeArgs' value='endTime=${endTime}'/>
    321                           <a href='/show_graph?${profPointArgs}&${timeArgs}'
    322                              class='collection-item profiling-point-name'>${pt}
    323                           </a>
    324                         </c:forEach>
    325                       </ul>
    326                     </div>
    327                   </li>
    328                   <li>
    329                     <a class='collapsible-link' href='/show_performance_digest?testName=${testName}'>
    330                       <div class='collapsible-header'><i class='material-icons'>toc</i>Performance Digest</div>
    331                     </a>
    332                   </li>
    333                 </ul>
    334               </c:otherwise>
    335             </c:choose>
    336           </div>
    337         </div>
    338         <div class='col s5 valign-wrapper'>
    339           <!-- pie chart -->
    340           <div id='pie-chart-wrapper' class='col s12 valign center-align card'>
    341             <h6 class='pie-chart-title'>Test Status for Device Build ID: ${topBuildId}</h6>
    342             <div id='pie-chart-div'></div>
    343           </div>
    344         </div>
    345       </div>
    346 
    347       <div class='col s12'>
    348         <div id='chart-holder' class='col s12 card'>
    349           <!-- Grid tables-->
    350           <div id='grid-table-div'></div>
    351         </div>
    352       </div>
    353       <div id='newer-wrapper' class='page-button-wrapper fixed-action-btn'>
    354         <a id='newer-button' class='btn-floating btn red waves-effect'>
    355           <i class='large material-icons'>keyboard_arrow_left</i>
    356         </a>
    357       </div>
    358       <div id='older-wrapper' class='page-button-wrapper fixed-action-btn'>
    359         <a id='older-button' class='btn-floating btn red waves-effect'>
    360           <i class='large material-icons'>keyboard_arrow_right</i>
    361         </a>
    362       </div>
    363     </div>
    364     <div id="help-modal" class="modal">
    365       <div class="modal-content">
    366         <h4>${searchHelpHeader}</h4>
    367         <p>${searchHelpBody}</p>
    368       </div>
    369       <div class="modal-footer">
    370         <a href="#!" class="modal-action modal-close waves-effect btn-flat">Close</a>
    371       </div>
    372     </div>
    373     <div id="info-modal" class="modal">
    374       <div class="modal-content">
    375         <h4>Logs</h4>
    376         <div class="info-container"></div>
    377       </div>
    378       <div class="modal-footer">
    379         <a href="#!" class="modal-action modal-close waves-effect btn-flat">Close</a>
    380       </div>
    381     </div>
    382     <%@ include file="footer.jsp" %>
    383   </body>
    384 </html>
    385