| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library utils; | |
| 6 | |
| 7 import 'dart:io'; | |
| 8 import 'dart:convert'; | |
| 9 | |
| 10 import 'path.dart'; | |
| 11 | |
| 12 // This is the maximum time we expect stdout/stderr of subprocesses to deliver | |
| 13 // data after we've got the exitCode. | |
| 14 const Duration MAX_STDIO_DELAY = const Duration(seconds: 30); | |
| 15 | |
| 16 String MAX_STDIO_DELAY_PASSED_MESSAGE = | |
| 17 """Not waiting for stdout/stderr from subprocess anymore | |
| 18 ($MAX_STDIO_DELAY passed). Please note that this could be an indicator | |
| 19 that there is a hanging process which we were unable to kill."""; | |
| 20 | |
| 21 class DebugLogger { | |
| 22 static IOSink _sink; | |
| 23 | |
| 24 /** | |
| 25 * If [path] was null, the DebugLogger will write messages to stdout. | |
| 26 */ | |
| 27 static init(Path path, {append: false}) { | |
| 28 if (path != null) { | |
| 29 var mode = append ? FileMode.APPEND : FileMode.WRITE; | |
| 30 _sink = new File(path.toNativePath()).openWrite(mode: mode); | |
| 31 } | |
| 32 } | |
| 33 | |
| 34 static void close() { | |
| 35 if (_sink != null) { | |
| 36 _sink.close(); | |
| 37 _sink = null; | |
| 38 } | |
| 39 } | |
| 40 | |
| 41 static String _formatErrorMessage(String msg, error) { | |
| 42 if (error == null) return msg; | |
| 43 msg += ": $error"; | |
| 44 // TODO(floitsch): once the dart-executable that is bundled | |
| 45 // with the Dart sources is updated, pass a trace parameter too and do: | |
| 46 // if (trace != null) msg += "\nStackTrace: $trace"; | |
| 47 return msg; | |
| 48 } | |
| 49 static void info(String msg, [error]) { | |
| 50 msg = _formatErrorMessage(msg, error); | |
| 51 _print("$_datetime Info: $msg"); | |
| 52 } | |
| 53 | |
| 54 static void warning(String msg, [error]) { | |
| 55 msg = _formatErrorMessage(msg, error); | |
| 56 _print("$_datetime Warning: $msg"); | |
| 57 } | |
| 58 | |
| 59 static void error(String msg, [error]) { | |
| 60 msg = _formatErrorMessage(msg, error); | |
| 61 _print("$_datetime Error: $msg"); | |
| 62 } | |
| 63 | |
| 64 static void _print(String msg) { | |
| 65 if (_sink != null) { | |
| 66 _sink.writeln(msg); | |
| 67 } else { | |
| 68 print(msg); | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 static String get _datetime => "${new DateTime.now()}"; | |
| 73 } | |
| 74 | |
| 75 String prettifyJson(Object json, {int startIndentation: 0, int shiftWidth: 6}) { | |
| 76 int currentIndentation = startIndentation; | |
| 77 var buffer = new StringBuffer(); | |
| 78 | |
| 79 String indentationString() { | |
| 80 return new List.filled(currentIndentation, ' ').join(''); | |
| 81 } | |
| 82 | |
| 83 addString(String s, {bool indentation: true, bool newLine: true}) { | |
| 84 if (indentation) { | |
| 85 buffer.write(indentationString()); | |
| 86 } | |
| 87 buffer.write(s.replaceAll("\n", "\n${indentationString()}")); | |
| 88 if (newLine) buffer.write("\n"); | |
| 89 } | |
| 90 | |
| 91 prettifyJsonInternal( | |
| 92 Object obj, {bool indentation: true, bool newLine: true}) { | |
| 93 if (obj is List) { | |
| 94 addString("[", indentation: indentation); | |
| 95 currentIndentation += shiftWidth; | |
| 96 for (var item in obj) { | |
| 97 | |
| 98 prettifyJsonInternal(item, indentation: indentation, newLine: false); | |
| 99 addString(",", indentation: false); | |
| 100 } | |
| 101 currentIndentation -= shiftWidth; | |
| 102 addString("]", indentation: indentation); | |
| 103 } else if (obj is Map) { | |
| 104 addString("{", indentation: indentation); | |
| 105 currentIndentation += shiftWidth; | |
| 106 for (var key in obj.keys) { | |
| 107 addString("$key: ", indentation: indentation, newLine: false); | |
| 108 currentIndentation += shiftWidth; | |
| 109 prettifyJsonInternal(obj[key], indentation: false); | |
| 110 currentIndentation -= shiftWidth; | |
| 111 } | |
| 112 currentIndentation -= shiftWidth; | |
| 113 addString("}", indentation: indentation, newLine: newLine); | |
| 114 } else { | |
| 115 addString("$obj", indentation: indentation, newLine: newLine); | |
| 116 } | |
| 117 } | |
| 118 prettifyJsonInternal(json); | |
| 119 return buffer.toString(); | |
| 120 } | |
| 121 | |
| 122 | |
| 123 /** | |
| 124 * [areByteArraysEqual] compares a range of bytes from [buffer1] with a | |
| 125 * range of bytes from [buffer2]. | |
| 126 * | |
| 127 * Returns [true] if the [count] bytes in [buffer1] (starting at | |
| 128 * [offset1]) match the [count] bytes in [buffer2] (starting at | |
| 129 * [offset2]). | |
| 130 * Otherwise [false] is returned. | |
| 131 */ | |
| 132 bool areByteArraysEqual(List<int> buffer1, int offset1, | |
| 133 List<int> buffer2, int offset2, | |
| 134 int count) { | |
| 135 if ((offset1 + count) > buffer1.length || | |
| 136 (offset2 + count) > buffer2.length) { | |
| 137 return false; | |
| 138 } | |
| 139 | |
| 140 for (var i = 0; i < count; i++) { | |
| 141 if (buffer1[offset1 + i] != buffer2[offset2 + i]) { | |
| 142 return false; | |
| 143 } | |
| 144 } | |
| 145 return true; | |
| 146 } | |
| 147 | |
| 148 /** | |
| 149 * [findBytes] searches for [pattern] in [data] beginning at [startPos]. | |
| 150 * | |
| 151 * Returns [true] if [pattern] was found in [data]. | |
| 152 * Otherwise [false] is returned. | |
| 153 */ | |
| 154 int findBytes(List<int> data, List<int> pattern, [int startPos=0]) { | |
| 155 // TODO(kustermann): Use one of the fast string-matching algorithms! | |
| 156 for (int i = startPos; i < (data.length - pattern.length); i++) { | |
| 157 bool found = true; | |
| 158 for (int j = 0; j < pattern.length; j++) { | |
| 159 if (data[i + j] != pattern[j]) { | |
| 160 found = false; | |
| 161 break; | |
| 162 } | |
| 163 } | |
| 164 if (found) { | |
| 165 return i; | |
| 166 } | |
| 167 } | |
| 168 return -1; | |
| 169 } | |
| 170 | |
| 171 List<int> encodeUtf8(String string) { | |
| 172 return UTF8.encode(string); | |
| 173 } | |
| 174 | |
| 175 // TODO(kustermann,ricow): As soon we have a debug log we should log | |
| 176 // invalid utf8-encoded input to the log. | |
| 177 // Currently invalid bytes will be replaced by a replacement character. | |
| 178 String decodeUtf8(List<int> bytes) { | |
| 179 return UTF8.decode(bytes, allowMalformed: true); | |
| 180 } | |
| 181 | |
| 182 class Locations { | |
| 183 static String getBrowserLocation(String browserName, | |
| 184 Map globalConfiguration) { | |
| 185 var location = globalConfiguration[browserName]; | |
| 186 if (location != null && location != '') { | |
| 187 return location; | |
| 188 } | |
| 189 var browserLocations = { | |
| 190 'firefox': const { | |
| 191 'windows': 'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe', | |
| 192 'linux': 'firefox', | |
| 193 'macos': '/Applications/Firefox.app/Contents/MacOS/firefox' | |
| 194 }, | |
| 195 'chrome': const { | |
| 196 'windows': | |
| 197 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', | |
| 198 'macos': | |
| 199 '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', | |
| 200 'linux': 'google-chrome' | |
| 201 }, | |
| 202 'dartium': const { | |
| 203 'windows': 'client\\tests\\dartium\\chrome.exe', | |
| 204 'macos': 'client/tests/dartium/Chromium.app/Contents/MacOS/Chromium', | |
| 205 'linux': 'client/tests/dartium/chrome' | |
| 206 }, | |
| 207 'safari': const { | |
| 208 'macos': '/Applications/Safari.app/Contents/MacOS/Safari' | |
| 209 }, | |
| 210 'safarimobilesim': const { | |
| 211 'macos': '/Applications/Xcode.app/Contents/Developer/Platforms/' | |
| 212 'iPhoneSimulator.platform/Developer/Applications/' | |
| 213 'iPhone Simulator.app/Contents/MacOS/iPhone Simulator' | |
| 214 }, | |
| 215 'ie9': const { | |
| 216 'windows': 'C:\\Program Files\\Internet Explorer\\iexplore.exe' | |
| 217 }, | |
| 218 'ie10': const { | |
| 219 'windows': 'C:\\Program Files\\Internet Explorer\\iexplore.exe' | |
| 220 }, | |
| 221 'ie11': const { | |
| 222 'windows': 'C:\\Program Files\\Internet Explorer\\iexplore.exe' | |
| 223 }}; | |
| 224 browserLocations['ff'] = browserLocations['firefox']; | |
| 225 | |
| 226 assert(browserLocations[browserName] != null); | |
| 227 location = browserLocations[browserName][Platform.operatingSystem]; | |
| 228 if (location != null) { | |
| 229 return location; | |
| 230 } else { | |
| 231 throw '$browserName not supported on ${Platform.operatingSystem}'; | |
| 232 } | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 // This function is pretty stupid and only puts quotes around an argument if | |
| 237 // it the argument contains a space. | |
| 238 String escapeCommandLineArgument(String argument) { | |
| 239 if (argument.contains(' ')) { | |
| 240 return '"$argument"'; | |
| 241 } | |
| 242 return argument; | |
| 243 } | |
| 244 | |
| 245 class HashCodeBuilder { | |
| 246 int _value = 0; | |
| 247 | |
| 248 void add(Object object) { | |
| 249 _value = ((_value * 31) ^ object.hashCode) & 0x3FFFFFFF; | |
| 250 } | |
| 251 | |
| 252 void addJson(Object object) { | |
| 253 if (object == null || object is num || object is String || | |
| 254 object is Uri || object is bool) { | |
| 255 add(object); | |
| 256 } else if (object is List) { | |
| 257 object.forEach(addJson); | |
| 258 } else if (object is Map) { | |
| 259 for (var key in object.keys.toList()..sort()) { | |
| 260 addJson(key); | |
| 261 addJson(object[key]); | |
| 262 } | |
| 263 } else { | |
| 264 throw new Exception("Can't build hashcode for non json-like object " | |
| 265 "(${object.runtimeType})"); | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 int get value => _value; | |
| 270 } | |
| 271 | |
| 272 bool deepJsonCompare(Object a, Object b) { | |
| 273 if (a == null || a is num || a is String) { | |
| 274 return a == b; | |
| 275 } else if (a is List) { | |
| 276 if (b is List) { | |
| 277 if (a.length != b.length) return false; | |
| 278 | |
| 279 for (int i = 0; i < a.length; i++) { | |
| 280 if (!deepJsonCompare(a[i], b[i])) return false; | |
| 281 } | |
| 282 return true; | |
| 283 } | |
| 284 return false; | |
| 285 } else if (a is Map) { | |
| 286 if (b is Map) { | |
| 287 if (a.length != b.length) return false; | |
| 288 | |
| 289 for (var key in a.keys) { | |
| 290 if (!b.containsKey(key)) return false; | |
| 291 if (!deepJsonCompare(a[key], b[key])) return false; | |
| 292 } | |
| 293 return true; | |
| 294 } | |
| 295 return false; | |
| 296 } else { | |
| 297 throw new Exception("Can't compare two non json-like objects " | |
| 298 "(a: ${a.runtimeType}, b: ${b.runtimeType})"); | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 class UniqueObject { | |
| 303 static int _nextId = 1; | |
| 304 final int _hashCode; | |
| 305 | |
| 306 int get hashCode => _hashCode; | |
| 307 operator==(other) => other is UniqueObject && _hashCode == other._hashCode; | |
| 308 | |
| 309 UniqueObject() : _hashCode = ++_nextId; | |
| 310 } | |
| OLD | NEW |