You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

910 lines
20 KiB

  1. // Copyright 2016 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package datastore
  15. import (
  16. "reflect"
  17. "testing"
  18. "time"
  19. "cloud.google.com/go/internal/testutil"
  20. pb "google.golang.org/genproto/googleapis/datastore/v1"
  21. )
  22. type Simple struct {
  23. I int64
  24. }
  25. type SimpleWithTag struct {
  26. I int64 `datastore:"II"`
  27. }
  28. type NestedSimpleWithTag struct {
  29. A SimpleWithTag `datastore:"AA"`
  30. }
  31. type NestedSliceOfSimple struct {
  32. A []Simple
  33. }
  34. type SimpleTwoFields struct {
  35. S string
  36. SS string
  37. }
  38. type NestedSimpleAnonymous struct {
  39. Simple
  40. X string
  41. }
  42. type NestedSimple struct {
  43. A Simple
  44. I int
  45. }
  46. type NestedSimple1 struct {
  47. A Simple
  48. X string
  49. }
  50. type NestedSimple2X struct {
  51. AA NestedSimple
  52. A SimpleTwoFields
  53. S string
  54. }
  55. type BDotB struct {
  56. B string `datastore:"B.B"`
  57. }
  58. type ABDotB struct {
  59. A BDotB
  60. }
  61. type MultiAnonymous struct {
  62. Simple
  63. SimpleTwoFields
  64. X string
  65. }
  66. func TestLoadEntityNestedLegacy(t *testing.T) {
  67. testCases := []struct {
  68. desc string
  69. src *pb.Entity
  70. want interface{}
  71. }{
  72. {
  73. desc: "nested",
  74. src: &pb.Entity{
  75. Key: keyToProto(testKey0),
  76. Properties: map[string]*pb.Value{
  77. "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}},
  78. "A.I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
  79. },
  80. },
  81. want: &NestedSimple1{
  82. A: Simple{I: 2},
  83. X: "two",
  84. },
  85. },
  86. {
  87. desc: "nested with tag",
  88. src: &pb.Entity{
  89. Key: keyToProto(testKey0),
  90. Properties: map[string]*pb.Value{
  91. "AA.II": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
  92. },
  93. },
  94. want: &NestedSimpleWithTag{
  95. A: SimpleWithTag{I: 2},
  96. },
  97. },
  98. {
  99. desc: "nested with anonymous struct field",
  100. src: &pb.Entity{
  101. Key: keyToProto(testKey0),
  102. Properties: map[string]*pb.Value{
  103. "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}},
  104. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
  105. },
  106. },
  107. want: &NestedSimpleAnonymous{
  108. Simple: Simple{I: 2},
  109. X: "two",
  110. },
  111. },
  112. {
  113. desc: "nested with dotted field tag",
  114. src: &pb.Entity{
  115. Key: keyToProto(testKey0),
  116. Properties: map[string]*pb.Value{
  117. "A.B.B": {ValueType: &pb.Value_StringValue{StringValue: "bb"}},
  118. },
  119. },
  120. want: &ABDotB{
  121. A: BDotB{
  122. B: "bb",
  123. },
  124. },
  125. },
  126. {
  127. desc: "nested with multiple anonymous fields",
  128. src: &pb.Entity{
  129. Key: keyToProto(testKey0),
  130. Properties: map[string]*pb.Value{
  131. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
  132. "S": {ValueType: &pb.Value_StringValue{StringValue: "S"}},
  133. "SS": {ValueType: &pb.Value_StringValue{StringValue: "s"}},
  134. "X": {ValueType: &pb.Value_StringValue{StringValue: "s"}},
  135. },
  136. },
  137. want: &MultiAnonymous{
  138. Simple: Simple{I: 3},
  139. SimpleTwoFields: SimpleTwoFields{S: "S", SS: "s"},
  140. X: "s",
  141. },
  142. },
  143. }
  144. for _, tc := range testCases {
  145. dst := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface()
  146. err := loadEntityProto(dst, tc.src)
  147. if err != nil {
  148. t.Errorf("loadEntityProto: %s: %v", tc.desc, err)
  149. continue
  150. }
  151. if !testutil.Equal(tc.want, dst) {
  152. t.Errorf("%s: compare:\ngot: %#v\nwant: %#v", tc.desc, dst, tc.want)
  153. }
  154. }
  155. }
  156. type WithKey struct {
  157. X string
  158. I int
  159. K *Key `datastore:"__key__"`
  160. }
  161. type NestedWithKey struct {
  162. Y string
  163. N WithKey
  164. }
  165. var (
  166. incompleteKey = newKey("", nil)
  167. invalidKey = newKey("s", incompleteKey)
  168. )
  169. func TestLoadEntityNested(t *testing.T) {
  170. testCases := []struct {
  171. desc string
  172. src *pb.Entity
  173. want interface{}
  174. }{
  175. {
  176. desc: "nested basic",
  177. src: &pb.Entity{
  178. Properties: map[string]*pb.Value{
  179. "A": {ValueType: &pb.Value_EntityValue{
  180. EntityValue: &pb.Entity{
  181. Properties: map[string]*pb.Value{
  182. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
  183. },
  184. },
  185. }},
  186. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 10}},
  187. },
  188. },
  189. want: &NestedSimple{
  190. A: Simple{I: 3},
  191. I: 10,
  192. },
  193. },
  194. {
  195. desc: "nested with struct tags",
  196. src: &pb.Entity{
  197. Properties: map[string]*pb.Value{
  198. "AA": {ValueType: &pb.Value_EntityValue{
  199. EntityValue: &pb.Entity{
  200. Properties: map[string]*pb.Value{
  201. "II": {ValueType: &pb.Value_IntegerValue{IntegerValue: 1}},
  202. },
  203. },
  204. }},
  205. },
  206. },
  207. want: &NestedSimpleWithTag{
  208. A: SimpleWithTag{I: 1},
  209. },
  210. },
  211. {
  212. desc: "nested 2x",
  213. src: &pb.Entity{
  214. Properties: map[string]*pb.Value{
  215. "AA": {ValueType: &pb.Value_EntityValue{
  216. EntityValue: &pb.Entity{
  217. Properties: map[string]*pb.Value{
  218. "A": {ValueType: &pb.Value_EntityValue{
  219. EntityValue: &pb.Entity{
  220. Properties: map[string]*pb.Value{
  221. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
  222. },
  223. },
  224. }},
  225. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 1}},
  226. },
  227. },
  228. }},
  229. "A": {ValueType: &pb.Value_EntityValue{
  230. EntityValue: &pb.Entity{
  231. Properties: map[string]*pb.Value{
  232. "S": {ValueType: &pb.Value_StringValue{StringValue: "S"}},
  233. "SS": {ValueType: &pb.Value_StringValue{StringValue: "s"}},
  234. },
  235. },
  236. }},
  237. "S": {ValueType: &pb.Value_StringValue{StringValue: "SS"}},
  238. },
  239. },
  240. want: &NestedSimple2X{
  241. AA: NestedSimple{
  242. A: Simple{I: 3},
  243. I: 1,
  244. },
  245. A: SimpleTwoFields{S: "S", SS: "s"},
  246. S: "SS",
  247. },
  248. },
  249. {
  250. desc: "nested anonymous",
  251. src: &pb.Entity{
  252. Properties: map[string]*pb.Value{
  253. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
  254. "X": {ValueType: &pb.Value_StringValue{StringValue: "SomeX"}},
  255. },
  256. },
  257. want: &NestedSimpleAnonymous{
  258. Simple: Simple{I: 3},
  259. X: "SomeX",
  260. },
  261. },
  262. {
  263. desc: "nested simple with slice",
  264. src: &pb.Entity{
  265. Properties: map[string]*pb.Value{
  266. "A": {ValueType: &pb.Value_ArrayValue{
  267. ArrayValue: &pb.ArrayValue{
  268. Values: []*pb.Value{
  269. {ValueType: &pb.Value_EntityValue{
  270. EntityValue: &pb.Entity{
  271. Properties: map[string]*pb.Value{
  272. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
  273. },
  274. },
  275. }},
  276. {ValueType: &pb.Value_EntityValue{
  277. EntityValue: &pb.Entity{
  278. Properties: map[string]*pb.Value{
  279. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 4}},
  280. },
  281. },
  282. }},
  283. },
  284. },
  285. }},
  286. },
  287. },
  288. want: &NestedSliceOfSimple{
  289. A: []Simple{{I: 3}, {I: 4}},
  290. },
  291. },
  292. {
  293. desc: "nested with multiple anonymous fields",
  294. src: &pb.Entity{
  295. Properties: map[string]*pb.Value{
  296. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 3}},
  297. "S": {ValueType: &pb.Value_StringValue{StringValue: "S"}},
  298. "SS": {ValueType: &pb.Value_StringValue{StringValue: "s"}},
  299. "X": {ValueType: &pb.Value_StringValue{StringValue: "ss"}},
  300. },
  301. },
  302. want: &MultiAnonymous{
  303. Simple: Simple{I: 3},
  304. SimpleTwoFields: SimpleTwoFields{S: "S", SS: "s"},
  305. X: "ss",
  306. },
  307. },
  308. {
  309. desc: "nested with dotted field tag",
  310. src: &pb.Entity{
  311. Properties: map[string]*pb.Value{
  312. "A": {ValueType: &pb.Value_EntityValue{
  313. EntityValue: &pb.Entity{
  314. Properties: map[string]*pb.Value{
  315. "B.B": {ValueType: &pb.Value_StringValue{StringValue: "bb"}},
  316. },
  317. },
  318. }},
  319. },
  320. },
  321. want: &ABDotB{
  322. A: BDotB{
  323. B: "bb",
  324. },
  325. },
  326. },
  327. {
  328. desc: "nested entity with key",
  329. src: &pb.Entity{
  330. Key: keyToProto(testKey0),
  331. Properties: map[string]*pb.Value{
  332. "Y": {ValueType: &pb.Value_StringValue{StringValue: "yyy"}},
  333. "N": {ValueType: &pb.Value_EntityValue{
  334. EntityValue: &pb.Entity{
  335. Key: keyToProto(testKey1a),
  336. Properties: map[string]*pb.Value{
  337. "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}},
  338. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
  339. },
  340. },
  341. }},
  342. },
  343. },
  344. want: &NestedWithKey{
  345. Y: "yyy",
  346. N: WithKey{
  347. X: "two",
  348. I: 2,
  349. K: testKey1a,
  350. },
  351. },
  352. },
  353. {
  354. desc: "nested entity with invalid key",
  355. src: &pb.Entity{
  356. Key: keyToProto(testKey0),
  357. Properties: map[string]*pb.Value{
  358. "Y": {ValueType: &pb.Value_StringValue{StringValue: "yyy"}},
  359. "N": {ValueType: &pb.Value_EntityValue{
  360. EntityValue: &pb.Entity{
  361. Key: keyToProto(invalidKey),
  362. Properties: map[string]*pb.Value{
  363. "X": {ValueType: &pb.Value_StringValue{StringValue: "two"}},
  364. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
  365. },
  366. },
  367. }},
  368. },
  369. },
  370. want: &NestedWithKey{
  371. Y: "yyy",
  372. N: WithKey{
  373. X: "two",
  374. I: 2,
  375. K: invalidKey,
  376. },
  377. },
  378. },
  379. }
  380. for _, tc := range testCases {
  381. dst := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface()
  382. err := loadEntityProto(dst, tc.src)
  383. if err != nil {
  384. t.Errorf("loadEntityProto: %s: %v", tc.desc, err)
  385. continue
  386. }
  387. if !testutil.Equal(tc.want, dst) {
  388. t.Errorf("%s: compare:\ngot: %#v\nwant: %#v", tc.desc, dst, tc.want)
  389. }
  390. }
  391. }
  392. type NestedStructPtrs struct {
  393. *SimpleTwoFields
  394. Nest *SimpleTwoFields
  395. TwiceNest *NestedSimple2
  396. I int
  397. }
  398. type NestedSimple2 struct {
  399. A *Simple
  400. I int
  401. }
  402. func TestAlreadyPopulatedDst(t *testing.T) {
  403. testCases := []struct {
  404. desc string
  405. src *pb.Entity
  406. dst interface{}
  407. want interface{}
  408. }{
  409. {
  410. desc: "simple already populated, nil properties",
  411. src: &pb.Entity{
  412. Key: keyToProto(testKey0),
  413. Properties: map[string]*pb.Value{
  414. "I": {ValueType: &pb.Value_NullValue{}},
  415. },
  416. },
  417. dst: &Simple{
  418. I: 12,
  419. },
  420. want: &Simple{},
  421. },
  422. {
  423. desc: "nested structs already populated",
  424. src: &pb.Entity{
  425. Key: keyToProto(testKey0),
  426. Properties: map[string]*pb.Value{
  427. "SS": {ValueType: &pb.Value_StringValue{StringValue: "world"}},
  428. },
  429. },
  430. dst: &SimpleTwoFields{S: "hello" /* SS: "" */},
  431. want: &SimpleTwoFields{S: "hello", SS: "world"},
  432. },
  433. {
  434. desc: "nested structs already populated, pValues nil",
  435. src: &pb.Entity{
  436. Key: keyToProto(testKey0),
  437. Properties: map[string]*pb.Value{
  438. "S": {ValueType: &pb.Value_NullValue{}},
  439. "SS": {ValueType: &pb.Value_StringValue{StringValue: "ss hello"}},
  440. "Nest": {ValueType: &pb.Value_NullValue{}},
  441. "TwiceNest": {ValueType: &pb.Value_EntityValue{
  442. EntityValue: &pb.Entity{
  443. Properties: map[string]*pb.Value{
  444. "A": {ValueType: &pb.Value_NullValue{}},
  445. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 2}},
  446. },
  447. },
  448. }},
  449. "I": {ValueType: &pb.Value_IntegerValue{IntegerValue: 5}},
  450. },
  451. },
  452. dst: &NestedStructPtrs{
  453. &SimpleTwoFields{S: "hello" /* SS: "" */},
  454. &SimpleTwoFields{ /* S: "" */ SS: "twice hello"},
  455. &NestedSimple2{
  456. A: &Simple{I: 2},
  457. /* I: 0 */
  458. },
  459. 0,
  460. },
  461. want: &NestedStructPtrs{
  462. &SimpleTwoFields{ /* S: "" */ SS: "ss hello"},
  463. nil,
  464. &NestedSimple2{
  465. /* A: nil, */
  466. I: 2,
  467. },
  468. 5,
  469. },
  470. },
  471. }
  472. for _, tc := range testCases {
  473. err := loadEntityProto(tc.dst, tc.src)
  474. if err != nil {
  475. t.Errorf("loadEntityProto: %s: %v", tc.desc, err)
  476. continue
  477. }
  478. if !testutil.Equal(tc.want, tc.dst) {
  479. t.Errorf("%s: compare:\ngot: %#v\nwant: %#v", tc.desc, tc.dst, tc.want)
  480. }
  481. }
  482. }
  483. type PLS0 struct {
  484. A string
  485. }
  486. func (p *PLS0) Load(props []Property) error {
  487. for _, pp := range props {
  488. if pp.Name == "A" {
  489. p.A = pp.Value.(string)
  490. }
  491. }
  492. return nil
  493. }
  494. func (p *PLS0) Save() (props []Property, err error) {
  495. return []Property{{Name: "A", Value: p.A}}, nil
  496. }
  497. type KeyLoader1 struct {
  498. A string
  499. K *Key
  500. }
  501. func (kl *KeyLoader1) Load(props []Property) error {
  502. for _, pp := range props {
  503. if pp.Name == "A" {
  504. kl.A = pp.Value.(string)
  505. }
  506. }
  507. return nil
  508. }
  509. func (kl *KeyLoader1) Save() (props []Property, err error) {
  510. return []Property{{Name: "A", Value: kl.A}}, nil
  511. }
  512. func (kl *KeyLoader1) LoadKey(k *Key) error {
  513. kl.K = k
  514. return nil
  515. }
  516. type KeyLoader2 struct {
  517. B int
  518. Key *Key
  519. }
  520. func (kl *KeyLoader2) Load(props []Property) error {
  521. for _, pp := range props {
  522. if pp.Name == "B" {
  523. kl.B = int(pp.Value.(int64))
  524. }
  525. }
  526. return nil
  527. }
  528. func (kl *KeyLoader2) Save() (props []Property, err error) {
  529. return []Property{{Name: "B", Value: int64(kl.B)}}, nil
  530. }
  531. func (kl *KeyLoader2) LoadKey(k *Key) error {
  532. kl.Key = k
  533. return nil
  534. }
  535. type KeyLoader3 struct {
  536. C bool
  537. K *Key
  538. }
  539. func (kl *KeyLoader3) Load(props []Property) error {
  540. for _, pp := range props {
  541. if pp.Name == "C" {
  542. kl.C = pp.Value.(bool)
  543. }
  544. }
  545. return nil
  546. }
  547. func (kl *KeyLoader3) Save() (props []Property, err error) {
  548. return []Property{{Name: "C", Value: kl.C}}, nil
  549. }
  550. func (kl *KeyLoader3) LoadKey(k *Key) error {
  551. kl.K = k
  552. return nil
  553. }
  554. type KeyLoader4 struct {
  555. PLS0
  556. K *Key
  557. }
  558. func (kl *KeyLoader4) LoadKey(k *Key) error {
  559. kl.K = k
  560. return nil
  561. }
  562. type NotKeyLoader struct {
  563. A string
  564. K *Key
  565. }
  566. func (p *NotKeyLoader) Load(props []Property) error {
  567. for _, pp := range props {
  568. if pp.Name == "A" {
  569. p.A = pp.Value.(string)
  570. }
  571. }
  572. return nil
  573. }
  574. func (p *NotKeyLoader) Save() (props []Property, err error) {
  575. return []Property{{Name: "A", Value: p.A}}, nil
  576. }
  577. type NotPLSKeyLoader struct {
  578. A string
  579. K *Key `datastore:"__key__"`
  580. }
  581. type NestedKeyLoaders struct {
  582. Two *KeyLoader2
  583. Three []*KeyLoader3
  584. Four *KeyLoader4
  585. PLS *NotKeyLoader
  586. }
  587. func TestKeyLoader(t *testing.T) {
  588. testCases := []struct {
  589. desc string
  590. src *pb.Entity
  591. dst interface{}
  592. want interface{}
  593. }{
  594. {
  595. desc: "simple key loader",
  596. src: &pb.Entity{
  597. Key: keyToProto(testKey0),
  598. Properties: map[string]*pb.Value{
  599. "A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}},
  600. },
  601. },
  602. dst: &KeyLoader1{},
  603. want: &KeyLoader1{
  604. A: "hello",
  605. K: testKey0,
  606. },
  607. },
  608. {
  609. desc: "simple key loader with unmatched properties",
  610. src: &pb.Entity{
  611. Key: keyToProto(testKey0),
  612. Properties: map[string]*pb.Value{
  613. "A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}},
  614. "B": {ValueType: &pb.Value_StringValue{StringValue: "unmatched"}},
  615. },
  616. },
  617. dst: &NotPLSKeyLoader{},
  618. want: &NotPLSKeyLoader{
  619. A: "hello",
  620. K: testKey0,
  621. },
  622. },
  623. {
  624. desc: "embedded PLS key loader",
  625. src: &pb.Entity{
  626. Key: keyToProto(testKey0),
  627. Properties: map[string]*pb.Value{
  628. "A": {ValueType: &pb.Value_StringValue{StringValue: "hello"}},
  629. },
  630. },
  631. dst: &KeyLoader4{},
  632. want: &KeyLoader4{
  633. PLS0: PLS0{A: "hello"},
  634. K: testKey0,
  635. },
  636. },
  637. {
  638. desc: "nested key loaders",
  639. src: &pb.Entity{
  640. Key: keyToProto(testKey0),
  641. Properties: map[string]*pb.Value{
  642. "Two": {ValueType: &pb.Value_EntityValue{
  643. EntityValue: &pb.Entity{
  644. Properties: map[string]*pb.Value{
  645. "B": {ValueType: &pb.Value_IntegerValue{IntegerValue: 12}},
  646. },
  647. Key: keyToProto(testKey1a),
  648. },
  649. }},
  650. "Three": {ValueType: &pb.Value_ArrayValue{
  651. ArrayValue: &pb.ArrayValue{
  652. Values: []*pb.Value{
  653. {ValueType: &pb.Value_EntityValue{
  654. EntityValue: &pb.Entity{
  655. Properties: map[string]*pb.Value{
  656. "C": {ValueType: &pb.Value_BooleanValue{BooleanValue: true}},
  657. },
  658. Key: keyToProto(testKey1b),
  659. },
  660. }},
  661. {ValueType: &pb.Value_EntityValue{
  662. EntityValue: &pb.Entity{
  663. Properties: map[string]*pb.Value{
  664. "C": {ValueType: &pb.Value_BooleanValue{BooleanValue: false}},
  665. },
  666. Key: keyToProto(testKey0),
  667. },
  668. }},
  669. },
  670. },
  671. }},
  672. "Four": {ValueType: &pb.Value_EntityValue{
  673. EntityValue: &pb.Entity{
  674. Properties: map[string]*pb.Value{
  675. "A": {ValueType: &pb.Value_StringValue{StringValue: "testing"}},
  676. },
  677. Key: keyToProto(testKey2a),
  678. },
  679. }},
  680. "PLS": {ValueType: &pb.Value_EntityValue{
  681. EntityValue: &pb.Entity{
  682. Properties: map[string]*pb.Value{
  683. "A": {ValueType: &pb.Value_StringValue{StringValue: "something"}},
  684. },
  685. Key: keyToProto(testKey1a),
  686. },
  687. }},
  688. },
  689. },
  690. dst: &NestedKeyLoaders{},
  691. want: &NestedKeyLoaders{
  692. Two: &KeyLoader2{B: 12, Key: testKey1a},
  693. Three: []*KeyLoader3{
  694. {
  695. C: true,
  696. K: testKey1b,
  697. },
  698. {
  699. C: false,
  700. K: testKey0,
  701. },
  702. },
  703. Four: &KeyLoader4{
  704. PLS0: PLS0{A: "testing"},
  705. K: testKey2a,
  706. },
  707. PLS: &NotKeyLoader{A: "something"},
  708. },
  709. },
  710. }
  711. for _, tc := range testCases {
  712. err := loadEntityProto(tc.dst, tc.src)
  713. if err != nil {
  714. // While loadEntityProto may return an error, if that error is
  715. // ErrFieldMismatch, then there is still data in tc.dst to compare.
  716. if _, ok := err.(*ErrFieldMismatch); !ok {
  717. t.Errorf("loadEntityProto: %s: %v", tc.desc, err)
  718. continue
  719. }
  720. }
  721. if !testutil.Equal(tc.want, tc.dst) {
  722. t.Errorf("%s: compare:\ngot: %+v\nwant: %+v", tc.desc, tc.dst, tc.want)
  723. }
  724. }
  725. }
  726. func TestLoadPointers(t *testing.T) {
  727. for _, test := range []struct {
  728. desc string
  729. in []Property
  730. want Pointers
  731. }{
  732. {
  733. desc: "nil properties load as nil pointers",
  734. in: []Property{
  735. {Name: "Pi", Value: nil},
  736. {Name: "Ps", Value: nil},
  737. {Name: "Pb", Value: nil},
  738. {Name: "Pf", Value: nil},
  739. {Name: "Pg", Value: nil},
  740. {Name: "Pt", Value: nil},
  741. },
  742. want: Pointers{},
  743. },
  744. {
  745. desc: "missing properties load as nil pointers",
  746. in: []Property(nil),
  747. want: Pointers{},
  748. },
  749. {
  750. desc: "non-nil properties load as the appropriate values",
  751. in: []Property{
  752. {Name: "Pi", Value: int64(1)},
  753. {Name: "Ps", Value: "x"},
  754. {Name: "Pb", Value: true},
  755. {Name: "Pf", Value: 3.14},
  756. {Name: "Pg", Value: GeoPoint{Lat: 1, Lng: 2}},
  757. {Name: "Pt", Value: time.Unix(100, 0)},
  758. },
  759. want: func() Pointers {
  760. p := populatedPointers()
  761. *p.Pi = 1
  762. *p.Ps = "x"
  763. *p.Pb = true
  764. *p.Pf = 3.14
  765. *p.Pg = GeoPoint{Lat: 1, Lng: 2}
  766. *p.Pt = time.Unix(100, 0)
  767. return *p
  768. }(),
  769. },
  770. } {
  771. var got Pointers
  772. if err := LoadStruct(&got, test.in); err != nil {
  773. t.Fatalf("%s: %v", test.desc, err)
  774. }
  775. if !testutil.Equal(got, test.want) {
  776. t.Errorf("%s:\ngot %+v\nwant %+v", test.desc, got, test.want)
  777. }
  778. }
  779. }
  780. func TestLoadNonArrayIntoSlice(t *testing.T) {
  781. // Loading a non-array value into a slice field results in a slice of size 1.
  782. var got struct{ S []string }
  783. if err := LoadStruct(&got, []Property{{Name: "S", Value: "x"}}); err != nil {
  784. t.Fatal(err)
  785. }
  786. if want := []string{"x"}; !testutil.Equal(got.S, want) {
  787. t.Errorf("got %#v, want %#v", got.S, want)
  788. }
  789. }
  790. func TestLoadEmptyArrayIntoSlice(t *testing.T) {
  791. // Loading an empty array into a slice field is a no-op.
  792. var got = struct{ S []string }{[]string{"x"}}
  793. if err := LoadStruct(&got, []Property{{Name: "S", Value: []interface{}{}}}); err != nil {
  794. t.Fatal(err)
  795. }
  796. if want := []string{"x"}; !testutil.Equal(got.S, want) {
  797. t.Errorf("got %#v, want %#v", got.S, want)
  798. }
  799. }
  800. func TestLoadNull(t *testing.T) {
  801. // Loading a Datastore Null into a basic type (int, float, etc.) results in a zero value.
  802. // Loading a Null into a slice of basic type results in a slice of size 1 containing the zero value.
  803. // (As expected from the behavior of slices and nulls with basic types.)
  804. type S struct {
  805. I int64
  806. F float64
  807. S string
  808. B bool
  809. A []string
  810. }
  811. got := S{
  812. I: 1,
  813. F: 1.0,
  814. S: "1",
  815. B: true,
  816. A: []string{"X"},
  817. }
  818. want := S{A: []string{""}}
  819. props := []Property{{Name: "I"}, {Name: "F"}, {Name: "S"}, {Name: "B"}, {Name: "A"}}
  820. if err := LoadStruct(&got, props); err != nil {
  821. t.Fatal(err)
  822. }
  823. if !testutil.Equal(got, want) {
  824. t.Errorf("got %+v, want %+v", got, want)
  825. }
  826. // Loading a Null into a pointer to struct field results in a nil field.
  827. got2 := struct{ X *S }{X: &S{}}
  828. if err := LoadStruct(&got2, []Property{{Name: "X"}}); err != nil {
  829. t.Fatal(err)
  830. }
  831. if got2.X != nil {
  832. t.Errorf("got %v, want nil", got2.X)
  833. }
  834. // Loading a Null into a struct field is an error.
  835. got3 := struct{ X S }{}
  836. err := LoadStruct(&got3, []Property{{Name: "X"}})
  837. if err == nil {
  838. t.Error("got nil, want error")
  839. }
  840. }
  841. // var got2 struct{ S []Pet }
  842. // if err := LoadStruct(&got2, []Property{{Name: "S", Value: nil}}); err != nil {
  843. // t.Fatal(err)
  844. // }
  845. // }