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 17 package com.android.dumprendertree2; 18 19 import android.os.Bundle; 20 import android.os.Handler; 21 import android.os.Message; 22 import android.webkit.WebView; 23 24 import name.fraser.neil.plaintext.diff_match_patch; 25 26 import java.util.LinkedList; 27 28 /** 29 * A result object for which the expected output is text. It does not have an image 30 * expected result. 31 * 32 * <p>Created if layoutTestController.dumpAsText() was called. 33 */ 34 public class TextResult extends AbstractResult { 35 36 private static final int MSG_DOCUMENT_AS_TEXT = 0; 37 38 private String mExpectedResult; 39 private String mExpectedResultPath; 40 private String mActualResult; 41 private String mRelativePath; 42 private boolean mDidTimeOut; 43 private ResultCode mResultCode; 44 transient private Message mResultObtainedMsg; 45 46 private boolean mDumpChildFramesAsText; 47 48 transient private Handler mHandler = new Handler() { 49 @Override 50 public void handleMessage(Message msg) { 51 if (msg.what == MSG_DOCUMENT_AS_TEXT) { 52 mActualResult = (String)msg.obj; 53 mResultObtainedMsg.sendToTarget(); 54 } 55 } 56 }; 57 58 public TextResult(String relativePath) { 59 mRelativePath = relativePath; 60 } 61 62 public void setDumpChildFramesAsText(boolean dumpChildFramesAsText) { 63 mDumpChildFramesAsText = dumpChildFramesAsText; 64 } 65 66 /** 67 * Used to recreate the Result when received by the service. 68 * 69 * @param bundle 70 * bundle with data used to recreate the result 71 */ 72 public TextResult(Bundle bundle) { 73 mExpectedResult = bundle.getString("expectedTextualResult"); 74 mExpectedResultPath = bundle.getString("expectedTextualResultPath"); 75 mActualResult = bundle.getString("actualTextualResult"); 76 setAdditionalTextOutputString(bundle.getString("additionalTextOutputString")); 77 mRelativePath = bundle.getString("relativePath"); 78 mDidTimeOut = bundle.getBoolean("didTimeOut"); 79 } 80 81 @Override 82 public void clearResults() { 83 super.clearResults(); 84 mExpectedResult = null; 85 mActualResult = null; 86 } 87 88 @Override 89 public ResultCode getResultCode() { 90 if (mResultCode == null) { 91 mResultCode = resultsMatch() ? AbstractResult.ResultCode.RESULTS_MATCH 92 : AbstractResult.ResultCode.RESULTS_DIFFER; 93 } 94 return mResultCode; 95 } 96 97 private boolean resultsMatch() { 98 assert mExpectedResult != null; 99 assert mActualResult != null; 100 // Trim leading and trailing empty lines, as other WebKit platforms do. 101 String leadingEmptyLines = "^\\n+"; 102 String trailingEmptyLines = "\\n+$"; 103 String trimmedExpectedResult = mExpectedResult.replaceFirst(leadingEmptyLines, "") 104 .replaceFirst(trailingEmptyLines, ""); 105 String trimmedActualResult = mActualResult.replaceFirst(leadingEmptyLines, "") 106 .replaceFirst(trailingEmptyLines, ""); 107 return trimmedExpectedResult.equals(trimmedActualResult); 108 } 109 110 @Override 111 public boolean didCrash() { 112 return false; 113 } 114 115 @Override 116 public boolean didTimeOut() { 117 return mDidTimeOut; 118 } 119 120 @Override 121 public void setDidTimeOut() { 122 mDidTimeOut = true; 123 } 124 125 @Override 126 public byte[] getActualImageResult() { 127 return null; 128 } 129 130 @Override 131 public String getActualTextResult() { 132 String additionalTextResultString = getAdditionalTextOutputString(); 133 if (additionalTextResultString != null) { 134 return additionalTextResultString + mActualResult; 135 } 136 137 return mActualResult; 138 } 139 140 @Override 141 public void setExpectedImageResult(byte[] expectedResult) { 142 /** This method is not applicable to this type of result */ 143 } 144 145 @Override 146 public void setExpectedImageResultPath(String relativePath) { 147 /** This method is not applicable to this type of result */ 148 } 149 150 @Override 151 public String getExpectedImageResultPath() { 152 /** This method is not applicable to this type of result */ 153 return null; 154 } 155 156 @Override 157 public void setExpectedTextResultPath(String relativePath) { 158 mExpectedResultPath = relativePath; 159 } 160 161 @Override 162 public String getExpectedTextResultPath() { 163 return mExpectedResultPath; 164 } 165 166 @Override 167 public void setExpectedTextResult(String expectedResult) { 168 // For text results, we use an empty string for the expected result when none is 169 // present, as other WebKit platforms do. 170 mExpectedResult = expectedResult == null ? "" : expectedResult; 171 } 172 173 @Override 174 public String getDiffAsHtml() { 175 assert mExpectedResult != null; 176 assert mActualResult != null; 177 178 StringBuilder html = new StringBuilder(); 179 html.append("<table class=\"visual_diff\">"); 180 html.append(" <tr class=\"headers\">"); 181 html.append(" <td colspan=\"2\">Expected result:</td>"); 182 html.append(" <td class=\"space\"></td>"); 183 html.append(" <td colspan=\"2\">Actual result:</td>"); 184 html.append(" </tr>"); 185 186 appendDiffHtml(html); 187 188 html.append(" <tr class=\"footers\">"); 189 html.append(" <td colspan=\"2\"></td>"); 190 html.append(" <td class=\"space\"></td>"); 191 html.append(" <td colspan=\"2\"></td>"); 192 html.append(" </tr>"); 193 html.append("</table>"); 194 195 return html.toString(); 196 } 197 198 private void appendDiffHtml(StringBuilder html) { 199 LinkedList<diff_match_patch.Diff> diffs = 200 new diff_match_patch().diff_main(mExpectedResult, mActualResult); 201 202 diffs = VisualDiffUtils.splitDiffsOnNewline(diffs); 203 204 LinkedList<String> expectedLines = new LinkedList<String>(); 205 LinkedList<Integer> expectedLineNums = new LinkedList<Integer>(); 206 LinkedList<String> actualLines = new LinkedList<String>(); 207 LinkedList<Integer> actualLineNums = new LinkedList<Integer>(); 208 209 VisualDiffUtils.generateExpectedResultLines(diffs, expectedLineNums, expectedLines); 210 VisualDiffUtils.generateActualResultLines(diffs, actualLineNums, actualLines); 211 // TODO: We should use a map for each line number and lines pair. 212 assert expectedLines.size() == expectedLineNums.size(); 213 assert actualLines.size() == actualLineNums.size(); 214 assert expectedLines.size() == actualLines.size(); 215 216 html.append(VisualDiffUtils.getHtml(expectedLineNums, expectedLines, 217 actualLineNums, actualLines)); 218 } 219 220 @Override 221 public TestType getType() { 222 return TestType.TEXT; 223 } 224 225 @Override 226 public void obtainActualResults(WebView webview, Message resultObtainedMsg) { 227 mResultObtainedMsg = resultObtainedMsg; 228 Message msg = mHandler.obtainMessage(MSG_DOCUMENT_AS_TEXT); 229 230 /** 231 * arg1 - should dump top frame as text 232 * arg2 - should dump child frames as text 233 */ 234 msg.arg1 = 1; 235 msg.arg2 = mDumpChildFramesAsText ? 1 : 0; 236 webview.documentAsText(msg); 237 } 238 239 @Override 240 public Bundle getBundle() { 241 Bundle bundle = new Bundle(); 242 bundle.putString("expectedTextualResult", mExpectedResult); 243 bundle.putString("expectedTextualResultPath", mExpectedResultPath); 244 bundle.putString("actualTextualResult", getActualTextResult()); 245 bundle.putString("additionalTextOutputString", getAdditionalTextOutputString()); 246 bundle.putString("relativePath", mRelativePath); 247 bundle.putBoolean("didTimeOut", mDidTimeOut); 248 bundle.putString("type", getType().name()); 249 return bundle; 250 } 251 252 @Override 253 public String getRelativePath() { 254 return mRelativePath; 255 } 256 } 257