| 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 |