Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Tests for the toString methods on collections and maps. | 6 * Tests for the toString methods on collections and maps. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 library collection_to_string; | 9 library collection_to_string; |
| 10 | 10 |
| 11 import "package:expect/expect.dart"; | 11 import "package:expect/expect.dart"; |
| 12 import 'dart:collection' show Queue, LinkedHashMap; | 12 import 'dart:collection' show Queue, LinkedHashMap; |
| 13 import 'dart:math' as Math; | 13 import 'dart:math' as Math; |
| 14 | 14 |
| 15 // TODO(jjb): seed random number generator when API allows it | 15 // TODO(jjb): seed random number generator when API allows it |
| 16 | 16 |
| 17 const int NUM_TESTS = 300; | 17 const int NUM_TESTS = 300; |
| 18 const int MAX_COLLECTION_SIZE = 7; | 18 const int MAX_COLLECTION_SIZE = 7; |
| 19 | 19 |
| 20 Math.Random rand; | 20 Math.Random rand; |
| 21 | 21 |
| 22 main() { | 22 main() { |
| 23 rand = new Math.Random(); | 23 rand = new Math.Random(); |
| 24 smokeTest(); | 24 smokeTest(); |
| 25 exactTest(); | 25 exactTest(); |
| 26 inexactTest(); | |
| 27 } | 26 } |
| 28 | 27 |
| 29 | 28 |
| 30 /** | 29 /** |
| 31 * Test a few simple examples. | 30 * Test a few simple examples. |
| 32 */ | 31 */ |
| 33 void smokeTest() { | 32 void smokeTest() { |
| 34 // Non-const lists | 33 // Non-const lists |
| 35 Expect.equals([].toString(), '[]'); | 34 Expect.equals([].toString(), '[]'); |
| 36 Expect.equals([1].toString(), '[1]'); | 35 Expect.equals([1].toString(), '[1]'); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 79 * orders (i.e., no HashSet, HashMap). | 78 * orders (i.e., no HashSet, HashMap). |
| 80 */ | 79 */ |
| 81 void exactTest() { | 80 void exactTest() { |
| 82 for (int i = 0; i < NUM_TESTS; i++) { | 81 for (int i = 0; i < NUM_TESTS; i++) { |
| 83 // Choose a size from 0 to MAX_COLLECTION_SIZE, favoring larger sizes | 82 // Choose a size from 0 to MAX_COLLECTION_SIZE, favoring larger sizes |
| 84 int size = | 83 int size = |
| 85 Math.sqrt(random(MAX_COLLECTION_SIZE * MAX_COLLECTION_SIZE)).toInt(); | 84 Math.sqrt(random(MAX_COLLECTION_SIZE * MAX_COLLECTION_SIZE)).toInt(); |
| 86 | 85 |
| 87 StringBuffer stringRep = new StringBuffer(); | 86 StringBuffer stringRep = new StringBuffer(); |
| 88 Object o = randomCollection(size, stringRep, exact:true); | 87 Object o = randomCollection(size, stringRep, exact:true); |
| 89 print(stringRep); | 88 String expected = stringRep.toString(); |
| 90 print(o); | 89 String actual = o.toString(); |
| 91 Expect.equals(o.toString(), stringRep.toString()); | 90 print("Expect: $expected"); |
|
Søren Gjesse
2014/05/23 07:18:13
Debug print.
Lasse Reichstein Nielsen
2014/05/23 09:09:53
Not really, they were already in the test, I just
| |
| 91 print("Actual: $actual"); | |
| 92 Expect.equals(expected, actual); | |
| 92 } | 93 } |
| 93 } | 94 } |
| 94 | 95 |
| 95 /** | |
| 96 * Generate a bunch of random collections (including Maps), and test that | |
| 97 * there string form is as expected. The collections include collections | |
| 98 * as elements, keys, and values, and include recursive references. | |
| 99 * | |
| 100 * This test includes collections with ill-defined iteration orders (i.e., | |
| 101 * HashSet, HashMap). As a consequence, it can't use equality tests on the | |
| 102 * string form. Instead, it performs equality tests on their "alphagrams." | |
| 103 * This might allow false positives, but it does give a fair amount of | |
| 104 * confidence. | |
| 105 */ | |
| 106 void inexactTest() { | |
| 107 for (int i = 0; i < NUM_TESTS; i++) { | |
| 108 // Choose a size from 0 to MAX_COLLECTION_SIZE, favoring larger sizes | |
| 109 int size = | |
| 110 Math.sqrt(random(MAX_COLLECTION_SIZE * MAX_COLLECTION_SIZE)).toInt(); | |
| 111 | |
| 112 StringBuffer stringRep = new StringBuffer(); | |
| 113 Object o = randomCollection(size, stringRep, exact:false); | |
| 114 print(stringRep); | |
| 115 print(o); | |
| 116 Expect.equals(alphagram(o.toString()), alphagram(stringRep.toString())); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 /** | 96 /** |
| 121 * Return a random collection (or Map) of the specified size, placing its | 97 * Return a random collection (or Map) of the specified size, placing its |
| 122 * string representation into the given string buffer. | 98 * string representation into the given string buffer. |
| 123 * | 99 * |
| 124 * If exact is true, the returned collections will not be, and will not contain | 100 * If exact is true, the returned collections will not be, and will not contain |
| 125 * a collection with ill-defined iteration order (i.e., a HashSet or HashMap). | 101 * a collection with ill-defined iteration order (i.e., a HashSet or HashMap). |
| 126 */ | 102 */ |
| 127 Object randomCollection(int size, StringBuffer stringRep, {bool exact}) { | 103 Object randomCollection(int size, StringBuffer stringRep, {bool exact}) { |
| 128 return randomCollectionHelper(size, exact, stringRep, []); | 104 return randomCollectionHelper(size, exact, stringRep, []); |
| 129 } | 105 } |
| 130 | 106 |
| 131 /** | 107 /** |
| 132 * Return a random collection (or map) of the specified size, placing its | 108 * Return a random collection (or map) of the specified size, placing its |
| 133 * string representation into the given string buffer. The beingMade | 109 * string representation into the given string buffer. The beingMade |
| 134 * parameter is a list of collections currently under construction, i.e., | 110 * parameter is a list of collections currently under construction, i.e., |
| 135 * candidates for recursive references. | 111 * candidates for recursive references. |
| 136 * | 112 * |
| 137 * If exact is true, the returned collections will not be, and will not contain | 113 * If exact is true, the returned collections will not be, and will not contain |
| 138 * a collection with ill-defined iteration order (i.e., a HashSet or HashMap). | 114 * a collection with ill-defined iteration order (i.e., a HashSet or HashMap). |
| 139 */ | 115 */ |
| 140 Object randomCollectionHelper(int size, bool exact, StringBuffer stringRep, | 116 Object randomCollectionHelper(int size, bool exact, StringBuffer stringRep, |
| 141 List beingMade) { | 117 List beingMade) { |
| 142 double interfaceFrac = rand.nextDouble(); | 118 double interfaceFrac = rand.nextDouble(); |
| 143 | 119 |
| 144 if (exact) { | 120 if (exact) { |
| 145 if (interfaceFrac < 1/3) { | 121 if (interfaceFrac < 1/3) { |
| 146 return randomList(size, exact, stringRep, beingMade); | 122 return randomList(size, exact, stringRep, beingMade); |
| 147 } else if (interfaceFrac < 2/3) { | 123 } else if (interfaceFrac < 2/3) { |
| 148 return randomQueue(size, exact, stringRep, beingMade); | 124 return randomQueue(size, exact, stringRep, beingMade); |
| 149 } else { | 125 } else { |
| 150 return randomMap(size, exact, stringRep, beingMade); | 126 return randomMap(size, exact, stringRep, beingMade); |
| 151 } | 127 } |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 181 Queue randomQueue(int size, bool exact, StringBuffer stringRep, List beingMade){ | 157 Queue randomQueue(int size, bool exact, StringBuffer stringRep, List beingMade){ |
| 182 return populateRandomCollection( | 158 return populateRandomCollection( |
| 183 size, exact, stringRep, beingMade, new Queue(), "{}"); | 159 size, exact, stringRep, beingMade, new Queue(), "{}"); |
| 184 } | 160 } |
| 185 | 161 |
| 186 /** | 162 /** |
| 187 * Like randomList, but returns a Set. | 163 * Like randomList, but returns a Set. |
| 188 */ | 164 */ |
| 189 Set randomSet(int size, bool exact, StringBuffer stringRep, List beingMade) { | 165 Set randomSet(int size, bool exact, StringBuffer stringRep, List beingMade) { |
| 190 // Until we have LinkedHashSet, method will only be called with exact==true | 166 // Until we have LinkedHashSet, method will only be called with exact==true |
| 191 return populateRandomSet(size, exact, stringRep, beingMade, new Set()); | 167 return populateRandomCollection( |
| 168 size, exact, stringRep, beingMade, new Set(), "{}"); | |
| 192 } | 169 } |
| 193 | 170 |
| 194 /** | 171 /** |
| 195 * Like randomList, but returns a map. | 172 * Like randomList, but returns a map. |
| 196 */ | 173 */ |
| 197 Map randomMap(int size, bool exact, StringBuffer stringRep, List beingMade) { | 174 Map randomMap(int size, bool exact, StringBuffer stringRep, List beingMade) { |
| 198 if (exact) { | 175 if (exact) { |
| 199 return populateRandomMap(size, exact, stringRep, beingMade, | 176 return populateRandomMap(size, exact, stringRep, beingMade, |
| 200 new LinkedHashMap()); | 177 new LinkedHashMap()); |
| 201 } else { | 178 } else { |
| 202 return populateRandomMap(size, exact, stringRep, beingMade, | 179 return populateRandomMap(size, exact, stringRep, beingMade, |
| 203 randomBool() ? new Map() : new LinkedHashMap()); | 180 randomBool() ? new Map() : new LinkedHashMap()); |
| 204 } | 181 } |
| 205 } | 182 } |
| 206 | 183 |
| 207 /** | 184 /** |
| 208 * Populates the given empty collection with elements, emitting the string | 185 * Populates the given empty collection with elements, emitting the string |
| 209 * representation of the collection to stringRep. The beingMade parameter is | 186 * representation of the collection to stringRep. The beingMade parameter is |
| 210 * a list of collections currently under construction, i.e., candidates for | 187 * a list of collections currently under construction, i.e., candidates for |
| 211 * recursive references. | 188 * recursive references. |
| 212 * | 189 * |
| 213 * If exact is true, the elements of the returned collections will not be, | 190 * If exact is true, the elements of the returned collections will not be, |
| 214 * and will not contain a collection with ill-defined iteration order | 191 * and will not contain, a collection with undefined iteration order |
| 215 * (i.e., a HashSet or HashMap). | 192 * (i.e., a HashSet or HashMap). |
| 216 */ | 193 */ |
| 217 populateRandomCollection(int size, bool exact, | 194 populateRandomCollection(int size, bool exact, |
| 218 StringBuffer stringRep, List beingMade, var coll, String delimiters) { | 195 StringBuffer stringRep, List beingMade, var coll, String delimiters) { |
| 219 beingMade.add(coll); | 196 beingMade.add(coll); |
| 220 int start = stringRep.length; | 197 int start = stringRep.length; |
| 221 | 198 |
| 222 stringRep.write(delimiters[0]); | 199 stringRep.write(delimiters[0]); |
| 223 | 200 |
| 224 List indices = []; | 201 List indices = []; |
| 225 for (int i = 0; i < size; i++) { | 202 for (int i = 0; i < size; i++) { |
| 226 indices.add(stringRep.length); | 203 indices.add(stringRep.length); |
| 227 if (i != 0) stringRep.write(', '); | 204 if (i != 0) stringRep.write(', '); |
| 228 coll.add(randomElement(random(size), exact, stringRep, beingMade)); | 205 coll.add(randomElement(random(size), exact, stringRep, beingMade)); |
| 229 } | 206 } |
| 230 if (size > 5 && delimiters == "()") { | 207 if (size > 5 && coll is! Map |
| 208 // Lists don't yet use ListMixin or its toString. | |
| 209 // Remove this line when they do. | |
| 210 && coll is! List /// 01: ok | |
| 211 ) { | |
| 231 const int MAX_LENGTH = 80; | 212 const int MAX_LENGTH = 80; |
| 232 const int MIN_COUNT = 3; | 213 const int MIN_COUNT = 3; |
| 233 const int MAX_COUNT = 100; | 214 const int MAX_COUNT = 100; |
| 234 // It's an iterable, it may omit some elements. | 215 // It may omit some elements. |
| 235 int end = stringRep.length; | 216 int end = stringRep.length; |
| 236 if (size > MAX_COUNT) { | 217 if (size > MAX_COUNT) { |
| 237 // Last two elements are also omitted, just find the first three or | 218 // Last two elements are also omitted, just find the first three elements |
| 238 // first 60 characters. | 219 // or first 60 characters. |
| 239 for (int i = MIN_COUNT; i < size; i++) { | 220 for (int i = MIN_COUNT; i < size; i++) { |
| 240 int startIndex = indices[i]; | 221 int startIndex = indices[i]; |
| 241 if (startIndex - start > MAX_LENGTH - 6) { // Limit - ", ...)".length. | 222 if (startIndex - start > MAX_LENGTH - 6) { // Limit - ", ...)".length. |
| 242 String prefix = stringRep.toString().substring(0, startIndex); | 223 String prefix = stringRep.toString().substring(0, startIndex); |
| 243 stringRep.clear(); | 224 stringRep.clear(); |
| 244 stringRep.write(prefix); | 225 stringRep.write(prefix); |
| 245 stringRep.write(", ..."); | 226 stringRep.write(", ..."); |
| 246 } | 227 } |
| 247 } | 228 } |
| 248 } else if (stringRep.length - start > MAX_LENGTH - 1) { // 80 - ")".length. | 229 } else if (stringRep.length - start > MAX_LENGTH - 1) { // 80 - ")".length. |
| 249 // Last two elements are always included. Middle ones may be omitted. | 230 // Last two elements are always included. Middle ones may be omitted. |
| 250 int lastTwoLength = end - indices[indices.length - 2]; | 231 int lastTwoLength = end - indices[indices.length - 2]; |
| 251 // Try to find first element to omit. | 232 // Try to find first element to omit. |
| 252 for (int i = 3; i <= size - 3; i++) { | 233 for (int i = 3; i <= size - 3; i++) { |
| 253 int elementEnd = indices[i + 1]; | 234 int elementEnd = indices[i + 1]; |
| 254 int lengthAfter = elementEnd - start; | 235 int lengthAfter = elementEnd - start; |
| 255 int ellipsisSize = 5; // ", ...".length | 236 int ellipsisSize = 5; // ", ...".length |
| 256 if (i == size - 3) ellipsisSize = 0; // No ellipsis if we hit the end. | 237 if (i == size - 3) ellipsisSize = 0; // No ellipsis if we hit the end. |
| 257 if (lengthAfter + ellipsisSize + lastTwoLength > MAX_LENGTH - 1) { | 238 if (lengthAfter + ellipsisSize + lastTwoLength > MAX_LENGTH - 1) { |
| 258 // Omit this element and everything up to the last two. | 239 // Omit this element and everything up to the last two. |
| 259 int elementStart = indices[i]; | 240 int elementStart = indices[i]; |
| 241 if (elementStart + ellipsisSize + lastTwoLength >= stringRep.length) { | |
| 242 break; | |
| 243 } | |
| 260 // Rewrite string buffer by copying it out, clearing, and putting | 244 // Rewrite string buffer by copying it out, clearing, and putting |
| 261 // the parts back in. | 245 // the parts back in. |
| 262 String buffer = stringRep.toString(); | 246 String buffer = stringRep.toString(); |
| 263 String prefix = buffer.substring(0, elementStart); | 247 String prefix = buffer.substring(0, elementStart); |
| 264 String suffix = buffer.substring(end - lastTwoLength, end); | 248 String suffix = buffer.substring(end - lastTwoLength, end); |
| 265 stringRep.clear(); | 249 stringRep.clear(); |
| 266 stringRep.write(prefix); | 250 stringRep.write(prefix); |
| 267 stringRep.write(", ..."); | 251 stringRep.write(", ..."); |
| 268 stringRep.write(suffix); | 252 stringRep.write(suffix); |
| 269 break; | 253 break; |
| 270 } | 254 } |
| 271 } | 255 } |
| 272 } | 256 } |
| 273 } | 257 } |
| 274 | 258 |
| 275 stringRep.write(delimiters[1]); | 259 stringRep.write(delimiters[1]); |
| 276 beingMade.removeLast(); | 260 beingMade.removeLast(); |
| 277 return coll; | 261 return coll; |
| 278 } | 262 } |
| 279 | 263 |
| 280 /** Like populateRandomCollection, but for sets (elements must be hashable) */ | |
| 281 Set populateRandomSet(int size, bool exact, StringBuffer stringRep, | |
| 282 List beingMade, Set set) { | |
| 283 stringRep.write('{'); | |
| 284 | |
| 285 for (int i = 0; i < size; i++) { | |
| 286 if (i != 0) stringRep.write(', '); | |
| 287 set.add(i); | |
| 288 stringRep.write(i); | |
| 289 } | |
| 290 | |
| 291 stringRep.write('}'); | |
| 292 return set; | |
| 293 } | |
| 294 | |
| 295 | |
| 296 /** Like populateRandomCollection, but for maps. */ | 264 /** Like populateRandomCollection, but for maps. */ |
| 297 Map populateRandomMap(int size, bool exact, StringBuffer stringRep, | 265 Map populateRandomMap(int size, bool exact, StringBuffer stringRep, |
| 298 List beingMade, Map map) { | 266 List beingMade, Map map) { |
| 299 beingMade.add(map); | 267 beingMade.add(map); |
| 300 stringRep.write('{'); | 268 stringRep.write('{'); |
| 301 | 269 |
| 302 for (int i = 0; i < size; i++) { | 270 for (int i = 0; i < size; i++) { |
| 303 if (i != 0) stringRep.write(', '); | 271 if (i != 0) stringRep.write(', '); |
| 304 | 272 |
| 305 int key = i; // Ensures no duplicates | 273 int key = i; // Ensures no duplicates |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 323 * a collection with ill-defined iteration order (i.e., a HashSet or HashMap). | 291 * a collection with ill-defined iteration order (i.e., a HashSet or HashMap). |
| 324 */ | 292 */ |
| 325 Object randomElement(int size, bool exact, StringBuffer stringRep, | 293 Object randomElement(int size, bool exact, StringBuffer stringRep, |
| 326 List beingMade) { | 294 List beingMade) { |
| 327 Object result; | 295 Object result; |
| 328 double elementTypeFrac = rand.nextDouble(); | 296 double elementTypeFrac = rand.nextDouble(); |
| 329 if (elementTypeFrac < 1/3) { | 297 if (elementTypeFrac < 1/3) { |
| 330 result = random(1000); | 298 result = random(1000); |
| 331 stringRep.write(result); | 299 stringRep.write(result); |
| 332 } else if (elementTypeFrac < 2/3) { | 300 } else if (elementTypeFrac < 2/3) { |
| 333 // Element Is a random (new) collection | 301 // Element is a random (new) collection |
| 334 result = randomCollectionHelper(size, exact, stringRep, beingMade); | 302 result = randomCollectionHelper(size, exact, stringRep, beingMade); |
| 335 } else { | 303 } else { |
| 336 // Element Is a random recursive ref | 304 // Element is a random recursive ref |
| 337 result = beingMade[random(beingMade.length)]; | 305 result = beingMade[random(beingMade.length)]; |
| 338 if (result is List) { | 306 if (result is List) { |
| 339 stringRep.write('[...]'); | 307 stringRep.write('[...]'); |
| 340 } else if (result is Set || result is Map || result is Queue) { | 308 } else if (result is Set || result is Map || result is Queue) { |
| 341 stringRep.write('{...}'); | 309 stringRep.write('{...}'); |
| 342 } else { | 310 } else { |
| 343 stringRep.write('(...)'); | 311 stringRep.write('(...)'); |
| 344 } | 312 } |
| 345 } | 313 } |
| 346 return result; | 314 return result; |
| 347 } | 315 } |
| 348 | 316 |
| 349 /** Returns a random int on [0, max) */ | 317 /** Returns a random int on [0, max) */ |
| 350 int random(int max) { | 318 int random(int max) { |
| 351 return rand.nextInt(max); | 319 return rand.nextInt(max); |
| 352 } | 320 } |
| 353 | 321 |
| 354 /** Returns a random boolean value. */ | 322 /** Returns a random boolean value. */ |
| 355 bool randomBool() { | 323 bool randomBool() { |
| 356 return rand.nextBool(); | 324 return rand.nextBool(); |
| 357 } | 325 } |
| 358 | |
| 359 /** Returns the alphabetized characters in a string. */ | |
| 360 String alphagram(String s) { | |
| 361 // Calling [toList] to convert unmodifiable list to normal list. | |
| 362 List<int> chars = s.codeUnits.toList(); | |
| 363 chars.sort((int a, int b) => a - b); | |
| 364 return new String.fromCharCodes(chars); | |
| 365 } | |
| OLD | NEW |