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