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 library serialization_test; | 5 library serialization_test; |
6 | 6 |
7 import 'dart:json' as json; | 7 import 'dart:json' as json; |
8 import 'package:unittest/unittest.dart'; | 8 import 'package:unittest/unittest.dart'; |
9 import 'package:serialization/serialization.dart'; | 9 import 'package:serialization/serialization.dart'; |
10 import 'package:serialization/src/serialization_helpers.dart'; | 10 import 'package:serialization/src/serialization_helpers.dart'; |
(...skipping 29 matching lines...) Expand all Loading... |
40 // TODO(alanknight): Tests that rely on what index rules are going to be | 40 // TODO(alanknight): Tests that rely on what index rules are going to be |
41 // at are very fragile. At least abstract it to something calculated. | 41 // at are very fragile. At least abstract it to something calculated. |
42 var p1 = new Person()..name = 'Alice'..address = a1; | 42 var p1 = new Person()..name = 'Alice'..address = a1; |
43 var s = new Serialization() | 43 var s = new Serialization() |
44 ..addRuleFor(p1).configureForMaps() | 44 ..addRuleFor(p1).configureForMaps() |
45 ..addRuleFor(a1).configureForMaps(); | 45 ..addRuleFor(a1).configureForMaps(); |
46 // TODO(alanknight): Need a better API for getting to flat state without | 46 // TODO(alanknight): Need a better API for getting to flat state without |
47 // actually writing. | 47 // actually writing. |
48 var w = new Writer(s); | 48 var w = new Writer(s); |
49 w.write(p1); | 49 w.write(p1); |
50 var flatPerson = w.states[3].first; | 50 var personRule = s.rules.firstMatching( |
| 51 (x) => x is BasicRule && x.type == reflect(p1).type); |
| 52 var flatPerson = w.states[personRule.number].first; |
51 var primStates = w.states.first; | 53 var primStates = w.states.first; |
52 expect(primStates.isEmpty, true); | 54 expect(primStates.isEmpty, true); |
53 expect(flatPerson["name"], "Alice"); | 55 expect(flatPerson["name"], "Alice"); |
54 var ref = flatPerson["address"]; | 56 var ref = flatPerson["address"]; |
55 expect(ref is Reference, true); | 57 expect(ref is Reference, true); |
56 expect(ref.ruleNumber, 4); | 58 var addressRule = s.rules.firstMatching( |
| 59 (x) => x is BasicRule && x.type == reflect(a1).type); |
| 60 expect(ref.ruleNumber, addressRule.number); |
57 expect(ref.objectNumber, 0); | 61 expect(ref.objectNumber, 0); |
58 expect(w.states[4].first['street'], 'N 34th'); | 62 expect(w.states[addressRule.number].first['street'], 'N 34th'); |
59 }); | 63 }); |
60 | 64 |
61 test('exclude fields', () { | 65 test('exclude fields', () { |
62 var s = new Serialization() | 66 var s = new Serialization() |
63 ..addRuleFor(a1, | 67 ..addRuleFor(a1, |
64 excludeFields: ['state', 'zip']).configureForMaps(); | 68 excludeFields: ['state', 'zip']).configureForMaps(); |
65 var extracted = states(a1, s).first; | 69 var extracted = states(a1, s).first; |
66 expect(extracted.length, 2); | 70 expect(extracted.length, 2); |
67 expect(extracted['street'], 'N 34th'); | 71 expect(extracted['street'], 'N 34th'); |
68 expect(extracted['city'], 'Seattle'); | 72 expect(extracted['city'], 'Seattle'); |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
199 expect(all.contains(n2), isTrue); | 203 expect(all.contains(n2), isTrue); |
200 expect(all.contains(n3), isTrue); | 204 expect(all.contains(n3), isTrue); |
201 expect(all.contains(n1.children), isTrue); | 205 expect(all.contains(n1.children), isTrue); |
202 }); | 206 }); |
203 | 207 |
204 test('Flatten references in a cyclical structure', () { | 208 test('Flatten references in a cyclical structure', () { |
205 var s = new Serialization(); | 209 var s = new Serialization(); |
206 var w = new Writer(s); | 210 var w = new Writer(s); |
207 w.trace = new Trace(w); | 211 w.trace = new Trace(w); |
208 w.write(n1); | 212 w.write(n1); |
209 expect(w.states.length, 4); // prims, lists, essential lists, basic | 213 expect(w.states.length, 5); // prims, lists, essential lists, basic |
210 var children = 0, name = 1, parent = 2; | 214 var children = 0, name = 1, parent = 2; |
211 List rootNode = w.states[3].where((x) => x[name] == "1").toList(); | 215 var nodeRule = s.rules.firstMatching((x) => x is BasicRule); |
| 216 List rootNode = w.states[nodeRule.number].where( |
| 217 (x) => x[name] == "1").toList(); |
212 rootNode = rootNode.first; | 218 rootNode = rootNode.first; |
213 expect(rootNode[parent], isNull); | 219 expect(rootNode[parent], isNull); |
214 var list = w.states[1].first; | 220 var list = w.states[1].first; |
215 expect(w.stateForReference(rootNode[children]), list); | 221 expect(w.stateForReference(rootNode[children]), list); |
216 var parentNode = w.stateForReference(list[0])[parent]; | 222 var parentNode = w.stateForReference(list[0])[parent]; |
217 expect(w.stateForReference(parentNode), rootNode); | 223 expect(w.stateForReference(parentNode), rootNode); |
218 }); | 224 }); |
219 | 225 |
220 test('round-trip', () { | 226 test('round-trip', () { |
221 runRoundTripTest(nodeSerializerReflective); | 227 runRoundTripTest(nodeSerializerReflective); |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
365 var writer = s.newWriter(new SimpleJsonFormat(storeRoundTripInfo: true)); | 371 var writer = s.newWriter(new SimpleJsonFormat(storeRoundTripInfo: true)); |
366 var out = writer.write(p1); | 372 var out = writer.write(p1); |
367 var reader = s.newReader(new SimpleJsonFormat(storeRoundTripInfo: true)); | 373 var reader = s.newReader(new SimpleJsonFormat(storeRoundTripInfo: true)); |
368 var p2 = reader.read(out); | 374 var p2 = reader.read(out); |
369 expect(p2.name, "Alice"); | 375 expect(p2.name, "Alice"); |
370 var a2 = p2.address; | 376 var a2 = p2.address; |
371 expect(a2.street, "N 34th"); | 377 expect(a2.street, "N 34th"); |
372 expect(a2.city, "Seattle"); | 378 expect(a2.city, "Seattle"); |
373 }); | 379 }); |
374 | 380 |
| 381 test("Straight JSON format, root is a Map", () { |
| 382 // Note that we can't use the usual round-trip test because it has cycles. |
| 383 var p1 = new Person()..name = 'Alice'..address = a1; |
| 384 // Use maps for one rule, lists for the other. |
| 385 var s = new Serialization() |
| 386 ..addRuleFor(a1) |
| 387 ..addRuleFor(p1).configureForMaps(); |
| 388 var format = new SimpleJsonFormat(storeRoundTripInfo: true); |
| 389 var writer = s.newWriter(format); |
| 390 var out = writer.write({"stuff" : p1}); |
| 391 var reader = s.newReader(format); |
| 392 var p2 = reader.read(out)["stuff"]; |
| 393 expect(p2.name, "Alice"); |
| 394 var a2 = p2.address; |
| 395 expect(a2.street, "N 34th"); |
| 396 expect(a2.city, "Seattle"); |
| 397 }); |
| 398 |
| 399 |
375 test("Straight JSON format, round-trip with named objects", () { | 400 test("Straight JSON format, round-trip with named objects", () { |
376 // Note that we can't use the usual round-trip test because it has cycles. | 401 // Note that we can't use the usual round-trip test because it has cycles. |
377 var p1 = new Person()..name = 'Alice'..address = a1; | 402 var p1 = new Person()..name = 'Alice'..address = a1; |
378 // Use maps for one rule, lists for the other. | 403 // Use maps for one rule, lists for the other. |
379 var s = new Serialization() | 404 var s = new Serialization() |
380 ..addRule(new NamedObjectRule()) | 405 ..addRule(new NamedObjectRule()) |
381 ..addRuleFor(a1) | 406 ..addRuleFor(a1) |
382 ..addRuleFor(p1).configureForMaps() | 407 ..addRuleFor(p1).configureForMaps() |
383 ..namedObjects["foo"] = a1; | 408 ..namedObjects["foo"] = a1; |
384 var writer = s.newWriter(new SimpleJsonFormat(storeRoundTripInfo: true)); | 409 var writer = s.newWriter(new SimpleJsonFormat(storeRoundTripInfo: true)); |
385 var out = writer.write(p1); | 410 var out = writer.write(p1); |
386 var reader = s.newReader(new SimpleJsonFormat(storeRoundTripInfo: true)); | 411 var reader = s.newReader(new SimpleJsonFormat(storeRoundTripInfo: true)); |
387 var p2 = reader.read(out, {"foo" : 12}); | 412 var p2 = reader.read(out, {"foo" : 12}); |
388 expect(p2.name, "Alice"); | 413 expect(p2.name, "Alice"); |
389 var a2 = p2.address; | 414 var a2 = p2.address; |
390 expect(a2, 12); | 415 expect(a2, 12); |
391 }); | 416 }); |
| 417 |
| 418 test("Maps", () { |
| 419 var s = new Serialization()..selfDescribing = false; |
| 420 var p1 = new Person()..name = 'Alice'..address = a1; |
| 421 var data = new Map(); |
| 422 data["simple data"] = 1; |
| 423 data[p1] = a1; |
| 424 data[a1] = p1; |
| 425 var formats = [new SimpleFlatFormat(), new SimpleMapFormat(), |
| 426 new SimpleJsonFormat(storeRoundTripInfo: true)]; |
| 427 for (var eachFormat in formats) { |
| 428 var output = s.write(data, eachFormat); |
| 429 var reader = s.newReader(eachFormat); |
| 430 var input = reader.read(output); |
| 431 expect(input["simple data"], data["simple data"]); |
| 432 var p2 = input.keys.firstMatching((x) => x is Person); |
| 433 var a2 = input.keys.firstMatching((x) => x is Address); |
| 434 if (eachFormat is SimpleJsonFormat) { |
| 435 // JSON doesn't handle cycles, so these won't be identical. |
| 436 expect(input[p2] is Address, isTrue); |
| 437 expect(input[a2] is Person, isTrue); |
| 438 var a3 = input[p2]; |
| 439 expect(a3.city, a2.city); |
| 440 expect(a3.state, a2.state); |
| 441 expect(a3.state, a2.state); |
| 442 var p3 = input[a2]; |
| 443 expect(p3.name, p2.name); |
| 444 expect(p3.rank, p2.rank); |
| 445 expect(p3.address.city, a2.city); |
| 446 } else { |
| 447 expect(input[p2], same(a2)); |
| 448 expect(input[a2], same(p2)); |
| 449 } |
| 450 } |
| 451 }); |
| 452 |
| 453 test("Map with string keys stays that way", () { |
| 454 var s = new Serialization()..addRuleFor(new Person()); |
| 455 var data = {"abc" : 1, "def" : "ghi"}; |
| 456 data["person"] = new Person()..name = "Foo"; |
| 457 var output = s.write(data, new SimpleMapFormat()); |
| 458 var mapRule = s.rules.firstMatching((x) => x is MapRule); |
| 459 var map = json.parse(output)["data"][mapRule.number][0]; |
| 460 expect(map is Map, isTrue); |
| 461 expect(map["abc"], 1); |
| 462 expect(map["def"], "ghi"); |
| 463 expect(new Reader(s).asReference(map["person"]) is Reference, isTrue); |
| 464 }); |
392 } | 465 } |
393 | 466 |
394 /****************************************************************************** | 467 /****************************************************************************** |
395 * The end of the tests and the beginning of various helper functions to make | 468 * The end of the tests and the beginning of various helper functions to make |
396 * it easier to write the repetitive sections. | 469 * it easier to write the repetitive sections. |
397 ******************************************************************************/ | 470 ******************************************************************************/ |
398 | 471 |
399 /** Create a Serialization for serializing Serializations. */ | 472 /** Create a Serialization for serializing Serializations. */ |
400 Serialization metaSerialization() { | 473 Serialization metaSerialization() { |
401 // Make some bogus rule instances so we have something to feed rule creation | 474 // Make some bogus rule instances so we have something to feed rule creation |
402 // and get their types. If only we had class literals implemented... | 475 // and get their types. If only we had class literals implemented... |
403 var basicRule = new BasicRule(reflect(null).type, '', [], [], []); | 476 var basicRule = new BasicRule(reflect(null).type, '', [], [], []); |
404 | 477 |
405 var meta = new Serialization() | 478 var meta = new Serialization() |
406 ..selfDescribing = false | 479 ..selfDescribing = false |
407 ..addRuleFor(new ListRule()) | 480 ..addRuleFor(new ListRule()) |
408 ..addRuleFor(new PrimitiveRule()) | 481 ..addRuleFor(new PrimitiveRule()) |
409 // TODO(alanknight): Handle CustomRule as well. | 482 // TODO(alanknight): Handle CustomRule as well. |
410 // Note that we're passing in a constant for one of the fields. | 483 // Note that we're passing in a constant for one of the fields. |
411 ..addRuleFor(basicRule, | 484 ..addRuleFor(basicRule, |
412 constructorFields: ['type', | 485 constructorFields: ['type', |
413 'constructorName', | 486 'constructorName', |
414 'constructorFields', 'regularFields', []], | 487 'constructorFields', 'regularFields', []], |
415 fields: []) | 488 fields: []) |
416 ..addRuleFor(new Serialization()).setFieldWith('rules', | 489 ..addRuleFor(new Serialization(), constructor: "blank") |
417 (InstanceMirror s, List rules) { | 490 .setFieldWith('rules', |
| 491 (InstanceMirror s, List rules) { |
418 rules.forEach((x) => s.reflectee.addRule(x)); | 492 rules.forEach((x) => s.reflectee.addRule(x)); |
419 }) | 493 }) |
420 ..addRule(new NamedObjectRule()) | 494 ..addRule(new NamedObjectRule()) |
421 ..addRule(new MirrorRule()); | 495 ..addRule(new MirrorRule()); |
422 return meta; | 496 return meta; |
423 } | 497 } |
424 | 498 |
425 /** | 499 /** |
426 * Read back a simple object, assumed to be the only one of its class in the | 500 * Read back a simple object, assumed to be the only one of its class in the |
427 * reader. | 501 * reader. |
428 */ | 502 */ |
429 readBackSimple(Serialization s, object, Reader reader) { | 503 readBackSimple(Serialization s, object, Reader reader) { |
430 var rule = s.rulesFor(object, null).first; | 504 var rule = s.rulesFor(object, null).first; |
431 reader.inflateForRule(rule); | 505 reader.inflateForRule(rule); |
432 var list2 = reader.allObjectsForRule(rule).first; | 506 var list2 = reader.allObjectsForRule(rule).first; |
433 return list2; | 507 return list2; |
434 } | 508 } |
435 | 509 |
436 /** | 510 /** |
437 * Set up a basic reader with some fake data. Hard-codes the assumption | 511 * Set up a basic reader with some fake data. Hard-codes the assumption |
438 * of how many rules there are. | 512 * of how many rules there are. |
439 */ | 513 */ |
440 Reader setUpReader(aSerialization, sampleData) { | 514 Reader setUpReader(aSerialization, sampleData) { |
441 var reader = new Reader(aSerialization); | 515 var reader = new Reader(aSerialization); |
442 // We're not sure which rule needs the sample data, so put it everywhere | 516 // We're not sure which rule needs the sample data, so put it everywhere |
443 // and trust that the extra will just be ignored. | 517 // and trust that the extra will just be ignored. |
444 reader.data = [[sampleData], [sampleData], [sampleData], [sampleData]]; | 518 reader.data = new List.filled(10, [sampleData]); |
445 return reader; | 519 return reader; |
446 } | 520 } |
447 | 521 |
448 /** Return a serialization for Node objects, using a reflective rule. */ | 522 /** Return a serialization for Node objects, using a reflective rule. */ |
449 Serialization nodeSerializerReflective(Node n) { | 523 Serialization nodeSerializerReflective(Node n) { |
450 return new Serialization() | 524 return new Serialization() |
451 ..addRuleFor(n, constructorFields: ["name"]) | 525 ..addRuleFor(n, constructorFields: ["name"]) |
452 ..namedObjects['Node'] = reflect(new Node('')).type; | 526 ..namedObjects['Node'] = reflect(new Node('')).type; |
453 } | 527 } |
454 | 528 |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
567 | 641 |
568 /** A hard-coded rule for serializing Node instances. */ | 642 /** A hard-coded rule for serializing Node instances. */ |
569 class NodeRule extends CustomRule { | 643 class NodeRule extends CustomRule { |
570 bool appliesTo(instance, _) => instance.runtimeType == Node; | 644 bool appliesTo(instance, _) => instance.runtimeType == Node; |
571 getState(instance) => [instance.parent, instance.name, instance.children]; | 645 getState(instance) => [instance.parent, instance.name, instance.children]; |
572 create(state) => new Node(state[1]); | 646 create(state) => new Node(state[1]); |
573 setState(Node node, state) { | 647 setState(Node node, state) { |
574 node.parent = state[0]; | 648 node.parent = state[0]; |
575 node.children = state[2]; | 649 node.children = state[2]; |
576 } | 650 } |
577 } | 651 } |
OLD | NEW |