prop_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. // Copyright 2011 Google Inc. All Rights Reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package datastore
  5. import (
  6. "reflect"
  7. "testing"
  8. "time"
  9. "google.golang.org/appengine"
  10. )
  11. func TestValidPropertyName(t *testing.T) {
  12. testCases := []struct {
  13. name string
  14. want bool
  15. }{
  16. // Invalid names.
  17. {"", false},
  18. {"'", false},
  19. {".", false},
  20. {"..", false},
  21. {".foo", false},
  22. {"0", false},
  23. {"00", false},
  24. {"X.X.4.X.X", false},
  25. {"\n", false},
  26. {"\x00", false},
  27. {"abc\xffz", false},
  28. {"foo.", false},
  29. {"foo..", false},
  30. {"foo..bar", false},
  31. {"☃", false},
  32. {`"`, false},
  33. // Valid names.
  34. {"AB", true},
  35. {"Abc", true},
  36. {"X.X.X.X.X", true},
  37. {"_", true},
  38. {"_0", true},
  39. {"a", true},
  40. {"a_B", true},
  41. {"f00", true},
  42. {"f0o", true},
  43. {"fo0", true},
  44. {"foo", true},
  45. {"foo.bar", true},
  46. {"foo.bar.baz", true},
  47. {"世界", true},
  48. }
  49. for _, tc := range testCases {
  50. got := validPropertyName(tc.name)
  51. if got != tc.want {
  52. t.Errorf("%q: got %v, want %v", tc.name, got, tc.want)
  53. }
  54. }
  55. }
  56. func TestStructCodec(t *testing.T) {
  57. type oStruct struct {
  58. O int
  59. }
  60. type pStruct struct {
  61. P int
  62. Q int
  63. }
  64. type rStruct struct {
  65. R int
  66. S pStruct
  67. T oStruct
  68. oStruct
  69. }
  70. type uStruct struct {
  71. U int
  72. v int
  73. }
  74. type vStruct struct {
  75. V string `datastore:",noindex"`
  76. }
  77. oStructCodec := &structCodec{
  78. byIndex: []structTag{
  79. {name: "O"},
  80. },
  81. byName: map[string]fieldCodec{
  82. "O": {index: 0},
  83. },
  84. complete: true,
  85. }
  86. pStructCodec := &structCodec{
  87. byIndex: []structTag{
  88. {name: "P"},
  89. {name: "Q"},
  90. },
  91. byName: map[string]fieldCodec{
  92. "P": {index: 0},
  93. "Q": {index: 1},
  94. },
  95. complete: true,
  96. }
  97. rStructCodec := &structCodec{
  98. byIndex: []structTag{
  99. {name: "R"},
  100. {name: "S."},
  101. {name: "T."},
  102. {name: ""},
  103. },
  104. byName: map[string]fieldCodec{
  105. "R": {index: 0},
  106. "S.P": {index: 1, substructCodec: pStructCodec},
  107. "S.Q": {index: 1, substructCodec: pStructCodec},
  108. "T.O": {index: 2, substructCodec: oStructCodec},
  109. "O": {index: 3, substructCodec: oStructCodec},
  110. },
  111. complete: true,
  112. }
  113. uStructCodec := &structCodec{
  114. byIndex: []structTag{
  115. {name: "U"},
  116. {name: "v"},
  117. },
  118. byName: map[string]fieldCodec{
  119. "U": {index: 0},
  120. "v": {index: 1},
  121. },
  122. complete: true,
  123. }
  124. vStructCodec := &structCodec{
  125. byIndex: []structTag{
  126. {name: "V", noIndex: true},
  127. },
  128. byName: map[string]fieldCodec{
  129. "V": {index: 0},
  130. },
  131. complete: true,
  132. }
  133. testCases := []struct {
  134. desc string
  135. structValue interface{}
  136. want *structCodec
  137. }{
  138. {
  139. "oStruct",
  140. oStruct{},
  141. oStructCodec,
  142. },
  143. {
  144. "pStruct",
  145. pStruct{},
  146. pStructCodec,
  147. },
  148. {
  149. "rStruct",
  150. rStruct{},
  151. rStructCodec,
  152. },
  153. {
  154. "uStruct",
  155. uStruct{},
  156. uStructCodec,
  157. },
  158. {
  159. "non-basic fields",
  160. struct {
  161. B appengine.BlobKey
  162. K *Key
  163. T time.Time
  164. }{},
  165. &structCodec{
  166. byIndex: []structTag{
  167. {name: "B"},
  168. {name: "K"},
  169. {name: "T"},
  170. },
  171. byName: map[string]fieldCodec{
  172. "B": {index: 0},
  173. "K": {index: 1},
  174. "T": {index: 2},
  175. },
  176. complete: true,
  177. },
  178. },
  179. {
  180. "struct tags with ignored embed",
  181. struct {
  182. A int `datastore:"a,noindex"`
  183. B int `datastore:"b"`
  184. C int `datastore:",noindex"`
  185. D int `datastore:""`
  186. E int
  187. I int `datastore:"-"`
  188. J int `datastore:",noindex" json:"j"`
  189. oStruct `datastore:"-"`
  190. }{},
  191. &structCodec{
  192. byIndex: []structTag{
  193. {name: "a", noIndex: true},
  194. {name: "b", noIndex: false},
  195. {name: "C", noIndex: true},
  196. {name: "D", noIndex: false},
  197. {name: "E", noIndex: false},
  198. {name: "-", noIndex: false},
  199. {name: "J", noIndex: true},
  200. {name: "-", noIndex: false},
  201. },
  202. byName: map[string]fieldCodec{
  203. "a": {index: 0},
  204. "b": {index: 1},
  205. "C": {index: 2},
  206. "D": {index: 3},
  207. "E": {index: 4},
  208. "J": {index: 6},
  209. },
  210. complete: true,
  211. },
  212. },
  213. {
  214. "unexported fields",
  215. struct {
  216. A int
  217. b int
  218. C int `datastore:"x"`
  219. d int `datastore:"Y"`
  220. }{},
  221. &structCodec{
  222. byIndex: []structTag{
  223. {name: "A"},
  224. {name: "b"},
  225. {name: "x"},
  226. {name: "Y"},
  227. },
  228. byName: map[string]fieldCodec{
  229. "A": {index: 0},
  230. "b": {index: 1},
  231. "x": {index: 2},
  232. "Y": {index: 3},
  233. },
  234. complete: true,
  235. },
  236. },
  237. {
  238. "nested and embedded structs",
  239. struct {
  240. A int
  241. B int
  242. CC oStruct
  243. DDD rStruct
  244. oStruct
  245. }{},
  246. &structCodec{
  247. byIndex: []structTag{
  248. {name: "A"},
  249. {name: "B"},
  250. {name: "CC."},
  251. {name: "DDD."},
  252. {name: ""},
  253. },
  254. byName: map[string]fieldCodec{
  255. "A": {index: 0},
  256. "B": {index: 1},
  257. "CC.O": {index: 2, substructCodec: oStructCodec},
  258. "DDD.R": {index: 3, substructCodec: rStructCodec},
  259. "DDD.S.P": {index: 3, substructCodec: rStructCodec},
  260. "DDD.S.Q": {index: 3, substructCodec: rStructCodec},
  261. "DDD.T.O": {index: 3, substructCodec: rStructCodec},
  262. "DDD.O": {index: 3, substructCodec: rStructCodec},
  263. "O": {index: 4, substructCodec: oStructCodec},
  264. },
  265. complete: true,
  266. },
  267. },
  268. {
  269. "struct tags with nested and embedded structs",
  270. struct {
  271. A int `datastore:"-"`
  272. B int `datastore:"w"`
  273. C oStruct `datastore:"xx"`
  274. D rStruct `datastore:"y"`
  275. oStruct `datastore:"z"`
  276. }{},
  277. &structCodec{
  278. byIndex: []structTag{
  279. {name: "-"},
  280. {name: "w"},
  281. {name: "xx."},
  282. {name: "y."},
  283. {name: "z."},
  284. },
  285. byName: map[string]fieldCodec{
  286. "w": {index: 1},
  287. "xx.O": {index: 2, substructCodec: oStructCodec},
  288. "y.R": {index: 3, substructCodec: rStructCodec},
  289. "y.S.P": {index: 3, substructCodec: rStructCodec},
  290. "y.S.Q": {index: 3, substructCodec: rStructCodec},
  291. "y.T.O": {index: 3, substructCodec: rStructCodec},
  292. "y.O": {index: 3, substructCodec: rStructCodec},
  293. "z.O": {index: 4, substructCodec: oStructCodec},
  294. },
  295. complete: true,
  296. },
  297. },
  298. {
  299. "unexported nested and embedded structs",
  300. struct {
  301. a int
  302. B int
  303. c uStruct
  304. D uStruct
  305. uStruct
  306. }{},
  307. &structCodec{
  308. byIndex: []structTag{
  309. {name: "a"},
  310. {name: "B"},
  311. {name: "c."},
  312. {name: "D."},
  313. {name: ""},
  314. },
  315. byName: map[string]fieldCodec{
  316. "a": {index: 0},
  317. "B": {index: 1},
  318. "c.U": {index: 2, substructCodec: uStructCodec},
  319. "c.v": {index: 2, substructCodec: uStructCodec},
  320. "D.U": {index: 3, substructCodec: uStructCodec},
  321. "D.v": {index: 3, substructCodec: uStructCodec},
  322. "U": {index: 4, substructCodec: uStructCodec},
  323. "v": {index: 4, substructCodec: uStructCodec},
  324. },
  325. complete: true,
  326. },
  327. },
  328. {
  329. "noindex nested struct",
  330. struct {
  331. A oStruct `datastore:",noindex"`
  332. }{},
  333. &structCodec{
  334. byIndex: []structTag{
  335. {name: "A.", noIndex: true},
  336. },
  337. byName: map[string]fieldCodec{
  338. "A.O": {index: 0, substructCodec: oStructCodec},
  339. },
  340. complete: true,
  341. },
  342. },
  343. {
  344. "noindex slice",
  345. struct {
  346. A []string `datastore:",noindex"`
  347. }{},
  348. &structCodec{
  349. byIndex: []structTag{
  350. {name: "A", noIndex: true},
  351. },
  352. byName: map[string]fieldCodec{
  353. "A": {index: 0},
  354. },
  355. hasSlice: true,
  356. complete: true,
  357. },
  358. },
  359. {
  360. "noindex embedded struct slice",
  361. struct {
  362. // vStruct has a single field, V, also with noindex.
  363. A []vStruct `datastore:",noindex"`
  364. }{},
  365. &structCodec{
  366. byIndex: []structTag{
  367. {name: "A.", noIndex: true},
  368. },
  369. byName: map[string]fieldCodec{
  370. "A.V": {index: 0, substructCodec: vStructCodec},
  371. },
  372. hasSlice: true,
  373. complete: true,
  374. },
  375. },
  376. }
  377. for _, tc := range testCases {
  378. got, err := getStructCodec(reflect.TypeOf(tc.structValue))
  379. if err != nil {
  380. t.Errorf("%s: getStructCodec: %v", tc.desc, err)
  381. continue
  382. }
  383. if !reflect.DeepEqual(got, tc.want) {
  384. t.Errorf("%s\ngot %+v\nwant %+v\n", tc.desc, got, tc.want)
  385. continue
  386. }
  387. }
  388. }
  389. func TestRepeatedPropertyName(t *testing.T) {
  390. good := []interface{}{
  391. struct {
  392. A int `datastore:"-"`
  393. }{},
  394. struct {
  395. A int `datastore:"b"`
  396. B int
  397. }{},
  398. struct {
  399. A int
  400. B int `datastore:"B"`
  401. }{},
  402. struct {
  403. A int `datastore:"B"`
  404. B int `datastore:"-"`
  405. }{},
  406. struct {
  407. A int `datastore:"-"`
  408. B int `datastore:"A"`
  409. }{},
  410. struct {
  411. A int `datastore:"B"`
  412. B int `datastore:"A"`
  413. }{},
  414. struct {
  415. A int `datastore:"B"`
  416. B int `datastore:"C"`
  417. C int `datastore:"A"`
  418. }{},
  419. struct {
  420. A int `datastore:"B"`
  421. B int `datastore:"C"`
  422. C int `datastore:"D"`
  423. }{},
  424. }
  425. bad := []interface{}{
  426. struct {
  427. A int `datastore:"B"`
  428. B int
  429. }{},
  430. struct {
  431. A int
  432. B int `datastore:"A"`
  433. }{},
  434. struct {
  435. A int `datastore:"C"`
  436. B int `datastore:"C"`
  437. }{},
  438. struct {
  439. A int `datastore:"B"`
  440. B int `datastore:"C"`
  441. C int `datastore:"B"`
  442. }{},
  443. }
  444. testGetStructCodec(t, good, bad)
  445. }
  446. func TestFlatteningNestedStructs(t *testing.T) {
  447. type deepGood struct {
  448. A struct {
  449. B []struct {
  450. C struct {
  451. D int
  452. }
  453. }
  454. }
  455. }
  456. type deepBad struct {
  457. A struct {
  458. B []struct {
  459. C struct {
  460. D []int
  461. }
  462. }
  463. }
  464. }
  465. type iSay struct {
  466. Tomato int
  467. }
  468. type youSay struct {
  469. Tomato int
  470. }
  471. type tweedledee struct {
  472. Dee int `datastore:"D"`
  473. }
  474. type tweedledum struct {
  475. Dum int `datastore:"D"`
  476. }
  477. good := []interface{}{
  478. struct {
  479. X []struct {
  480. Y string
  481. }
  482. }{},
  483. struct {
  484. X []struct {
  485. Y []byte
  486. }
  487. }{},
  488. struct {
  489. P []int
  490. X struct {
  491. Y []int
  492. }
  493. }{},
  494. struct {
  495. X struct {
  496. Y []int
  497. }
  498. Q []int
  499. }{},
  500. struct {
  501. P []int
  502. X struct {
  503. Y []int
  504. }
  505. Q []int
  506. }{},
  507. struct {
  508. deepGood
  509. }{},
  510. struct {
  511. DG deepGood
  512. }{},
  513. struct {
  514. Foo struct {
  515. Z int `datastore:"X"`
  516. } `datastore:"A"`
  517. Bar struct {
  518. Z int `datastore:"Y"`
  519. } `datastore:"A"`
  520. }{},
  521. }
  522. bad := []interface{}{
  523. struct {
  524. X []struct {
  525. Y []string
  526. }
  527. }{},
  528. struct {
  529. X []struct {
  530. Y []int
  531. }
  532. }{},
  533. struct {
  534. deepBad
  535. }{},
  536. struct {
  537. DB deepBad
  538. }{},
  539. struct {
  540. iSay
  541. youSay
  542. }{},
  543. struct {
  544. tweedledee
  545. tweedledum
  546. }{},
  547. struct {
  548. Foo struct {
  549. Z int
  550. } `datastore:"A"`
  551. Bar struct {
  552. Z int
  553. } `datastore:"A"`
  554. }{},
  555. }
  556. testGetStructCodec(t, good, bad)
  557. }
  558. func testGetStructCodec(t *testing.T, good []interface{}, bad []interface{}) {
  559. for _, x := range good {
  560. if _, err := getStructCodec(reflect.TypeOf(x)); err != nil {
  561. t.Errorf("type %T: got non-nil error (%s), want nil", x, err)
  562. }
  563. }
  564. for _, x := range bad {
  565. if _, err := getStructCodec(reflect.TypeOf(x)); err == nil {
  566. t.Errorf("type %T: got nil error, want non-nil", x)
  567. }
  568. }
  569. }
  570. func TestNilKeyIsStored(t *testing.T) {
  571. x := struct {
  572. K *Key
  573. I int
  574. }{}
  575. p := PropertyList{}
  576. // Save x as properties.
  577. p1, _ := SaveStruct(&x)
  578. p.Load(p1)
  579. // Set x's fields to non-zero.
  580. x.K = &Key{}
  581. x.I = 2
  582. // Load x from properties.
  583. p2, _ := p.Save()
  584. LoadStruct(&x, p2)
  585. // Check that x's fields were set to zero.
  586. if x.K != nil {
  587. t.Errorf("K field was not zero")
  588. }
  589. if x.I != 0 {
  590. t.Errorf("I field was not zero")
  591. }
  592. }