Summarizer.java revision 4a9e3a6dc3bfbc885730cfec7b2f1c147d179cc1
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may 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 implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dumprendertree2; 18 19import java.io.File; 20import java.text.SimpleDateFormat; 21import java.util.ArrayList; 22import java.util.Collections; 23import java.util.Date; 24import java.util.HashSet; 25import java.util.List; 26import java.util.Set; 27 28/** 29 * A class that collects information about tests that ran and can create HTML 30 * files with summaries and easy navigation. 31 */ 32public class Summarizer { 33 34 private static final String LOG_TAG = "Summarizer"; 35 36 private static final String CSS = 37 "<style type=\"text/css\">" + 38 "* {" + 39 " font-family: Verdana;" + 40 " border: 0;" + 41 " margin: 0;" + 42 " padding: 0;}" + 43 "body {" + 44 " margin: 10px;}" + 45 "h1 {" + 46 " font-size: 24px;" + 47 " margin: 4px 0 4px 0;}" + 48 "h2 {" + 49 " font-size:18px;" + 50 " text-transform: uppercase;" + 51 " margin: 20px 0 3px 0;}" + 52 "h3, h3 a {" + 53 " font-size: 14px;" + 54 " color: black;" + 55 " text-decoration: none;" + 56 " margin-bottom: 4px;}" + 57 "h3 a span.path {" + 58 " text-decoration: underline;}" + 59 "h3 span.tri {" + 60 " text-decoration: none;" + 61 " float: left;" + 62 " width: 20px;}" + 63 "h3 span.sqr {" + 64 " text-decoration: none;" + 65 " color: #8ee100;" + 66 " float: left;" + 67 " width: 20px;}" + 68 "h3 img {" + 69 " width: 8px;" + 70 " margin-right: 4px;}" + 71 "div.diff {" + 72 " margin-bottom: 25px;}" + 73 "div.diff a {" + 74 " font-size: 12px;" + 75 " color: #888;}" + 76 "table.visual_diff {" + 77 " border-bottom: 0px solid;" + 78 " border-collapse: collapse;" + 79 " width: 100%;" + 80 " margin-bottom: 2px;}" + 81 "table.visual_diff tr.headers td {" + 82 " border-bottom: 1px solid;" + 83 " border-top: 0;" + 84 " padding-bottom: 3px;}" + 85 "table.visual_diff tr.results td {" + 86 " border-top: 1px dashed;" + 87 " border-right: 1px solid;" + 88 " font-size: 15px;" + 89 " vertical-align: top;}" + 90 "table.visual_diff tr.results td.line_count {" + 91 " background-color:#aaa;" + 92 " min-width:20px;" + 93 " text-align: right;" + 94 " border-right: 1px solid;" + 95 " border-left: 1px solid;" + 96 " padding: 2px 1px 2px 0px;}" + 97 "table.visual_diff tr.results td.line {" + 98 " padding: 2px 0px 2px 4px;" + 99 " border-right: 1px solid;" + 100 " width: 49.8%;}" + 101 "table.visual_diff tr.footers td {" + 102 " border-top: 1px solid;" + 103 " border-bottom: 0;}" + 104 "table.visual_diff tr td.space {" + 105 " border: 0;" + 106 " width: 0.4%}" + 107 "div.space {" + 108 " margin-top:4px;}" + 109 "span.eql {" + 110 " background-color: #f3f3f3;}" + 111 "span.del {" + 112 " background-color: #ff8888; }" + 113 "span.ins {" + 114 " background-color: #88ff88; }" + 115 "span.fail {" + 116 " color: red;}" + 117 "span.pass {" + 118 " color: green;}" + 119 "span.time_out {" + 120 " color: orange;}" + 121 "table.summary {" + 122 " border: 1px solid black;" + 123 " margin-top: 20px;}" + 124 "table.summary td {" + 125 " padding: 3px;}" + 126 "span.listItem {" + 127 " font-size: 11px;" + 128 " font-weight: normal;" + 129 " text-transform: uppercase;" + 130 " padding: 3px;" + 131 " -webkit-border-radius: 4px;}" + 132 "span." + AbstractResult.ResultCode.PASS.name() + "{" + 133 " background-color: #8ee100;" + 134 " color: black;}" + 135 "span." + AbstractResult.ResultCode.FAIL_RESULT_DIFFERS.name() + "{" + 136 " background-color: #ccc;" + 137 " color: black;}" + 138 "span." + AbstractResult.ResultCode.FAIL_NO_EXPECTED_RESULT.name() + "{" + 139 " background-color: #a700e4;" + 140 " color: #fff;}" + 141 "span." + AbstractResult.ResultCode.FAIL_TIMED_OUT.name() + "{" + 142 " background-color: #f3cb00;" + 143 " color: black;}" + 144 "span." + AbstractResult.ResultCode.FAIL_CRASHED.name() + "{" + 145 " background-color: #c30000;" + 146 " color: #fff;}" + 147 "span.noLtc {" + 148 " background-color: #944000;" + 149 " color: #fff;" + 150 "</style>"; 151 152 private static final String SCRIPT = 153 "<script type=\"text/javascript\">" + 154 " function toggleDisplay(id) {" + 155 " element = document.getElementById(id);" + 156 " triangle = document.getElementById('tri.' + id);" + 157 " if (element.style.display == 'none') {" + 158 " element.style.display = 'inline';" + 159 " triangle.innerHTML = '▼ ';" + 160 " } else {" + 161 " element.style.display = 'none';" + 162 " triangle.innerHTML = '▶ ';" + 163 " }" + 164 " }" + 165 "</script>"; 166 167 /** TODO: Make it a setting */ 168 private static final String HTML_SUMMARY_RELATIVE_PATH = "summary.html"; 169 170 private int mCrashedTestsCount = 0; 171 private List<AbstractResult> mFailedNotIgnoredTests = new ArrayList<AbstractResult>(); 172 private List<AbstractResult> mIgnoredTests = new ArrayList<AbstractResult>(); 173 private List<String> mPassedNotIgnoredTests = new ArrayList<String>(); 174 175 private FileFilter mFileFilter; 176 private String mResultsRootDirPath; 177 178 public Summarizer(FileFilter fileFilter, String resultsRootDirPath) { 179 mFileFilter = fileFilter; 180 mResultsRootDirPath = resultsRootDirPath; 181 } 182 183 public void appendTest(AbstractResult result) { 184 String relativePath = result.getRelativePath(); 185 186 if (result.getResultCode() == AbstractResult.ResultCode.FAIL_CRASHED) { 187 mCrashedTestsCount++; 188 } 189 190 if (mFileFilter.isIgnoreRes(relativePath)) { 191 mIgnoredTests.add(result); 192 } else if (result.getResultCode() == AbstractResult.ResultCode.PASS) { 193 mPassedNotIgnoredTests.add(relativePath); 194 } else { 195 mFailedNotIgnoredTests.add(result); 196 } 197 } 198 199 public void summarize() { 200 StringBuilder html = new StringBuilder(); 201 202 html.append("<html><head>"); 203 html.append(CSS); 204 html.append(SCRIPT); 205 html.append("</head><body>"); 206 207 createTopSummaryTable(html); 208 209 createResultsListWithDiff(html, "Failed", mFailedNotIgnoredTests); 210 211 createResultsListWithDiff(html, "Ignored", mIgnoredTests); 212 213 createResultsListNoDiff(html, "Passed", mPassedNotIgnoredTests); 214 215 html.append("</body></html>"); 216 217 FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_SUMMARY_RELATIVE_PATH), 218 html.toString().getBytes(), false); 219 } 220 221 private void createTopSummaryTable(StringBuilder html) { 222 int total = mFailedNotIgnoredTests.size() + 223 mPassedNotIgnoredTests.size() + 224 mIgnoredTests.size(); 225 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); 226 html.append("<h1> - total of " + total + " tests - "); 227 html.append(dateFormat.format(new Date()) + "</h1>"); 228 229 html.append("<table class=\"summary\">"); 230 createSummaryTableRow(html, "CRASHED", mCrashedTestsCount); 231 createSummaryTableRow(html, "FAILED", mFailedNotIgnoredTests.size()); 232 createSummaryTableRow(html, "IGNORED", mIgnoredTests.size()); 233 createSummaryTableRow(html, "PASSED", mPassedNotIgnoredTests.size()); 234 html.append("</table>"); 235 } 236 237 private void createSummaryTableRow(StringBuilder html, String caption, int size) { 238 html.append("<tr>"); 239 html.append(" <td>" + caption + "</td>"); 240 html.append(" <td>" + size + "</td>"); 241 html.append("</tr>"); 242 } 243 244 private void createResultsListWithDiff(StringBuilder html, String title, 245 List<AbstractResult> resultsList) { 246 String relativePath; 247 String id = ""; 248 AbstractResult.ResultCode resultCode; 249 250 Collections.sort(resultsList); 251 html.append("<h2>" + title + " [" + resultsList.size() + "]</h2>"); 252 for (AbstractResult result : resultsList) { 253 relativePath = result.getRelativePath(); 254 resultCode = result.getResultCode(); 255 256 html.append("<h3>"); 257 258 if (resultCode == AbstractResult.ResultCode.PASS) { 259 html.append("<span class=\"sqr\">■ </span>"); 260 html.append("<span class=\"path\">" + relativePath + "</span>"); 261 } else { 262 /** 263 * Technically, two different paths could end up being the same, because 264 * ':' is a valid character in a path. However, it is probably not going 265 * to cause any problems in this case 266 */ 267 id = relativePath.replace(File.separator, ":"); 268 html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');"); 269 html.append("return false;\">"); 270 html.append("<span class=\"tri\" id=\"tri." + id + "\">▶ </span>"); 271 html.append("<span class=\"path\">" + relativePath + "</span>"); 272 html.append("</a>"); 273 } 274 275 html.append(" <span class=\"listItem " + resultCode.name() + "\">"); 276 html.append(resultCode.toString()); 277 html.append("</span>"); 278 279 /** Detect missing LTC function */ 280 String additionalTextOutputString = result.getAdditionalTextOutputString(); 281 if (additionalTextOutputString != null && 282 additionalTextOutputString.contains("com.android.dumprendertree") && 283 additionalTextOutputString.contains("LayoutTestController") && 284 additionalTextOutputString.contains("has no method")) { 285 html.append(" <span class=\"listItem noLtc\">LTC function missing</span>"); 286 } 287 288 html.append("</h3>"); 289 290 if (resultCode != AbstractResult.ResultCode.PASS) { 291 html.append("<div class=\"diff\" style=\"display: none;\" id=\"" + id + "\">"); 292 html.append(result.getDiffAsHtml()); 293 html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');"); 294 html.append("return false;\">Hide</a>"); 295 html.append("</div>"); 296 } 297 298 html.append("<div class=\"space\"></div>"); 299 } 300 } 301 302 private void createResultsListNoDiff(StringBuilder html, String title, 303 List<String> resultsList) { 304 Collections.sort(resultsList); 305 html.append("<h2>Passed [" + resultsList.size() + "]</h2>"); 306 for (String result : resultsList) { 307 html.append("<h3>"); 308 html.append("<span class=\"sqr\">■ </span>"); 309 html.append("<span class=\"path\">" + result + "</span>"); 310 html.append("</h3>"); 311 html.append("<div class=\"space\"></div>"); 312 } 313 } 314}