| 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 '../../unittest/lib/unittest.dart'; | 7 import '../../unittest/lib/unittest.dart'; |
| 8 import '../lib/serialization.dart'; | 8 import '../lib/serialization.dart'; |
| 9 import '../lib/src/serialization_helpers.dart'; | 9 import '../lib/src/serialization_helpers.dart'; |
| 10 import '../lib/src/mirrors_helpers.dart'; | 10 import '../lib/src/mirrors_helpers.dart'; |
| (...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 213 var list = w.states[1].first; | 213 var list = w.states[1].first; |
| 214 expect(w.stateForReference(rootNode[children]), list); | 214 expect(w.stateForReference(rootNode[children]), list); |
| 215 var parentNode = w.stateForReference(list[0])[parent]; | 215 var parentNode = w.stateForReference(list[0])[parent]; |
| 216 expect(w.stateForReference(parentNode), rootNode); | 216 expect(w.stateForReference(parentNode), rootNode); |
| 217 }); | 217 }); |
| 218 | 218 |
| 219 test('round-trip', () { | 219 test('round-trip', () { |
| 220 runRoundTripTest(nodeSerializerReflective); | 220 runRoundTripTest(nodeSerializerReflective); |
| 221 }); | 221 }); |
| 222 | 222 |
| 223 test('round-trip hard-coded', () { | 223 test('round-trip ClosureRule', () { |
| 224 runRoundTripTest(nodeSerializerNonReflective); | 224 runRoundTripTest(nodeSerializerNonReflective); |
| 225 }); | 225 }); |
| 226 | 226 |
| 227 test('round-trip with essential parent', () { | 227 test('round-trip with essential parent', () { |
| 228 runRoundTripTest(nodeSerializerWithEssentialParent); | 228 runRoundTripTest(nodeSerializerWithEssentialParent); |
| 229 }); | 229 }); |
| 230 | 230 |
| 231 test('round-trip, flat format', () { | 231 test('round-trip, flat format', () { |
| 232 runRoundTripTestFlat(nodeSerializerReflective); | 232 runRoundTripTestFlat(nodeSerializerReflective); |
| 233 }); | 233 }); |
| 234 | 234 |
| 235 test('round-trip using Maps', () { | 235 test('round-trip using Maps', () { |
| 236 runRoundTripTest(nodeSerializerUsingMaps); | 236 runRoundTripTest(nodeSerializerUsingMaps); |
| 237 }); | 237 }); |
| 238 | 238 |
| 239 test('round-trip with Node CustomRule', () { |
| 240 runRoundTripTestFlat(nodeSerializerCustom); |
| 241 }); |
| 242 |
| 243 test('round-trip with Node CustomRule, to maps', () { |
| 244 runRoundTripTest(nodeSerializerCustom); |
| 245 }); |
| 246 |
| 239 test('eating your own tail', () { | 247 test('eating your own tail', () { |
| 240 // Create a meta-serializer, that serializes serializations, then | 248 // Create a meta-serializer, that serializes serializations, then |
| 241 // use it to serialize a basic serialization, then run a test on the | 249 // use it to serialize a basic serialization, then run a test on the |
| 242 // the result. | 250 // the result. |
| 243 var s = new Serialization() | 251 var s = new Serialization() |
| 244 ..addRuleFor(new Node(''), constructorFields: ['name']) | 252 ..addRuleFor(new Node(''), constructorFields: ['name']) |
| 245 ..selfDescribing = false; | 253 ..selfDescribing = false; |
| 246 var meta = metaSerialization(); | 254 var meta = metaSerialization(); |
| 247 var serialized = meta.write(s); | 255 var serialized = meta.write(s); |
| 248 var s2 = new Reader(meta) | 256 var s2 = new Reader(meta) |
| 249 .readOne(serialized, {"Node" : reflect(new Node('')).type}); | 257 .read(serialized, {"Node" : reflect(new Node('')).type}); |
| 250 runRoundTripTest((x) => s2); | 258 runRoundTripTest((x) => s2); |
| 251 }); | 259 }); |
| 252 | 260 |
| 253 test("Verify we're not serializing lists twice if they're essential", () { | 261 test("Verify we're not serializing lists twice if they're essential", () { |
| 254 Node n1 = new Node("1"), n2 = new Node("2"), n3 = new Node("3"); | 262 Node n1 = new Node("1"), n2 = new Node("2"), n3 = new Node("3"); |
| 255 n1.children = [n2, n3]; | 263 n1.children = [n2, n3]; |
| 256 n2.parent = n1; | 264 n2.parent = n1; |
| 257 n3.parent = n1; | 265 n3.parent = n1; |
| 258 var s = new Serialization() | 266 var s = new Serialization() |
| 259 ..addRuleFor(n1, constructorFields: ["name"]). | 267 ..addRuleFor(n1, constructorFields: ["name"]). |
| (...skipping 18 matching lines...) Expand all Loading... |
| 278 n2 = new NodeEqualByName("foo"), | 286 n2 = new NodeEqualByName("foo"), |
| 279 n3 = new NodeEqualByName("3"); | 287 n3 = new NodeEqualByName("3"); |
| 280 n1.children = [n2, n3]; | 288 n1.children = [n2, n3]; |
| 281 n2.parent = n1; | 289 n2.parent = n1; |
| 282 n3.parent = n1; | 290 n3.parent = n1; |
| 283 var s = new Serialization() | 291 var s = new Serialization() |
| 284 ..selfDescribing = false | 292 ..selfDescribing = false |
| 285 ..addRuleFor(n1, constructorFields: ["name"]); | 293 ..addRuleFor(n1, constructorFields: ["name"]); |
| 286 var w = new Writer(s); | 294 var w = new Writer(s); |
| 287 var r = new Reader(s); | 295 var r = new Reader(s); |
| 288 var m1 = r.read(w.write(n1)).first; | 296 var m1 = r.read(w.write(n1)); |
| 289 var m2 = m1.children.first; | 297 var m2 = m1.children.first; |
| 290 var m3 = m1.children.last; | 298 var m3 = m1.children.last; |
| 291 expect(m1, m2); | 299 expect(m1, m2); |
| 292 expect(identical(m1, m2), isFalse); | 300 expect(identical(m1, m2), isFalse); |
| 293 expect(m1 == m3, isFalse); | 301 expect(m1 == m3, isFalse); |
| 294 expect(identical(m2.parent, m3.parent), isTrue); | 302 expect(identical(m2.parent, m3.parent), isTrue); |
| 295 }); | 303 }); |
| 296 | 304 |
| 297 } | 305 } |
| 298 | 306 |
| 299 /****************************************************************************** | 307 /****************************************************************************** |
| 300 * The end of the tests and the beginning of various helper functions to make | 308 * The end of the tests and the beginning of various helper functions to make |
| 301 * it easier to write the repetitive sections. | 309 * it easier to write the repetitive sections. |
| 302 ******************************************************************************/ | 310 ******************************************************************************/ |
| 303 | 311 |
| 304 /** Create a Serialization for serializing Serializations. */ | 312 /** Create a Serialization for serializing Serializations. */ |
| 305 Serialization metaSerialization() { | 313 Serialization metaSerialization() { |
| 306 // Make some bogus rule instances so we have something to feed rule creation | 314 // Make some bogus rule instances so we have something to feed rule creation |
| 307 // and get their types. If only we had class literals implemented... | 315 // and get their types. If only we had class literals implemented... |
| 308 var closureRule = new ClosureToMapRule.stub([].runtimeType); | |
| 309 var basicRule = new BasicRule(reflect(null).type, '', [], [], []); | 316 var basicRule = new BasicRule(reflect(null).type, '', [], [], []); |
| 310 | 317 |
| 311 var meta = new Serialization() | 318 var meta = new Serialization() |
| 312 ..selfDescribing = false | 319 ..selfDescribing = false |
| 313 ..addRuleFor(new ListRule()) | 320 ..addRuleFor(new ListRule()) |
| 314 ..addRuleFor(new PrimitiveRule()) | 321 ..addRuleFor(new PrimitiveRule()) |
| 315 // TODO(alanknight): Handle the ClosureToMapRule as well. | 322 // TODO(alanknight): Handle CustomRule as well. |
| 316 // Note that we're passing in a constant for one of the fields. | 323 // Note that we're passing in a constant for one of the fields. |
| 317 ..addRuleFor(basicRule, | 324 ..addRuleFor(basicRule, |
| 318 constructorFields: ['typeWrapped', | 325 constructorFields: ['typeWrapped', |
| 319 'constructorName', | 326 'constructorName', |
| 320 'constructorFields', 'regularFields', []], | 327 'constructorFields', 'regularFields', []], |
| 321 fields: []) | 328 fields: []) |
| 322 ..addRuleFor(new Serialization()).specialTreatmentFor('rules', | 329 ..addRuleFor(new Serialization()).specialTreatmentFor('rules', |
| 323 (InstanceMirror s, List rules) { | 330 (InstanceMirror s, List rules) { |
| 324 rules.forEach((x) => s.reflectee.addRule(x)); | 331 rules.forEach((x) => s.reflectee.addRule(x)); |
| 325 }) | 332 }) |
| 326 ..addRule(new ClassMirrorRule()); | 333 ..addRule(new NamedObjectRule()) |
| 334 ..addRule(new MirrorRule()); |
| 327 return meta; | 335 return meta; |
| 328 } | 336 } |
| 329 | 337 |
| 330 /** | 338 /** |
| 331 * Read back a simple object, assumed to be the only one of its class in the | 339 * Read back a simple object, assumed to be the only one of its class in the |
| 332 * reader. | 340 * reader. |
| 333 */ | 341 */ |
| 334 readBackSimple(Serialization s, object, Reader reader) { | 342 readBackSimple(Serialization s, object, Reader reader) { |
| 335 var rule = s.rulesFor(object)[0]; | 343 var rule = s.rulesFor(object, null)[0]; |
| 336 reader.inflateForRule(rule); | 344 reader.inflateForRule(rule); |
| 337 var list2 = reader.allObjectsForRule(rule)[0]; | 345 var list2 = reader.allObjectsForRule(rule)[0]; |
| 338 return list2; | 346 return list2; |
| 339 } | 347 } |
| 340 | 348 |
| 341 /** | 349 /** |
| 342 * Set up a basic reader with some fake data. Hard-codes the assumption | 350 * Set up a basic reader with some fake data. Hard-codes the assumption |
| 343 * of how many rules there are. | 351 * of how many rules there are. |
| 344 */ | 352 */ |
| 345 Reader setUpReader(aSerialization, sampleData) { | 353 Reader setUpReader(aSerialization, sampleData) { |
| 346 var reader = new Reader(aSerialization); | 354 var reader = new Reader(aSerialization); |
| 347 // We're not sure which rule needs the sample data, so put it everywhere | 355 // We're not sure which rule needs the sample data, so put it everywhere |
| 348 // and trust that the extra will just be ignored. | 356 // and trust that the extra will just be ignored. |
| 349 reader.data = [[sampleData], [sampleData], [sampleData], [sampleData]]; | 357 reader.data = [[sampleData], [sampleData], [sampleData], [sampleData]]; |
| 350 return reader; | 358 return reader; |
| 351 } | 359 } |
| 352 | 360 |
| 353 /** Return a serialization for Node objects, using a reflective rule. */ | 361 /** Return a serialization for Node objects, using a reflective rule. */ |
| 354 Serialization nodeSerializerReflective(Node n) { | 362 Serialization nodeSerializerReflective(Node n) { |
| 355 return new Serialization() | 363 return new Serialization() |
| 356 ..addRuleFor(n, constructorFields: ["name"]) | 364 ..addRuleFor(n, constructorFields: ["name"]) |
| 357 ..externalObjects['Node'] = reflect(new Node('')).type; | 365 ..namedObjects['Node'] = reflect(new Node('')).type; |
| 358 } | 366 } |
| 359 | 367 |
| 360 /** | 368 /** |
| 361 * Return a serialization for Node objects but using Maps for the internal | 369 * Return a serialization for Node objects but using Maps for the internal |
| 362 * representation rather than lists. | 370 * representation rather than lists. |
| 363 */ | 371 */ |
| 364 Serialization nodeSerializerUsingMaps(Node n) { | 372 Serialization nodeSerializerUsingMaps(Node n) { |
| 365 return new Serialization() | 373 return new Serialization() |
| 366 ..addRuleFor(n, constructorFields: ["name"]).configureForMaps() | 374 ..addRuleFor(n, constructorFields: ["name"]).configureForMaps() |
| 367 ..externalObjects['Node'] = reflect(new Node('')).type; | 375 ..namedObjects['Node'] = reflect(new Node('')).type; |
| 368 } | 376 } |
| 369 | 377 |
| 370 /** | 378 /** |
| 379 * Return a serialization for Node objects but using Maps for the internal |
| 380 * representation rather than lists. |
| 381 */ |
| 382 Serialization nodeSerializerCustom(Node n) { |
| 383 return new Serialization() |
| 384 ..addRule(new NodeRule()); |
| 385 } |
| 386 |
| 387 /** |
| 371 * Return a serialization for Node objects where the "parent" instance | 388 * Return a serialization for Node objects where the "parent" instance |
| 372 * variable is considered essential state. | 389 * variable is considered essential state. |
| 373 */ | 390 */ |
| 374 Serialization nodeSerializerWithEssentialParent(Node n) { | 391 Serialization nodeSerializerWithEssentialParent(Node n) { |
| 375 // Force the node rule to be first, in order to make a cycle which would | 392 // Force the node rule to be first, in order to make a cycle which would |
| 376 // not cause a problem if we handled the list first, because the list | 393 // not cause a problem if we handled the list first, because the list |
| 377 // considers all of its state non-essential, thus breaking the cycle. | 394 // considers all of its state non-essential, thus breaking the cycle. |
| 378 var s = new Serialization.blank() | 395 var s = new Serialization.blank() |
| 379 ..addRuleFor( | 396 ..addRuleFor( |
| 380 n, | 397 n, |
| 381 constructor: "parentEssential", | 398 constructor: "parentEssential", |
| 382 constructorFields: ["parent"]) | 399 constructorFields: ["parent"]) |
| 383 ..addDefaultRules() | 400 ..addDefaultRules() |
| 384 ..externalObjects['Node'] = reflect(new Node('')).type | 401 ..namedObjects['Node'] = reflect(new Node('')).type |
| 385 ..selfDescribing = false; | 402 ..selfDescribing = false; |
| 386 return s; | 403 return s; |
| 387 } | 404 } |
| 388 | 405 |
| 389 /** Return a serialization for Node objects using a ClosureToMapRule. */ | 406 /** Return a serialization for Node objects using a ClosureToMapRule. */ |
| 390 Serialization nodeSerializerNonReflective(Node n) { | 407 Serialization nodeSerializerNonReflective(Node n) { |
| 391 var rule = new ClosureToMapRule( | 408 var rule = new ClosureRule( |
| 392 n.runtimeType, | 409 n.runtimeType, |
| 393 (o) => {"name" : o.name, "children" : o.children, "parent" : o.parent}, | 410 (o) => {"name" : o.name, "children" : o.children, "parent" : o.parent}, |
| 394 (map) => new Node(map["name"]), | 411 (map) => new Node(map["name"]), |
| 395 (map, object) { | 412 (object, map) { |
| 396 object | 413 object |
| 397 ..children = map["children"] | 414 ..children = map["children"] |
| 398 ..parent = map["parent"]; | 415 ..parent = map["parent"]; |
| 399 }); | 416 }); |
| 400 return new Serialization() | 417 return new Serialization() |
| 401 ..selfDescribing = false | 418 ..selfDescribing = false |
| 402 ..addRule(rule); | 419 ..addRule(rule); |
| 403 } | 420 } |
| 404 | 421 |
| 405 /** | 422 /** |
| 406 * Run a round-trip test on a simple tree of nodes, using a serialization | 423 * Run a round-trip test on a simple tree of nodes, using a serialization |
| 407 * that's returned by the [serializerSetup] function. | 424 * that's returned by the [serializerSetup] function. |
| 408 */ | 425 */ |
| 409 runRoundTripTest(Function serializerSetUp) { | 426 runRoundTripTest(Function serializerSetUp) { |
| 410 Node n1 = new Node("1"), n2 = new Node("2"), n3 = new Node("3"); | 427 Node n1 = new Node("1"), n2 = new Node("2"), n3 = new Node("3"); |
| 411 n1.children = [n2, n3]; | 428 n1.children = [n2, n3]; |
| 412 n2.parent = n1; | 429 n2.parent = n1; |
| 413 n3.parent = n1; | 430 n3.parent = n1; |
| 414 var s = serializerSetUp(n1); | 431 var s = serializerSetUp(n1); |
| 415 var output = s.write(n2); | 432 var output = s.write(n2); |
| 416 var s2 = serializerSetUp(n1); | 433 var s2 = serializerSetUp(n1); |
| 417 var reader = new Reader(s2); | 434 var reader = new Reader(s2); |
| 418 var m2 = reader.readOne(output); | 435 var m2 = reader.read(output); |
| 419 var m1 = m2.parent; | 436 var m1 = m2.parent; |
| 420 expect(m1 is Node, isTrue); | 437 expect(m1 is Node, isTrue); |
| 421 var children = m1.children; | 438 var children = m1.children; |
| 422 expect(m1.name,"1"); | 439 expect(m1.name,"1"); |
| 423 var m3 = m1.children.last; | 440 var m3 = m1.children.last; |
| 424 expect(m2.name, "2"); | 441 expect(m2.name, "2"); |
| 425 expect(m3.name, "3"); | 442 expect(m3.name, "3"); |
| 426 expect(m2.parent, m1); | 443 expect(m2.parent, m1); |
| 427 expect(m3.parent, m1); | 444 expect(m3.parent, m1); |
| 428 expect(m1.parent, isNull); | 445 expect(m1.parent, isNull); |
| 429 } | 446 } |
| 430 | 447 |
| 431 /** | 448 /** |
| 432 * Run a round-trip test on a simple of nodes, but using the flat format | 449 * Run a round-trip test on a simple of nodes, but using the flat format |
| 433 * rather than the maps. | 450 * rather than the maps. |
| 434 */ | 451 */ |
| 435 runRoundTripTestFlat(serializerSetUp) { | 452 runRoundTripTestFlat(serializerSetUp) { |
| 436 Node n1 = new Node("1"), n2 = new Node("2"), n3 = new Node("3"); | 453 Node n1 = new Node("1"), n2 = new Node("2"), n3 = new Node("3"); |
| 437 n1.children = [n2, n3]; | 454 n1.children = [n2, n3]; |
| 438 n2.parent = n1; | 455 n2.parent = n1; |
| 439 n3.parent = n1; | 456 n3.parent = n1; |
| 440 var s = serializerSetUp(n1); | 457 var s = serializerSetUp(n1); |
| 441 var output = s.writeFlat(n2); | 458 var output = s.writeFlat(n2); |
| 442 expect(output is List, isTrue); | 459 expect(output is List, isTrue); |
| 443 var s2 = serializerSetUp(n1); | 460 var s2 = serializerSetUp(n1); |
| 444 var reader = new Reader(s2); | 461 var reader = new Reader(s2); |
| 445 var m2 = reader.readFlat(output).first; | 462 var m2 = reader.readFlat(output); |
| 446 var m1 = m2.parent; | 463 var m1 = m2.parent; |
| 447 expect(m1 is Node, isTrue); | 464 expect(m1 is Node, isTrue); |
| 448 var children = m1.children; | 465 var children = m1.children; |
| 449 expect(m1.name,"1"); | 466 expect(m1.name,"1"); |
| 450 var m3 = m1.children.last; | 467 var m3 = m1.children.last; |
| 451 expect(m2.name, "2"); | 468 expect(m2.name, "2"); |
| 452 expect(m3.name, "3"); | 469 expect(m3.name, "3"); |
| 453 expect(m2.parent, m1); | 470 expect(m2.parent, m1); |
| 454 expect(m3.parent, m1); | 471 expect(m3.parent, m1); |
| 455 expect(m1.parent, isNull); | 472 expect(m1.parent, isNull); |
| 456 } | 473 } |
| 457 | 474 |
| 458 /** Extract the state from [object] using the rules in [s] and return it. */ | 475 /** Extract the state from [object] using the rules in [s] and return it. */ |
| 459 states(Object object, Serialization s) { | 476 states(object, Serialization s) { |
| 460 var rules = s.rulesFor(object); | 477 var rules = s.rulesFor(object, null); |
| 461 return rules.map((x) => x.extractState(object, doNothing)); | 478 return rules.map((x) => x.extractState(object, doNothing)); |
| 479 } |
| 480 |
| 481 /** A hard-coded rule for serializing Node instances. */ |
| 482 class NodeRule extends CustomRule { |
| 483 bool appliesTo(instance, _) => instance is Node; |
| 484 getState(instance) => [instance.parent, instance.name, instance.children]; |
| 485 create(state) => new Node(state[1]); |
| 486 setState(Node node, state) { |
| 487 node.parent = state[0]; |
| 488 node.children = state[2]; |
| 489 } |
| 462 } | 490 } |
| OLD | NEW |