OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // adapted from github.com/golang/appengine/datastore | 5 // adapted from github.com/golang/appengine/datastore |
6 | 6 |
7 package helper | 7 package rawdatastore |
8 | 8 |
9 import ( | 9 import ( |
10 "bytes" | 10 "bytes" |
11 "encoding/json" | 11 "encoding/json" |
12 "fmt" | 12 "fmt" |
13 "math" | 13 "math" |
14 "reflect" | 14 "reflect" |
15 "strconv" | 15 "strconv" |
16 "strings" | 16 "strings" |
17 "testing" | 17 "testing" |
18 "time" | 18 "time" |
19 | 19 |
20 » "github.com/luci/gae" | 20 » "github.com/luci/gae/service/blobstore" |
21 . "github.com/smartystreets/goconvey/convey" | 21 . "github.com/smartystreets/goconvey/convey" |
22 ) | 22 ) |
23 | 23 |
24 var ( | 24 var ( |
25 » mp = gae.MkDSProperty | 25 » mp = MkProperty |
26 » mpNI = gae.MkDSPropertyNI | 26 » mpNI = MkPropertyNI |
27 ) | 27 ) |
28 | 28 |
29 const testAppID = "testApp" | 29 const testAppID = "testApp" |
30 | 30 |
31 type ( | 31 type ( |
32 myBlob []byte | 32 myBlob []byte |
33 myByte byte | 33 myByte byte |
34 myString string | 34 myString string |
35 ) | 35 ) |
36 | 36 |
(...skipping 20 matching lines...) Expand all Loading... |
57 } | 57 } |
58 return b | 58 return b |
59 } | 59 } |
60 | 60 |
61 var ( | 61 var ( |
62 testKey0 = mkKey("aid", "", "kind", "name0") | 62 testKey0 = mkKey("aid", "", "kind", "name0") |
63 testKey1a = mkKey("aid", "", "kind", "name1") | 63 testKey1a = mkKey("aid", "", "kind", "name1") |
64 testKey1b = mkKey("aid", "", "kind", "name1") | 64 testKey1b = mkKey("aid", "", "kind", "name1") |
65 testKey2a = mkKey("aid", "", "kind", "name0", "kind", "name2") | 65 testKey2a = mkKey("aid", "", "kind", "name0", "kind", "name2") |
66 testKey2b = mkKey("aid", "", "kind", "name0", "kind", "name2") | 66 testKey2b = mkKey("aid", "", "kind", "name0", "kind", "name2") |
67 » testGeoPt0 = gae.DSGeoPoint{Lat: 1.2, Lng: 3.4} | 67 » testGeoPt0 = GeoPoint{Lat: 1.2, Lng: 3.4} |
68 » testGeoPt1 = gae.DSGeoPoint{Lat: 5, Lng: 10} | 68 » testGeoPt1 = GeoPoint{Lat: 5, Lng: 10} |
69 » testBadGeoPt = gae.DSGeoPoint{Lat: 1000, Lng: 34} | 69 » testBadGeoPt = GeoPoint{Lat: 1000, Lng: 34} |
70 ) | 70 ) |
71 | 71 |
72 type B0 struct { | 72 type B0 struct { |
73 B []byte | 73 B []byte |
74 } | 74 } |
75 | 75 |
76 type B1 struct { | 76 type B1 struct { |
77 B []int8 | 77 B []int8 |
78 } | 78 } |
79 | 79 |
80 type B2 struct { | 80 type B2 struct { |
81 B myBlob | 81 B myBlob |
82 } | 82 } |
83 | 83 |
84 type B3 struct { | 84 type B3 struct { |
85 B []myByte | 85 B []myByte |
86 } | 86 } |
87 | 87 |
88 type B4 struct { | 88 type B4 struct { |
89 B [][]byte | 89 B [][]byte |
90 } | 90 } |
91 | 91 |
92 type B5 struct { | 92 type B5 struct { |
93 » B gae.DSByteString | 93 » B ByteString |
94 } | 94 } |
95 | 95 |
96 type C0 struct { | 96 type C0 struct { |
97 I int | 97 I int |
98 C chan int | 98 C chan int |
99 } | 99 } |
100 | 100 |
101 type C1 struct { | 101 type C1 struct { |
102 I int | 102 I int |
103 C *chan int | 103 C *chan int |
104 } | 104 } |
105 | 105 |
106 type C2 struct { | 106 type C2 struct { |
107 I int | 107 I int |
108 C []chan int | 108 C []chan int |
109 } | 109 } |
110 | 110 |
111 type C3 struct { | 111 type C3 struct { |
112 C string | 112 C string |
113 } | 113 } |
114 | 114 |
115 type E struct{} | 115 type E struct{} |
116 | 116 |
117 type G0 struct { | 117 type G0 struct { |
118 » G gae.DSGeoPoint | 118 » G GeoPoint |
119 } | 119 } |
120 | 120 |
121 type G1 struct { | 121 type G1 struct { |
122 » G []gae.DSGeoPoint | 122 » G []GeoPoint |
123 } | 123 } |
124 | 124 |
125 type K0 struct { | 125 type K0 struct { |
126 » K gae.DSKey | 126 » K Key |
127 } | 127 } |
128 | 128 |
129 type K1 struct { | 129 type K1 struct { |
130 » K []gae.DSKey | 130 » K []Key |
131 } | 131 } |
132 | 132 |
133 type N0 struct { | 133 type N0 struct { |
134 X0 | 134 X0 |
135 ID int64 `gae:"$id"` | 135 ID int64 `gae:"$id"` |
136 _kind string `gae:"$kind,whatnow"` | 136 _kind string `gae:"$kind,whatnow"` |
137 Nonymous X0 | 137 Nonymous X0 |
138 Ignore string `gae:"-"` | 138 Ignore string `gae:"-"` |
139 Other string | 139 Other string |
140 } | 140 } |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
312 I int | 312 I int |
313 R []MutuallyRecursive1 | 313 R []MutuallyRecursive1 |
314 } | 314 } |
315 | 315 |
316 type MutuallyRecursive1 struct { | 316 type MutuallyRecursive1 struct { |
317 I int | 317 I int |
318 R []MutuallyRecursive0 | 318 R []MutuallyRecursive0 |
319 } | 319 } |
320 | 320 |
321 type ExoticTypes struct { | 321 type ExoticTypes struct { |
322 » BS gae.BSKey | 322 » BS blobstore.Key |
323 » DSBS gae.DSByteString | 323 » DSBS ByteString |
324 } | 324 } |
325 | 325 |
326 type Underspecified struct { | 326 type Underspecified struct { |
327 » Iface gae.DSPropertyConverter | 327 » Iface PropertyConverter |
328 } | 328 } |
329 | 329 |
330 type MismatchTypes struct { | 330 type MismatchTypes struct { |
331 S string | 331 S string |
332 B bool | 332 B bool |
333 F float32 | 333 F float32 |
334 » K gae.DSKey | 334 » K Key |
335 T time.Time | 335 T time.Time |
336 » G gae.DSGeoPoint | 336 » G GeoPoint |
337 IS []int | 337 IS []int |
338 } | 338 } |
339 | 339 |
340 type BadSpecial struct { | 340 type BadSpecial struct { |
341 ID int64 `gae:"$id"` | 341 ID int64 `gae:"$id"` |
342 id string `gae:"$id"` | 342 id string `gae:"$id"` |
343 } | 343 } |
344 | 344 |
345 type Doubler struct { | 345 type Doubler struct { |
346 S string | 346 S string |
347 I int64 | 347 I int64 |
348 B bool | 348 B bool |
349 } | 349 } |
350 | 350 |
351 func (d *Doubler) Load(props gae.DSPropertyMap) error { | 351 func (d *Doubler) Load(props PropertyMap) error { |
352 return GetPLS(d).Load(props) | 352 return GetPLS(d).Load(props) |
353 } | 353 } |
354 | 354 |
355 func (d *Doubler) Save(withMeta bool) (gae.DSPropertyMap, error) { | 355 func (d *Doubler) Save(withMeta bool) (PropertyMap, error) { |
356 pls := GetPLS(d) | 356 pls := GetPLS(d) |
357 propMap, err := pls.Save(withMeta) | 357 propMap, err := pls.Save(withMeta) |
358 if err != nil { | 358 if err != nil { |
359 return nil, err | 359 return nil, err |
360 } | 360 } |
361 | 361 |
362 // Edit that map and send it on. | 362 // Edit that map and send it on. |
363 for _, props := range propMap { | 363 for _, props := range propMap { |
364 for i := range props { | 364 for i := range props { |
365 switch v := props[i].Value().(type) { | 365 switch v := props[i].Value().(type) { |
366 case string: | 366 case string: |
367 // + means string concatenation. | 367 // + means string concatenation. |
368 props[i].SetValue(v+v, props[i].IndexSetting()) | 368 props[i].SetValue(v+v, props[i].IndexSetting()) |
369 case int64: | 369 case int64: |
370 // + means integer addition. | 370 // + means integer addition. |
371 props[i].SetValue(v+v, props[i].IndexSetting()) | 371 props[i].SetValue(v+v, props[i].IndexSetting()) |
372 } | 372 } |
373 } | 373 } |
374 } | 374 } |
375 return propMap, nil | 375 return propMap, nil |
376 } | 376 } |
377 | 377 |
378 func (d *Doubler) GetMeta(string) (interface{}, error) { return nil, gae.ErrDSMe
taFieldUnset } | 378 func (d *Doubler) GetMeta(string) (interface{}, error) { return nil, ErrMetaFiel
dUnset } |
379 func (d *Doubler) SetMeta(string, interface{}) error { return gae.ErrDSMetaFie
ldUnset } | 379 func (d *Doubler) SetMeta(string, interface{}) error { return ErrMetaFieldUnse
t } |
380 func (d *Doubler) Problem() error { return nil } | 380 func (d *Doubler) Problem() error { return nil } |
381 | 381 |
382 var _ gae.DSPropertyLoadSaver = (*Doubler)(nil) | 382 var _ PropertyLoadSaver = (*Doubler)(nil) |
383 | 383 |
384 type Deriver struct { | 384 type Deriver struct { |
385 S, Derived, Ignored string | 385 S, Derived, Ignored string |
386 } | 386 } |
387 | 387 |
388 func (d *Deriver) Load(props gae.DSPropertyMap) error { | 388 func (d *Deriver) Load(props PropertyMap) error { |
389 for name, p := range props { | 389 for name, p := range props { |
390 if name != "S" { | 390 if name != "S" { |
391 continue | 391 continue |
392 } | 392 } |
393 d.S = p[0].Value().(string) | 393 d.S = p[0].Value().(string) |
394 d.Derived = "derived+" + d.S | 394 d.Derived = "derived+" + d.S |
395 } | 395 } |
396 return nil | 396 return nil |
397 } | 397 } |
398 | 398 |
399 func (d *Deriver) Save(withMeta bool) (gae.DSPropertyMap, error) { | 399 func (d *Deriver) Save(withMeta bool) (PropertyMap, error) { |
400 » return map[string][]gae.DSProperty{ | 400 » return map[string][]Property{ |
401 "S": {mp(d.S)}, | 401 "S": {mp(d.S)}, |
402 }, nil | 402 }, nil |
403 } | 403 } |
404 | 404 |
405 func (d *Deriver) GetMeta(string) (interface{}, error) { return nil, gae.ErrDSMe
taFieldUnset } | 405 func (d *Deriver) GetMeta(string) (interface{}, error) { return nil, ErrMetaFiel
dUnset } |
406 func (d *Deriver) SetMeta(string, interface{}) error { return gae.ErrDSMetaFie
ldUnset } | 406 func (d *Deriver) SetMeta(string, interface{}) error { return ErrMetaFieldUnse
t } |
407 func (d *Deriver) Problem() error { return nil } | 407 func (d *Deriver) Problem() error { return nil } |
408 | 408 |
409 var _ gae.DSPropertyLoadSaver = (*Deriver)(nil) | 409 var _ PropertyLoadSaver = (*Deriver)(nil) |
410 | 410 |
411 type BK struct { | 411 type BK struct { |
412 » Key gae.BSKey | 412 » Key blobstore.Key |
413 } | 413 } |
414 | 414 |
415 type Convertable []int64 | 415 type Convertable []int64 |
416 | 416 |
417 var _ gae.DSPropertyConverter = (*Convertable)(nil) | 417 var _ PropertyConverter = (*Convertable)(nil) |
418 | 418 |
419 func (c *Convertable) ToDSProperty() (ret gae.DSProperty, err error) { | 419 func (c *Convertable) ToProperty() (ret Property, err error) { |
420 buf := make([]string, len(*c)) | 420 buf := make([]string, len(*c)) |
421 for i, v := range *c { | 421 for i, v := range *c { |
422 buf[i] = strconv.FormatInt(v, 10) | 422 buf[i] = strconv.FormatInt(v, 10) |
423 } | 423 } |
424 » err = ret.SetValue(strings.Join(buf, ","), gae.NoIndex) | 424 » err = ret.SetValue(strings.Join(buf, ","), NoIndex) |
425 return | 425 return |
426 } | 426 } |
427 | 427 |
428 func (c *Convertable) FromDSProperty(pv gae.DSProperty) error { | 428 func (c *Convertable) FromProperty(pv Property) error { |
429 if sval, ok := pv.Value().(string); ok { | 429 if sval, ok := pv.Value().(string); ok { |
430 for _, t := range strings.Split(sval, ",") { | 430 for _, t := range strings.Split(sval, ",") { |
431 ival, err := strconv.ParseInt(t, 10, 64) | 431 ival, err := strconv.ParseInt(t, 10, 64) |
432 if err != nil { | 432 if err != nil { |
433 return err | 433 return err |
434 } | 434 } |
435 *c = append(*c, ival) | 435 *c = append(*c, ival) |
436 } | 436 } |
437 return nil | 437 return nil |
438 } | 438 } |
439 return fmt.Errorf("nope") | 439 return fmt.Errorf("nope") |
440 } | 440 } |
441 | 441 |
442 type Impossible struct { | 442 type Impossible struct { |
443 Nested []ImpossibleInner | 443 Nested []ImpossibleInner |
444 } | 444 } |
445 | 445 |
446 type ImpossibleInner struct { | 446 type ImpossibleInner struct { |
447 Ints Convertable `gae:"wot"` | 447 Ints Convertable `gae:"wot"` |
448 } | 448 } |
449 | 449 |
450 type Convertable2 struct { | 450 type Convertable2 struct { |
451 Data string | 451 Data string |
452 Exploded []string | 452 Exploded []string |
453 } | 453 } |
454 | 454 |
455 func (c *Convertable2) ToDSProperty() (ret gae.DSProperty, err error) { | 455 func (c *Convertable2) ToProperty() (ret Property, err error) { |
456 » err = ret.SetValue(c.Data, gae.ShouldIndex) | 456 » err = ret.SetValue(c.Data, ShouldIndex) |
457 return | 457 return |
458 } | 458 } |
459 | 459 |
460 func (c *Convertable2) FromDSProperty(pv gae.DSProperty) error { | 460 func (c *Convertable2) FromProperty(pv Property) error { |
461 if sval, ok := pv.Value().(string); ok { | 461 if sval, ok := pv.Value().(string); ok { |
462 c.Data = sval | 462 c.Data = sval |
463 c.Exploded = []string{"turn", "down", "for", "what"} | 463 c.Exploded = []string{"turn", "down", "for", "what"} |
464 return nil | 464 return nil |
465 } | 465 } |
466 return fmt.Errorf("nope") | 466 return fmt.Errorf("nope") |
467 } | 467 } |
468 | 468 |
469 type Impossible2 struct { | 469 type Impossible2 struct { |
470 Nested []ImpossibleInner2 | 470 Nested []ImpossibleInner2 |
471 } | 471 } |
472 | 472 |
473 type ImpossibleInner2 struct { | 473 type ImpossibleInner2 struct { |
474 Thingy Convertable2 `gae:"nerb"` | 474 Thingy Convertable2 `gae:"nerb"` |
475 } | 475 } |
476 | 476 |
477 type JSONKVProp map[string]interface{} | 477 type JSONKVProp map[string]interface{} |
478 | 478 |
479 var _ gae.DSPropertyConverter = (*JSONKVProp)(nil) | 479 var _ PropertyConverter = (*JSONKVProp)(nil) |
480 | 480 |
481 func (j *JSONKVProp) ToDSProperty() (ret gae.DSProperty, err error) { | 481 func (j *JSONKVProp) ToProperty() (ret Property, err error) { |
482 data, err := json.Marshal(map[string]interface{}(*j)) | 482 data, err := json.Marshal(map[string]interface{}(*j)) |
483 if err != nil { | 483 if err != nil { |
484 return | 484 return |
485 } | 485 } |
486 » err = ret.SetValue(data, gae.NoIndex) | 486 » err = ret.SetValue(data, NoIndex) |
487 return | 487 return |
488 } | 488 } |
489 | 489 |
490 func (j *JSONKVProp) FromDSProperty(pv gae.DSProperty) error { | 490 func (j *JSONKVProp) FromProperty(pv Property) error { |
491 if bval, ok := pv.Value().([]byte); ok { | 491 if bval, ok := pv.Value().([]byte); ok { |
492 dec := json.NewDecoder(bytes.NewBuffer(bval)) | 492 dec := json.NewDecoder(bytes.NewBuffer(bval)) |
493 dec.UseNumber() | 493 dec.UseNumber() |
494 return dec.Decode((*map[string]interface{})(j)) | 494 return dec.Decode((*map[string]interface{})(j)) |
495 } | 495 } |
496 return fmt.Errorf("nope") | 496 return fmt.Errorf("nope") |
497 } | 497 } |
498 | 498 |
499 type Impossible3 struct { | 499 type Impossible3 struct { |
500 KMap JSONKVProp `gae:"kewelmap"` | 500 KMap JSONKVProp `gae:"kewelmap"` |
501 } | 501 } |
502 | 502 |
503 type Complex complex128 | 503 type Complex complex128 |
504 | 504 |
505 var _ gae.DSPropertyConverter = (*Complex)(nil) | 505 var _ PropertyConverter = (*Complex)(nil) |
506 | 506 |
507 func (c *Complex) ToDSProperty() (ret gae.DSProperty, err error) { | 507 func (c *Complex) ToProperty() (ret Property, err error) { |
508 // cheat hardkore and usurp GeoPoint so datastore will index these sucke
rs | 508 // cheat hardkore and usurp GeoPoint so datastore will index these sucke
rs |
509 // (note that this won't REALLY work, since GeoPoints are limited to a v
ery | 509 // (note that this won't REALLY work, since GeoPoints are limited to a v
ery |
510 // limited range of values, but it's nice to pretend ;)). You'd probably | 510 // limited range of values, but it's nice to pretend ;)). You'd probably |
511 // really end up with a packed binary representation. | 511 // really end up with a packed binary representation. |
512 » err = ret.SetValue(gae.DSGeoPoint{Lat: real(*c), Lng: imag(*c)}, gae.Sho
uldIndex) | 512 » err = ret.SetValue(GeoPoint{Lat: real(*c), Lng: imag(*c)}, ShouldIndex) |
513 return | 513 return |
514 } | 514 } |
515 | 515 |
516 func (c *Complex) FromDSProperty(p gae.DSProperty) error { | 516 func (c *Complex) FromProperty(p Property) error { |
517 » if gval, ok := p.Value().(gae.DSGeoPoint); ok { | 517 » if gval, ok := p.Value().(GeoPoint); ok { |
518 *c = Complex(complex(gval.Lat, gval.Lng)) | 518 *c = Complex(complex(gval.Lat, gval.Lng)) |
519 return nil | 519 return nil |
520 } | 520 } |
521 return fmt.Errorf("nope") | 521 return fmt.Errorf("nope") |
522 } | 522 } |
523 | 523 |
524 type Impossible4 struct { | 524 type Impossible4 struct { |
525 Values []Complex | 525 Values []Complex |
526 } | 526 } |
527 | 527 |
528 type DerivedKey struct { | 528 type DerivedKey struct { |
529 » K *GenericDSKey | 529 » K *GenericKey |
530 } | 530 } |
531 | 531 |
532 type IfaceKey struct { | 532 type IfaceKey struct { |
533 » K gae.DSKey | 533 » K Key |
534 } | 534 } |
535 | 535 |
536 type testCase struct { | 536 type testCase struct { |
537 desc string | 537 desc string |
538 src interface{} | 538 src interface{} |
539 want interface{} | 539 want interface{} |
540 plsErr string | 540 plsErr string |
541 saveErr string | 541 saveErr string |
542 actualNoIndex bool | 542 actualNoIndex bool |
543 plsLoadErr string | 543 plsLoadErr string |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
589 want: &G0{G: testGeoPt0}, | 589 want: &G0{G: testGeoPt0}, |
590 }, | 590 }, |
591 { | 591 { |
592 desc: "geopoint invalid", | 592 desc: "geopoint invalid", |
593 src: &G0{G: testBadGeoPt}, | 593 src: &G0{G: testBadGeoPt}, |
594 saveErr: "invalid GeoPoint value", | 594 saveErr: "invalid GeoPoint value", |
595 }, | 595 }, |
596 { | 596 { |
597 desc: "geopoint as props", | 597 desc: "geopoint as props", |
598 src: &G0{G: testGeoPt0}, | 598 src: &G0{G: testGeoPt0}, |
599 » » want: gae.DSPropertyMap{ | 599 » » want: PropertyMap{ |
600 "G": {mp(testGeoPt0)}, | 600 "G": {mp(testGeoPt0)}, |
601 }, | 601 }, |
602 }, | 602 }, |
603 { | 603 { |
604 desc: "geopoint slice", | 604 desc: "geopoint slice", |
605 » » src: &G1{G: []gae.DSGeoPoint{testGeoPt0, testGeoPt1}}, | 605 » » src: &G1{G: []GeoPoint{testGeoPt0, testGeoPt1}}, |
606 » » want: &G1{G: []gae.DSGeoPoint{testGeoPt0, testGeoPt1}}, | 606 » » want: &G1{G: []GeoPoint{testGeoPt0, testGeoPt1}}, |
607 }, | 607 }, |
608 { | 608 { |
609 desc: "key", | 609 desc: "key", |
610 src: &K0{K: testKey1a}, | 610 src: &K0{K: testKey1a}, |
611 want: &K0{K: testKey1b}, | 611 want: &K0{K: testKey1b}, |
612 }, | 612 }, |
613 { | 613 { |
614 desc: "key with parent", | 614 desc: "key with parent", |
615 src: &K0{K: testKey2a}, | 615 src: &K0{K: testKey2a}, |
616 want: &K0{K: testKey2b}, | 616 want: &K0{K: testKey2b}, |
617 }, | 617 }, |
618 { | 618 { |
619 desc: "nil key", | 619 desc: "nil key", |
620 src: &K0{}, | 620 src: &K0{}, |
621 want: &K0{}, | 621 want: &K0{}, |
622 }, | 622 }, |
623 { | 623 { |
624 desc: "all nil keys in slice", | 624 desc: "all nil keys in slice", |
625 » » src: &K1{[]gae.DSKey{nil, nil}}, | 625 » » src: &K1{[]Key{nil, nil}}, |
626 » » want: &K1{[]gae.DSKey{nil, nil}}, | 626 » » want: &K1{[]Key{nil, nil}}, |
627 }, | 627 }, |
628 { | 628 { |
629 desc: "some nil keys in slice", | 629 desc: "some nil keys in slice", |
630 » » src: &K1{[]gae.DSKey{testKey1a, nil, testKey2a}}, | 630 » » src: &K1{[]Key{testKey1a, nil, testKey2a}}, |
631 » » want: &K1{[]gae.DSKey{testKey1b, nil, testKey2b}}, | 631 » » want: &K1{[]Key{testKey1b, nil, testKey2b}}, |
632 }, | 632 }, |
633 { | 633 { |
634 desc: "overflow", | 634 desc: "overflow", |
635 src: &O0{I: 1 << 48}, | 635 src: &O0{I: 1 << 48}, |
636 want: &O1{}, | 636 want: &O1{}, |
637 loadErr: "overflow", | 637 loadErr: "overflow", |
638 }, | 638 }, |
639 { | 639 { |
640 desc: "time", | 640 desc: "time", |
641 src: &T{T: time.Unix(1e9, 0).UTC()}, | 641 src: &T{T: time.Unix(1e9, 0).UTC()}, |
642 want: &T{T: time.Unix(1e9, 0).UTC()}, | 642 want: &T{T: time.Unix(1e9, 0).UTC()}, |
643 }, | 643 }, |
644 { | 644 { |
645 desc: "time as props", | 645 desc: "time as props", |
646 src: &T{T: time.Unix(1e9, 0).UTC()}, | 646 src: &T{T: time.Unix(1e9, 0).UTC()}, |
647 » » want: gae.DSPropertyMap{ | 647 » » want: PropertyMap{ |
648 "T": {mp(time.Unix(1e9, 0).UTC())}, | 648 "T": {mp(time.Unix(1e9, 0).UTC())}, |
649 }, | 649 }, |
650 }, | 650 }, |
651 { | 651 { |
652 desc: "uint save", | 652 desc: "uint save", |
653 src: &U0{U: 1}, | 653 src: &U0{U: 1}, |
654 plsErr: `field "U" has invalid type: uint`, | 654 plsErr: `field "U" has invalid type: uint`, |
655 }, | 655 }, |
656 { | 656 { |
657 desc: "uint load", | 657 desc: "uint load", |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
708 want: &Y2{B: true, F: []int64{7}}, | 708 want: &Y2{B: true, F: []int64{7}}, |
709 }, | 709 }, |
710 { | 710 { |
711 desc: "use convertable slice", | 711 desc: "use convertable slice", |
712 src: &Impossible{[]ImpossibleInner{{Convertable{1, 5, 9}}, {Con
vertable{2, 4, 6}}}}, | 712 src: &Impossible{[]ImpossibleInner{{Convertable{1, 5, 9}}, {Con
vertable{2, 4, 6}}}}, |
713 want: &Impossible{[]ImpossibleInner{{Convertable{1, 5, 9}}, {Con
vertable{2, 4, 6}}}}, | 713 want: &Impossible{[]ImpossibleInner{{Convertable{1, 5, 9}}, {Con
vertable{2, 4, 6}}}}, |
714 }, | 714 }, |
715 { | 715 { |
716 desc: "use convertable slice (to map)", | 716 desc: "use convertable slice (to map)", |
717 src: &Impossible{[]ImpossibleInner{{Convertable{1, 5, 9}}, {Con
vertable{2, 4, 6}}}}, | 717 src: &Impossible{[]ImpossibleInner{{Convertable{1, 5, 9}}, {Con
vertable{2, 4, 6}}}}, |
718 » » want: gae.DSPropertyMap{ | 718 » » want: PropertyMap{ |
719 "Nested.wot": {mpNI("1,5,9"), mpNI("2,4,6")}, | 719 "Nested.wot": {mpNI("1,5,9"), mpNI("2,4,6")}, |
720 }, | 720 }, |
721 }, | 721 }, |
722 { | 722 { |
723 desc: "convertable slice (bad load)", | 723 desc: "convertable slice (bad load)", |
724 » » src: gae.DSPropertyMap{"Nested.wot": {mpNI([]byte("ohai"))}}
, | 724 » » src: PropertyMap{"Nested.wot": {mpNI([]byte("ohai"))}}, |
725 want: &Impossible{[]ImpossibleInner{{}}}, | 725 want: &Impossible{[]ImpossibleInner{{}}}, |
726 loadErr: "nope", | 726 loadErr: "nope", |
727 }, | 727 }, |
728 { | 728 { |
729 desc: "use convertable struct", | 729 desc: "use convertable struct", |
730 src: &Impossible2{ | 730 src: &Impossible2{ |
731 []ImpossibleInner2{ | 731 []ImpossibleInner2{ |
732 {Convertable2{"nerb", nil}}, | 732 {Convertable2{"nerb", nil}}, |
733 }, | 733 }, |
734 }, | 734 }, |
(...skipping 22 matching lines...) Expand all Loading... |
757 }, | 757 }, |
758 { | 758 { |
759 desc: "convertable json KVMap (to map)", | 759 desc: "convertable json KVMap (to map)", |
760 src: &Impossible3{ | 760 src: &Impossible3{ |
761 JSONKVProp{ | 761 JSONKVProp{ |
762 "epic": "success", | 762 "epic": "success", |
763 "no_way!": []interface{}{true, "story"}, | 763 "no_way!": []interface{}{true, "story"}, |
764 "what": []interface{}{"is", "really", 100}, | 764 "what": []interface{}{"is", "really", 100}, |
765 }, | 765 }, |
766 }, | 766 }, |
767 » » want: gae.DSPropertyMap{ | 767 » » want: PropertyMap{ |
768 "kewelmap": { | 768 "kewelmap": { |
769 mpNI([]byte( | 769 mpNI([]byte( |
770 `{"epic":"success","no_way!":[true,"stor
y"],"what":["is","really",100]}`))}, | 770 `{"epic":"success","no_way!":[true,"stor
y"],"what":["is","really",100]}`))}, |
771 }, | 771 }, |
772 }, | 772 }, |
773 { | 773 { |
774 desc: "convertable complex slice", | 774 desc: "convertable complex slice", |
775 src: &Impossible4{ | 775 src: &Impossible4{ |
776 []Complex{complex(1, 2), complex(3, 4)}, | 776 []Complex{complex(1, 2), complex(3, 4)}, |
777 }, | 777 }, |
778 want: &Impossible4{ | 778 want: &Impossible4{ |
779 []Complex{complex(1, 2), complex(3, 4)}, | 779 []Complex{complex(1, 2), complex(3, 4)}, |
780 }, | 780 }, |
781 }, | 781 }, |
782 { | 782 { |
783 desc: "convertable complex slice (to map)", | 783 desc: "convertable complex slice (to map)", |
784 src: &Impossible4{ | 784 src: &Impossible4{ |
785 []Complex{complex(1, 2), complex(3, 4)}, | 785 []Complex{complex(1, 2), complex(3, 4)}, |
786 }, | 786 }, |
787 » » want: gae.DSPropertyMap{ | 787 » » want: PropertyMap{ |
788 "Values": { | 788 "Values": { |
789 » » » » mp(gae.DSGeoPoint{Lat: 1, Lng: 2}), mp(gae.DSGeo
Point{Lat: 3, Lng: 4})}, | 789 » » » » mp(GeoPoint{Lat: 1, Lng: 2}), mp(GeoPoint{Lat: 3
, Lng: 4})}, |
790 }, | 790 }, |
791 }, | 791 }, |
792 { | 792 { |
793 desc: "convertable complex slice (bad load)", | 793 desc: "convertable complex slice (bad load)", |
794 » » src: gae.DSPropertyMap{"Values": {mp("hello")}}, | 794 » » src: PropertyMap{"Values": {mp("hello")}}, |
795 want: &Impossible4{[]Complex(nil)}, | 795 want: &Impossible4{[]Complex(nil)}, |
796 loadErr: "nope", | 796 loadErr: "nope", |
797 }, | 797 }, |
798 { | 798 { |
799 » » desc: "allow concrete gae.DSKey implementors (save)", | 799 » » desc: "allow concrete Key implementors (save)", |
800 » » src: &DerivedKey{testKey2a.(*GenericDSKey)}, | 800 » » src: &DerivedKey{testKey2a.(*GenericKey)}, |
801 want: &IfaceKey{testKey2b}, | 801 want: &IfaceKey{testKey2b}, |
802 }, | 802 }, |
803 { | 803 { |
804 » » desc: "allow concrete gae.DSKey implementors (load)", | 804 » » desc: "allow concrete Key implementors (load)", |
805 src: &IfaceKey{testKey2b}, | 805 src: &IfaceKey{testKey2b}, |
806 » » want: &DerivedKey{testKey2a.(*GenericDSKey)}, | 806 » » want: &DerivedKey{testKey2a.(*GenericKey)}, |
807 }, | 807 }, |
808 { | 808 { |
809 desc: "save []float64 load []int64", | 809 desc: "save []float64 load []int64", |
810 src: &Y0{B: true, F: []float64{7, 8, 9}}, | 810 src: &Y0{B: true, F: []float64{7, 8, 9}}, |
811 want: &Y2{B: true}, | 811 want: &Y2{B: true}, |
812 loadErr: "type mismatch", | 812 loadErr: "type mismatch", |
813 }, | 813 }, |
814 { | 814 { |
815 desc: "single slice is too long", | 815 desc: "single slice is too long", |
816 src: &Y0{F: make([]float64, maxIndexedProperties+1)}, | 816 src: &Y0{F: make([]float64, maxIndexedProperties+1)}, |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
872 makeUint8Slice(4), | 872 makeUint8Slice(4), |
873 makeUint8Slice(5), | 873 makeUint8Slice(5), |
874 }}, | 874 }}, |
875 want: &B4{B: [][]byte{ | 875 want: &B4{B: [][]byte{ |
876 makeUint8Slice(3), | 876 makeUint8Slice(3), |
877 makeUint8Slice(4), | 877 makeUint8Slice(4), |
878 makeUint8Slice(5), | 878 makeUint8Slice(5), |
879 }}, | 879 }}, |
880 }, | 880 }, |
881 { | 881 { |
882 » » desc: "short gae.DSByteString", | 882 » » desc: "short ByteString", |
883 » » src: &B5{B: gae.DSByteString(makeUint8Slice(3))}, | 883 » » src: &B5{B: ByteString(makeUint8Slice(3))}, |
884 » » want: &B5{B: gae.DSByteString(makeUint8Slice(3))}, | 884 » » want: &B5{B: ByteString(makeUint8Slice(3))}, |
885 }, | 885 }, |
886 { | 886 { |
887 » » desc: "short gae.DSByteString as props", | 887 » » desc: "short ByteString as props", |
888 » » src: &B5{B: gae.DSByteString(makeUint8Slice(3))}, | 888 » » src: &B5{B: ByteString(makeUint8Slice(3))}, |
889 » » want: gae.DSPropertyMap{ | 889 » » want: PropertyMap{ |
890 » » » "B": {mp(gae.DSByteString(makeUint8Slice(3)))}, | 890 » » » "B": {mp(ByteString(makeUint8Slice(3)))}, |
891 }, | 891 }, |
892 }, | 892 }, |
893 { | 893 { |
894 desc: "[]byte must be noindex", | 894 desc: "[]byte must be noindex", |
895 » » src: gae.DSPropertyMap{ | 895 » » src: PropertyMap{ |
896 "B": {mp(makeUint8Slice(3))}, | 896 "B": {mp(makeUint8Slice(3))}, |
897 }, | 897 }, |
898 actualNoIndex: true, | 898 actualNoIndex: true, |
899 }, | 899 }, |
900 { | 900 { |
901 desc: "save tagged load props", | 901 desc: "save tagged load props", |
902 src: &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, I: 6
, J: 7}, | 902 src: &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, I: 6
, J: 7}, |
903 » » want: gae.DSPropertyMap{ | 903 » » want: PropertyMap{ |
904 // A and B are renamed to a and b; A and C are noindex,
I is ignored. | 904 // A and B are renamed to a and b; A and C are noindex,
I is ignored. |
905 // Indexed properties are loaded before raw properties.
Thus, the | 905 // Indexed properties are loaded before raw properties.
Thus, the |
906 // result is: b, b, b, D, E, a, c. | 906 // result is: b, b, b, D, E, a, c. |
907 "b1": { | 907 "b1": { |
908 mp(21), | 908 mp(21), |
909 mp(22), | 909 mp(22), |
910 mp(23), | 910 mp(23), |
911 }, | 911 }, |
912 "D": {mp(4)}, | 912 "D": {mp(4)}, |
913 "E": {mp(5)}, | 913 "E": {mp(5)}, |
914 "a": {mpNI(1)}, | 914 "a": {mpNI(1)}, |
915 "C": {mpNI(3)}, | 915 "C": {mpNI(3)}, |
916 "J": {mpNI(7)}, | 916 "J": {mpNI(7)}, |
917 }, | 917 }, |
918 }, | 918 }, |
919 { | 919 { |
920 desc: "save tagged load tagged", | 920 desc: "save tagged load tagged", |
921 src: &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, I: 6
, J: 7}, | 921 src: &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, I: 6
, J: 7}, |
922 want: &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, J: 7
}, | 922 want: &Tagged{A: 1, B: []int{21, 22, 23}, C: 3, D: 4, E: 5, J: 7
}, |
923 }, | 923 }, |
924 { | 924 { |
925 desc: "save props load tagged", | 925 desc: "save props load tagged", |
926 » » src: gae.DSPropertyMap{ | 926 » » src: PropertyMap{ |
927 "A": {mpNI(11)}, | 927 "A": {mpNI(11)}, |
928 "a": {mpNI(12)}, | 928 "a": {mpNI(12)}, |
929 }, | 929 }, |
930 want: &Tagged{A: 12}, | 930 want: &Tagged{A: 12}, |
931 loadErr: `cannot load field "A"`, | 931 loadErr: `cannot load field "A"`, |
932 }, | 932 }, |
933 { | 933 { |
934 desc: "invalid tagged1", | 934 desc: "invalid tagged1", |
935 src: &InvalidTagged1{I: 1}, | 935 src: &InvalidTagged1{I: 1}, |
936 plsErr: `struct tag has invalid property name: "\t"`, | 936 plsErr: `struct tag has invalid property name: "\t"`, |
(...skipping 20 matching lines...) Expand all Loading... |
957 plsErr: `struct tag has repeated property name: "V.I"`, | 957 plsErr: `struct tag has repeated property name: "V.I"`, |
958 }, | 958 }, |
959 { | 959 { |
960 desc: "doubler", | 960 desc: "doubler", |
961 src: &Doubler{S: "s", I: 1, B: true}, | 961 src: &Doubler{S: "s", I: 1, B: true}, |
962 want: &Doubler{S: "ss", I: 2, B: true}, | 962 want: &Doubler{S: "ss", I: 2, B: true}, |
963 }, | 963 }, |
964 { | 964 { |
965 desc: "save struct load props", | 965 desc: "save struct load props", |
966 src: &X0{S: "s", I: 1}, | 966 src: &X0{S: "s", I: 1}, |
967 » » want: gae.DSPropertyMap{ | 967 » » want: PropertyMap{ |
968 "S": {mp("s")}, | 968 "S": {mp("s")}, |
969 "I": {mp(1)}, | 969 "I": {mp(1)}, |
970 }, | 970 }, |
971 }, | 971 }, |
972 { | 972 { |
973 desc: "save props load struct", | 973 desc: "save props load struct", |
974 » » src: gae.DSPropertyMap{ | 974 » » src: PropertyMap{ |
975 "S": {mp("s")}, | 975 "S": {mp("s")}, |
976 "I": {mp(1)}, | 976 "I": {mp(1)}, |
977 }, | 977 }, |
978 want: &X0{S: "s", I: 1}, | 978 want: &X0{S: "s", I: 1}, |
979 }, | 979 }, |
980 { | 980 { |
981 desc: "nil-value props", | 981 desc: "nil-value props", |
982 » » src: gae.DSPropertyMap{ | 982 » » src: PropertyMap{ |
983 "I": {mp(nil)}, | 983 "I": {mp(nil)}, |
984 "B": {mp(nil)}, | 984 "B": {mp(nil)}, |
985 "S": {mp(nil)}, | 985 "S": {mp(nil)}, |
986 "F": {mp(nil)}, | 986 "F": {mp(nil)}, |
987 "K": {mp(nil)}, | 987 "K": {mp(nil)}, |
988 "T": {mp(nil)}, | 988 "T": {mp(nil)}, |
989 "J": { | 989 "J": { |
990 mp(nil), | 990 mp(nil), |
991 mp(7), | 991 mp(7), |
992 mp(nil), | 992 mp(nil), |
993 }, | 993 }, |
994 }, | 994 }, |
995 want: &struct { | 995 want: &struct { |
996 I int64 | 996 I int64 |
997 B bool | 997 B bool |
998 S string | 998 S string |
999 F float64 | 999 F float64 |
1000 » » » K gae.DSKey | 1000 » » » K Key |
1001 T time.Time | 1001 T time.Time |
1002 J []int64 | 1002 J []int64 |
1003 }{ | 1003 }{ |
1004 J: []int64{0, 7, 0}, | 1004 J: []int64{0, 7, 0}, |
1005 }, | 1005 }, |
1006 }, | 1006 }, |
1007 { | 1007 { |
1008 desc: "save outer load props", | 1008 desc: "save outer load props", |
1009 src: &Outer{ | 1009 src: &Outer{ |
1010 A: 1, | 1010 A: 1, |
1011 I: []Inner1{ | 1011 I: []Inner1{ |
1012 {10, "ten"}, | 1012 {10, "ten"}, |
1013 {20, "twenty"}, | 1013 {20, "twenty"}, |
1014 {30, "thirty"}, | 1014 {30, "thirty"}, |
1015 }, | 1015 }, |
1016 J: Inner2{ | 1016 J: Inner2{ |
1017 Y: 3.14, | 1017 Y: 3.14, |
1018 }, | 1018 }, |
1019 Inner3: Inner3{ | 1019 Inner3: Inner3{ |
1020 Z: true, | 1020 Z: true, |
1021 }, | 1021 }, |
1022 }, | 1022 }, |
1023 » » want: gae.DSPropertyMap{ | 1023 » » want: PropertyMap{ |
1024 "A": {mp(1)}, | 1024 "A": {mp(1)}, |
1025 "I.W": { | 1025 "I.W": { |
1026 mp(10), | 1026 mp(10), |
1027 mp(20), | 1027 mp(20), |
1028 mp(30), | 1028 mp(30), |
1029 }, | 1029 }, |
1030 "I.X": { | 1030 "I.X": { |
1031 mp("ten"), | 1031 mp("ten"), |
1032 mp("twenty"), | 1032 mp("twenty"), |
1033 mp("thirty"), | 1033 mp("thirty"), |
1034 }, | 1034 }, |
1035 "J.Y": {mp(3.14)}, | 1035 "J.Y": {mp(3.14)}, |
1036 "Z": {mp(true)}, | 1036 "Z": {mp(true)}, |
1037 }, | 1037 }, |
1038 }, | 1038 }, |
1039 { | 1039 { |
1040 desc: "save props load outer-equivalent", | 1040 desc: "save props load outer-equivalent", |
1041 » » src: gae.DSPropertyMap{ | 1041 » » src: PropertyMap{ |
1042 "A": {mp(1)}, | 1042 "A": {mp(1)}, |
1043 "I.W": { | 1043 "I.W": { |
1044 mp(10), | 1044 mp(10), |
1045 mp(20), | 1045 mp(20), |
1046 mp(30), | 1046 mp(30), |
1047 }, | 1047 }, |
1048 "I.X": { | 1048 "I.X": { |
1049 mp("ten"), | 1049 mp("ten"), |
1050 mp("twenty"), | 1050 mp("twenty"), |
1051 mp("thirty"), | 1051 mp("thirty"), |
(...skipping 29 matching lines...) Expand all Loading... |
1081 Y: 3.14, | 1081 Y: 3.14, |
1082 }, | 1082 }, |
1083 Inner3: Inner3{ | 1083 Inner3: Inner3{ |
1084 Z: true, | 1084 Z: true, |
1085 }, | 1085 }, |
1086 }, | 1086 }, |
1087 }, | 1087 }, |
1088 { | 1088 { |
1089 desc: "dotted names save", | 1089 desc: "dotted names save", |
1090 src: &Dotted{A: DottedA{B: DottedB{C: 88}}}, | 1090 src: &Dotted{A: DottedA{B: DottedB{C: 88}}}, |
1091 » » want: gae.DSPropertyMap{ | 1091 » » want: PropertyMap{ |
1092 "A0.A1.A2.B3.C4.C5": {mp(88)}, | 1092 "A0.A1.A2.B3.C4.C5": {mp(88)}, |
1093 }, | 1093 }, |
1094 }, | 1094 }, |
1095 { | 1095 { |
1096 desc: "dotted names load", | 1096 desc: "dotted names load", |
1097 » » src: gae.DSPropertyMap{ | 1097 » » src: PropertyMap{ |
1098 "A0.A1.A2.B3.C4.C5": {mp(99)}, | 1098 "A0.A1.A2.B3.C4.C5": {mp(99)}, |
1099 }, | 1099 }, |
1100 want: &Dotted{A: DottedA{B: DottedB{C: 99}}}, | 1100 want: &Dotted{A: DottedA{B: DottedB{C: 99}}}, |
1101 }, | 1101 }, |
1102 { | 1102 { |
1103 desc: "save struct load deriver", | 1103 desc: "save struct load deriver", |
1104 src: &X0{S: "s", I: 1}, | 1104 src: &X0{S: "s", I: 1}, |
1105 want: &Deriver{S: "s", Derived: "derived+s"}, | 1105 want: &Deriver{S: "s", Derived: "derived+s"}, |
1106 }, | 1106 }, |
1107 { | 1107 { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1153 want: &N0{ | 1153 want: &N0{ |
1154 X0: X0{S: "one", I: 2}, | 1154 X0: X0{S: "one", I: 2}, |
1155 Nonymous: X0{S: "four", I: 5}, | 1155 Nonymous: X0{S: "four", I: 5}, |
1156 Other: "other", | 1156 Other: "other", |
1157 }, | 1157 }, |
1158 }, | 1158 }, |
1159 { | 1159 { |
1160 desc: "exotic types", | 1160 desc: "exotic types", |
1161 src: &ExoticTypes{ | 1161 src: &ExoticTypes{ |
1162 BS: "sup", | 1162 BS: "sup", |
1163 » » » DSBS: gae.DSByteString("nerds"), | 1163 » » » DSBS: ByteString("nerds"), |
1164 }, | 1164 }, |
1165 want: &ExoticTypes{ | 1165 want: &ExoticTypes{ |
1166 BS: "sup", | 1166 BS: "sup", |
1167 » » » DSBS: gae.DSByteString("nerds"), | 1167 » » » DSBS: ByteString("nerds"), |
1168 }, | 1168 }, |
1169 }, | 1169 }, |
1170 { | 1170 { |
1171 desc: "underspecified types", | 1171 desc: "underspecified types", |
1172 src: &Underspecified{}, | 1172 src: &Underspecified{}, |
1173 plsErr: "non-concrete interface", | 1173 plsErr: "non-concrete interface", |
1174 }, | 1174 }, |
1175 { | 1175 { |
1176 desc: "mismatch (string)", | 1176 desc: "mismatch (string)", |
1177 » » src: gae.DSPropertyMap{ | 1177 » » src: PropertyMap{ |
1178 "K": {mp(199)}, | 1178 "K": {mp(199)}, |
1179 "S": {mp([]byte("cats"))}, | 1179 "S": {mp([]byte("cats"))}, |
1180 » » » "F": {mp(gae.DSByteString("nurbs"))}, | 1180 » » » "F": {mp(ByteString("nurbs"))}, |
1181 }, | 1181 }, |
1182 want: &MismatchTypes{}, | 1182 want: &MismatchTypes{}, |
1183 loadErr: "type mismatch", | 1183 loadErr: "type mismatch", |
1184 }, | 1184 }, |
1185 { | 1185 { |
1186 desc: "mismatch (float)", | 1186 desc: "mismatch (float)", |
1187 » » src: gae.DSPropertyMap{"F": {mp(gae.BSKey("wot"))}}, | 1187 » » src: PropertyMap{"F": {mp(blobstore.Key("wot"))}}, |
1188 want: &MismatchTypes{}, | 1188 want: &MismatchTypes{}, |
1189 loadErr: "type mismatch", | 1189 loadErr: "type mismatch", |
1190 }, | 1190 }, |
1191 { | 1191 { |
1192 desc: "mismatch (float/overflow)", | 1192 desc: "mismatch (float/overflow)", |
1193 » » src: gae.DSPropertyMap{"F": {mp(math.MaxFloat64)}}, | 1193 » » src: PropertyMap{"F": {mp(math.MaxFloat64)}}, |
1194 want: &MismatchTypes{}, | 1194 want: &MismatchTypes{}, |
1195 loadErr: "overflows", | 1195 loadErr: "overflows", |
1196 }, | 1196 }, |
1197 { | 1197 { |
1198 desc: "mismatch (key)", | 1198 desc: "mismatch (key)", |
1199 » » src: gae.DSPropertyMap{"K": {mp(false)}}, | 1199 » » src: PropertyMap{"K": {mp(false)}}, |
1200 want: &MismatchTypes{}, | 1200 want: &MismatchTypes{}, |
1201 loadErr: "type mismatch", | 1201 loadErr: "type mismatch", |
1202 }, | 1202 }, |
1203 { | 1203 { |
1204 desc: "mismatch (bool)", | 1204 desc: "mismatch (bool)", |
1205 » » src: gae.DSPropertyMap{"B": {mp(testKey0)}}, | 1205 » » src: PropertyMap{"B": {mp(testKey0)}}, |
1206 want: &MismatchTypes{}, | 1206 want: &MismatchTypes{}, |
1207 loadErr: "type mismatch", | 1207 loadErr: "type mismatch", |
1208 }, | 1208 }, |
1209 { | 1209 { |
1210 desc: "mismatch (time)", | 1210 desc: "mismatch (time)", |
1211 » » src: gae.DSPropertyMap{"T": {mp(gae.DSGeoPoint{})}}, | 1211 » » src: PropertyMap{"T": {mp(GeoPoint{})}}, |
1212 want: &MismatchTypes{}, | 1212 want: &MismatchTypes{}, |
1213 loadErr: "type mismatch", | 1213 loadErr: "type mismatch", |
1214 }, | 1214 }, |
1215 { | 1215 { |
1216 desc: "mismatch (geopoint)", | 1216 desc: "mismatch (geopoint)", |
1217 » » src: gae.DSPropertyMap{"G": {mp(time.Now().UTC())}}, | 1217 » » src: PropertyMap{"G": {mp(time.Now().UTC())}}, |
1218 want: &MismatchTypes{}, | 1218 want: &MismatchTypes{}, |
1219 loadErr: "type mismatch", | 1219 loadErr: "type mismatch", |
1220 }, | 1220 }, |
1221 { | 1221 { |
1222 desc: "slice of structs", | 1222 desc: "slice of structs", |
1223 src: &N1{ | 1223 src: &N1{ |
1224 X0: X0{S: "one", I: 2, i: 3}, | 1224 X0: X0{S: "one", I: 2, i: 3}, |
1225 Nonymous: []X0{ | 1225 Nonymous: []X0{ |
1226 {S: "four", I: 5, i: 6}, | 1226 {S: "four", I: 5, i: 6}, |
1227 {S: "seven", I: 8, i: 9}, | 1227 {S: "seven", I: 8, i: 9}, |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1318 Blue: N1{ | 1318 Blue: N1{ |
1319 X0: X0{S: "bleu"}, | 1319 X0: X0{S: "bleu"}, |
1320 Nonymous: []X0{ | 1320 Nonymous: []X0{ |
1321 {S: "blu0"}, | 1321 {S: "blu0"}, |
1322 {S: "blu1"}, | 1322 {S: "blu1"}, |
1323 {S: "blu2"}, | 1323 {S: "blu2"}, |
1324 {S: "blu3"}, | 1324 {S: "blu3"}, |
1325 }, | 1325 }, |
1326 }, | 1326 }, |
1327 }, | 1327 }, |
1328 » » want: gae.DSPropertyMap{ | 1328 » » want: PropertyMap{ |
1329 "red.S": {mp("rouge")}, | 1329 "red.S": {mp("rouge")}, |
1330 "red.I": {mp(0)}, | 1330 "red.I": {mp(0)}, |
1331 "red.Nonymous.S": {mp("rosso0"), mp("rosso1")}, | 1331 "red.Nonymous.S": {mp("rosso0"), mp("rosso1")}, |
1332 "red.Nonymous.I": {mp(0), mp(0)}, | 1332 "red.Nonymous.I": {mp(0), mp(0)}, |
1333 "red.Other": {mp("")}, | 1333 "red.Other": {mp("")}, |
1334 "green.S": {mp("vert")}, | 1334 "green.S": {mp("vert")}, |
1335 "green.I": {mp(0)}, | 1335 "green.I": {mp(0)}, |
1336 "green.Nonymous.S": {mp("verde0"), mp("verde1"), mp("ver
de2")}, | 1336 "green.Nonymous.S": {mp("verde0"), mp("verde1"), mp("ver
de2")}, |
1337 "green.Nonymous.I": {mp(0), mp(0), mp(0)}, | 1337 "green.Nonymous.I": {mp(0), mp(0), mp(0)}, |
1338 "green.Other": {mp("")}, | 1338 "green.Other": {mp("")}, |
1339 "Blue.S": {mp("bleu")}, | 1339 "Blue.S": {mp("bleu")}, |
1340 "Blue.I": {mp(0)}, | 1340 "Blue.I": {mp(0)}, |
1341 "Blue.Nonymous.S": {mp("blu0"), mp("blu1"), mp("blu2"),
mp("blu3")}, | 1341 "Blue.Nonymous.S": {mp("blu0"), mp("blu1"), mp("blu2"),
mp("blu3")}, |
1342 "Blue.Nonymous.I": {mp(0), mp(0), mp(0), mp(0)}, | 1342 "Blue.Nonymous.I": {mp(0), mp(0), mp(0), mp(0)}, |
1343 "Blue.Other": {mp("")}, | 1343 "Blue.Other": {mp("")}, |
1344 }, | 1344 }, |
1345 }, | 1345 }, |
1346 { | 1346 { |
1347 desc: "save props load structs with ragged fields", | 1347 desc: "save props load structs with ragged fields", |
1348 » » src: gae.DSPropertyMap{ | 1348 » » src: PropertyMap{ |
1349 "red.S": {mp("rot")}, | 1349 "red.S": {mp("rot")}, |
1350 "green.Nonymous.I": {mp(10), mp(11), mp(12), mp(13)}, | 1350 "green.Nonymous.I": {mp(10), mp(11), mp(12), mp(13)}, |
1351 "Blue.Nonymous.S": {mp("blau0"), mp("blau1"), mp("blau2
")}, | 1351 "Blue.Nonymous.S": {mp("blau0"), mp("blau1"), mp("blau2
")}, |
1352 "Blue.Nonymous.I": {mp(20), mp(21)}, | 1352 "Blue.Nonymous.I": {mp(20), mp(21)}, |
1353 }, | 1353 }, |
1354 want: &N2{ | 1354 want: &N2{ |
1355 N1: N1{ | 1355 N1: N1{ |
1356 X0: X0{S: "rot"}, | 1356 X0: X0{S: "rot"}, |
1357 }, | 1357 }, |
1358 Green: N1{ | 1358 Green: N1{ |
(...skipping 18 matching lines...) Expand all Loading... |
1377 src: &struct { | 1377 src: &struct { |
1378 A struct { | 1378 A struct { |
1379 X string `gae:",noindex"` | 1379 X string `gae:",noindex"` |
1380 Y string | 1380 Y string |
1381 } `gae:",noindex"` | 1381 } `gae:",noindex"` |
1382 B struct { | 1382 B struct { |
1383 X string `gae:",noindex"` | 1383 X string `gae:",noindex"` |
1384 Y string | 1384 Y string |
1385 } | 1385 } |
1386 }{}, | 1386 }{}, |
1387 » » want: gae.DSPropertyMap{ | 1387 » » want: PropertyMap{ |
1388 "B.Y": {mp("")}, | 1388 "B.Y": {mp("")}, |
1389 "A.X": {mpNI("")}, | 1389 "A.X": {mpNI("")}, |
1390 "A.Y": {mpNI("")}, | 1390 "A.Y": {mpNI("")}, |
1391 "B.X": {mpNI("")}, | 1391 "B.X": {mpNI("")}, |
1392 }, | 1392 }, |
1393 }, | 1393 }, |
1394 { | 1394 { |
1395 desc: "embedded struct with name override", | 1395 desc: "embedded struct with name override", |
1396 src: &struct { | 1396 src: &struct { |
1397 Inner1 `gae:"foo"` | 1397 Inner1 `gae:"foo"` |
1398 }{}, | 1398 }{}, |
1399 » » want: gae.DSPropertyMap{ | 1399 » » want: PropertyMap{ |
1400 "foo.W": {mp(0)}, | 1400 "foo.W": {mp(0)}, |
1401 "foo.X": {mp("")}, | 1401 "foo.X": {mp("")}, |
1402 }, | 1402 }, |
1403 }, | 1403 }, |
1404 { | 1404 { |
1405 desc: "slice of slices", | 1405 desc: "slice of slices", |
1406 src: &SliceOfSlices{}, | 1406 src: &SliceOfSlices{}, |
1407 plsErr: `flattening nested structs leads to a slice of slices: f
ield "S"`, | 1407 plsErr: `flattening nested structs leads to a slice of slices: f
ield "S"`, |
1408 }, | 1408 }, |
1409 { | 1409 { |
1410 desc: "recursive struct", | 1410 desc: "recursive struct", |
1411 src: &Recursive{}, | 1411 src: &Recursive{}, |
1412 plsErr: `field "R" is recursively defined`, | 1412 plsErr: `field "R" is recursively defined`, |
1413 }, | 1413 }, |
1414 { | 1414 { |
1415 desc: "mutually recursive struct", | 1415 desc: "mutually recursive struct", |
1416 src: &MutuallyRecursive0{}, | 1416 src: &MutuallyRecursive0{}, |
1417 plsErr: `field "R" has problem: field "R" is recursively defined
`, | 1417 plsErr: `field "R" has problem: field "R" is recursively defined
`, |
1418 }, | 1418 }, |
1419 { | 1419 { |
1420 desc: "non-exported struct fields", | 1420 desc: "non-exported struct fields", |
1421 src: &struct { | 1421 src: &struct { |
1422 i, J int64 | 1422 i, J int64 |
1423 }{i: 1, J: 2}, | 1423 }{i: 1, J: 2}, |
1424 » » want: gae.DSPropertyMap{ | 1424 » » want: PropertyMap{ |
1425 "J": {mp(2)}, | 1425 "J": {mp(2)}, |
1426 }, | 1426 }, |
1427 }, | 1427 }, |
1428 { | 1428 { |
1429 desc: "json.RawMessage", | 1429 desc: "json.RawMessage", |
1430 src: &struct { | 1430 src: &struct { |
1431 J json.RawMessage | 1431 J json.RawMessage |
1432 }{ | 1432 }{ |
1433 J: json.RawMessage("rawr"), | 1433 J: json.RawMessage("rawr"), |
1434 }, | 1434 }, |
1435 » » want: gae.DSPropertyMap{ | 1435 » » want: PropertyMap{ |
1436 "J": {mp([]byte("rawr"))}, | 1436 "J": {mp([]byte("rawr"))}, |
1437 }, | 1437 }, |
1438 }, | 1438 }, |
1439 { | 1439 { |
1440 desc: "json.RawMessage to myBlob", | 1440 desc: "json.RawMessage to myBlob", |
1441 src: &struct { | 1441 src: &struct { |
1442 B json.RawMessage | 1442 B json.RawMessage |
1443 }{ | 1443 }{ |
1444 B: json.RawMessage("rawr"), | 1444 B: json.RawMessage("rawr"), |
1445 }, | 1445 }, |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1498 | 1498 |
1499 checkErr := func(actual interface{}, expected string) bool { | 1499 checkErr := func(actual interface{}, expected string) bool { |
1500 So(actual, ShouldErrLike, expected) | 1500 So(actual, ShouldErrLike, expected) |
1501 return expected != "" | 1501 return expected != "" |
1502 } | 1502 } |
1503 | 1503 |
1504 Convey("Test round-trip", t, func() { | 1504 Convey("Test round-trip", t, func() { |
1505 for _, tc := range testCases { | 1505 for _, tc := range testCases { |
1506 tc := tc | 1506 tc := tc |
1507 Convey(tc.desc, func() { | 1507 Convey(tc.desc, func() { |
1508 » » » » pls, ok := tc.src.(gae.DSPropertyLoadSaver) | 1508 » » » » pls, ok := tc.src.(PropertyLoadSaver) |
1509 if !ok { | 1509 if !ok { |
1510 pls = GetPLS(tc.src) | 1510 pls = GetPLS(tc.src) |
1511 } | 1511 } |
1512 if checkErr(pls.Problem(), tc.plsErr) { | 1512 if checkErr(pls.Problem(), tc.plsErr) { |
1513 return | 1513 return |
1514 } | 1514 } |
1515 So(pls, ShouldNotBeNil) | 1515 So(pls, ShouldNotBeNil) |
1516 | 1516 |
1517 savedProps, err := pls.Save(false) | 1517 savedProps, err := pls.Save(false) |
1518 if checkErr(err, tc.saveErr) { | 1518 if checkErr(err, tc.saveErr) { |
1519 return | 1519 return |
1520 } | 1520 } |
1521 So(savedProps, ShouldNotBeNil) | 1521 So(savedProps, ShouldNotBeNil) |
1522 | 1522 |
1523 if tc.actualNoIndex { | 1523 if tc.actualNoIndex { |
1524 for _, props := range savedProps { | 1524 for _, props := range savedProps { |
1525 » » » » » » So(props[0].IndexSetting(), Shou
ldEqual, gae.NoIndex) | 1525 » » » » » » So(props[0].IndexSetting(), Shou
ldEqual, NoIndex) |
1526 return | 1526 return |
1527 } | 1527 } |
1528 So(true, ShouldBeFalse) // shouldn't get
here | 1528 So(true, ShouldBeFalse) // shouldn't get
here |
1529 } | 1529 } |
1530 | 1530 |
1531 var got interface{} | 1531 var got interface{} |
1532 » » » » if _, ok := tc.want.(gae.DSPropertyMap); ok { | 1532 » » » » if _, ok := tc.want.(PropertyMap); ok { |
1533 » » » » » pls = gae.DSPropertyMap{} | 1533 » » » » » pls = PropertyMap{} |
1534 got = pls | 1534 got = pls |
1535 } else { | 1535 } else { |
1536 got = reflect.New(reflect.TypeOf(tc.want
).Elem()).Interface() | 1536 got = reflect.New(reflect.TypeOf(tc.want
).Elem()).Interface() |
1537 » » » » » if pls, ok = got.(gae.DSPropertyLoadSave
r); !ok { | 1537 » » » » » if pls, ok = got.(PropertyLoadSaver); !o
k { |
1538 pls = GetPLS(got) | 1538 pls = GetPLS(got) |
1539 } | 1539 } |
1540 } | 1540 } |
1541 | 1541 |
1542 if checkErr(pls.Problem(), tc.plsLoadErr) { | 1542 if checkErr(pls.Problem(), tc.plsLoadErr) { |
1543 return | 1543 return |
1544 } | 1544 } |
1545 So(pls, ShouldNotBeNil) | 1545 So(pls, ShouldNotBeNil) |
1546 | 1546 |
1547 err = pls.Load(savedProps) | 1547 err = pls.Load(savedProps) |
(...skipping 29 matching lines...) Expand all Loading... |
1577 | 1577 |
1578 val, err = pls.GetMeta("kind") | 1578 val, err = pls.GetMeta("kind") |
1579 So(err, ShouldBeNil) | 1579 So(err, ShouldBeNil) |
1580 So(val, ShouldEqual, "whatnow") | 1580 So(val, ShouldEqual, "whatnow") |
1581 }) | 1581 }) |
1582 | 1582 |
1583 Convey("Getting something not there is an error", func() { | 1583 Convey("Getting something not there is an error", func() { |
1584 o := &N0{ID: 100} | 1584 o := &N0{ID: 100} |
1585 pls := GetPLS(o) | 1585 pls := GetPLS(o) |
1586 _, err := pls.GetMeta("wat") | 1586 _, err := pls.GetMeta("wat") |
1587 » » » So(err, ShouldEqual, gae.ErrDSMetaFieldUnset) | 1587 » » » So(err, ShouldEqual, ErrMetaFieldUnset) |
1588 }) | 1588 }) |
1589 | 1589 |
1590 Convey("getting/setting from a bad struct is an error", func() { | 1590 Convey("getting/setting from a bad struct is an error", func() { |
1591 o := &Recursive{} | 1591 o := &Recursive{} |
1592 pls := GetPLS(o) | 1592 pls := GetPLS(o) |
1593 _, err := pls.GetMeta("wat") | 1593 _, err := pls.GetMeta("wat") |
1594 So(err, ShouldNotBeNil) | 1594 So(err, ShouldNotBeNil) |
1595 | 1595 |
1596 err = pls.SetMeta("wat", 100) | 1596 err = pls.SetMeta("wat", 100) |
1597 So(err, ShouldNotBeNil) | 1597 So(err, ShouldNotBeNil) |
1598 }) | 1598 }) |
1599 | 1599 |
1600 Convey("can assign values to exported special fields", func() { | 1600 Convey("can assign values to exported special fields", func() { |
1601 o := &N0{ID: 100} | 1601 o := &N0{ID: 100} |
1602 pls := GetPLS(o) | 1602 pls := GetPLS(o) |
1603 err := pls.SetMeta("id", int64(200)) | 1603 err := pls.SetMeta("id", int64(200)) |
1604 So(err, ShouldBeNil) | 1604 So(err, ShouldBeNil) |
1605 So(o.ID, ShouldEqual, 200) | 1605 So(o.ID, ShouldEqual, 200) |
1606 | 1606 |
1607 }) | 1607 }) |
1608 | 1608 |
1609 Convey("assigning to unsassiagnable fields is a simple error", f
unc() { | 1609 Convey("assigning to unsassiagnable fields is a simple error", f
unc() { |
1610 o := &N0{ID: 100} | 1610 o := &N0{ID: 100} |
1611 pls := GetPLS(o) | 1611 pls := GetPLS(o) |
1612 err := pls.SetMeta("kind", "hi") | 1612 err := pls.SetMeta("kind", "hi") |
1613 So(err.Error(), ShouldContainSubstring, "unexported fiel
d") | 1613 So(err.Error(), ShouldContainSubstring, "unexported fiel
d") |
1614 | 1614 |
1615 err = pls.SetMeta("noob", "hi") | 1615 err = pls.SetMeta("noob", "hi") |
1616 » » » So(err, ShouldEqual, gae.ErrDSMetaFieldUnset) | 1616 » » » So(err, ShouldEqual, ErrMetaFieldUnset) |
1617 }) | 1617 }) |
1618 }) | 1618 }) |
1619 | 1619 |
1620 Convey("StructPLS Miscellaneous", t, func() { | 1620 Convey("StructPLS Miscellaneous", t, func() { |
1621 Convey("multiple overlapping fields is an error", func() { | 1621 Convey("multiple overlapping fields is an error", func() { |
1622 o := &BadSpecial{} | 1622 o := &BadSpecial{} |
1623 pls := GetPLS(o) | 1623 pls := GetPLS(o) |
1624 err := pls.Load(nil) | 1624 err := pls.Load(nil) |
1625 So(err, ShouldErrLike, "multiple times") | 1625 So(err, ShouldErrLike, "multiple times") |
1626 e := pls.Problem() | 1626 e := pls.Problem() |
1627 _, err = pls.Save(true) | 1627 _, err = pls.Save(true) |
1628 So(err, ShouldEqual, e) | 1628 So(err, ShouldEqual, e) |
1629 err = pls.Load(nil) | 1629 err = pls.Load(nil) |
1630 So(err, ShouldEqual, e) | 1630 So(err, ShouldEqual, e) |
1631 }) | 1631 }) |
1632 | 1632 |
1633 Convey("empty property names are invalid", func() { | 1633 Convey("empty property names are invalid", func() { |
1634 So(validPropertyName(""), ShouldBeFalse) | 1634 So(validPropertyName(""), ShouldBeFalse) |
1635 }) | 1635 }) |
1636 | 1636 |
1637 Convey("attempting to get a PLS for a non *struct is an error",
func() { | 1637 Convey("attempting to get a PLS for a non *struct is an error",
func() { |
1638 pls := GetPLS((*[]string)(nil)) | 1638 pls := GetPLS((*[]string)(nil)) |
1639 » » » So(pls.Problem(), ShouldEqual, gae.ErrDSInvalidEntityTyp
e) | 1639 » » » So(pls.Problem(), ShouldEqual, ErrInvalidEntityType) |
1640 }) | 1640 }) |
1641 | 1641 |
1642 Convey("convertible meta default types", func() { | 1642 Convey("convertible meta default types", func() { |
1643 type OKDefaults struct { | 1643 type OKDefaults struct { |
1644 When string `gae:"$when,tomorrow"` | 1644 When string `gae:"$when,tomorrow"` |
1645 Amount int64 `gae:"$amt,100"` | 1645 Amount int64 `gae:"$amt,100"` |
1646 } | 1646 } |
1647 pls := GetPLS(&OKDefaults{}) | 1647 pls := GetPLS(&OKDefaults{}) |
1648 So(pls.Problem(), ShouldBeNil) | 1648 So(pls.Problem(), ShouldBeNil) |
1649 | 1649 |
1650 v, err := pls.GetMeta("when") | 1650 v, err := pls.GetMeta("when") |
1651 So(err, ShouldBeNil) | 1651 So(err, ShouldBeNil) |
1652 So(v, ShouldEqual, "tomorrow") | 1652 So(v, ShouldEqual, "tomorrow") |
1653 | 1653 |
1654 v, err = pls.GetMeta("amt") | 1654 v, err = pls.GetMeta("amt") |
1655 So(err, ShouldBeNil) | 1655 So(err, ShouldBeNil) |
1656 So(v, ShouldEqual, int64(100)) | 1656 So(v, ShouldEqual, int64(100)) |
1657 }) | 1657 }) |
1658 | 1658 |
1659 Convey("meta fields can be saved", func() { | 1659 Convey("meta fields can be saved", func() { |
1660 type OKDefaults struct { | 1660 type OKDefaults struct { |
1661 When string `gae:"$when,tomorrow"` | 1661 When string `gae:"$when,tomorrow"` |
1662 Amount int64 `gae:"$amt,100"` | 1662 Amount int64 `gae:"$amt,100"` |
1663 } | 1663 } |
1664 pls := GetPLS(&OKDefaults{}) | 1664 pls := GetPLS(&OKDefaults{}) |
1665 pm, err := pls.Save(true) | 1665 pm, err := pls.Save(true) |
1666 So(err, ShouldBeNil) | 1666 So(err, ShouldBeNil) |
1667 » » » So(pm, ShouldResemble, gae.DSPropertyMap{ | 1667 » » » So(pm, ShouldResemble, PropertyMap{ |
1668 » » » » "$when": {gae.MkDSPropertyNI("tomorrow")}, | 1668 » » » » "$when": {mpNI("tomorrow")}, |
1669 » » » » "$amt": {gae.MkDSPropertyNI(100)}, | 1669 » » » » "$amt": {mpNI(100)}, |
1670 }) | 1670 }) |
1671 | 1671 |
1672 v, err := pm.GetMeta("when") | 1672 v, err := pm.GetMeta("when") |
1673 So(err, ShouldBeNil) | 1673 So(err, ShouldBeNil) |
1674 So(v, ShouldEqual, "tomorrow") | 1674 So(v, ShouldEqual, "tomorrow") |
1675 | 1675 |
1676 v, err = pm.GetMeta("amt") | 1676 v, err = pm.GetMeta("amt") |
1677 So(err, ShouldBeNil) | 1677 So(err, ShouldBeNil) |
1678 So(v, ShouldEqual, int64(100)) | 1678 So(v, ShouldEqual, int64(100)) |
1679 }) | 1679 }) |
(...skipping 29 matching lines...) Expand all Loading... |
1709 | 1709 |
1710 Convey("Bad default meta type", func() { | 1710 Convey("Bad default meta type", func() { |
1711 type BadDefault struct { | 1711 type BadDefault struct { |
1712 Val time.Time `gae:"$meta,tomorrow"` | 1712 Val time.Time `gae:"$meta,tomorrow"` |
1713 } | 1713 } |
1714 pls := GetPLS(&BadDefault{}) | 1714 pls := GetPLS(&BadDefault{}) |
1715 So(pls.Problem().Error(), ShouldContainSubstring, "bad t
ype") | 1715 So(pls.Problem().Error(), ShouldContainSubstring, "bad t
ype") |
1716 }) | 1716 }) |
1717 }) | 1717 }) |
1718 } | 1718 } |
OLD | NEW |