github.com/mjibson/goon@v1.1.0/goon_test.go (about)

     1  /*
     2   * Copyright (c) 2012 The Goon Authors
     3   *
     4   * Permission to use, copy, modify, and distribute this software for any
     5   * purpose with or without fee is hereby granted, provided that the above
     6   * copyright notice and this permission notice appear in all copies.
     7   *
     8   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     9   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    10   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    11   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    12   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    13   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    14   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    15   */
    16  
    17  package goon
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"math/rand"
    25  	"reflect"
    26  	"strings"
    27  	"sync"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/golang/protobuf/proto"
    32  	"google.golang.org/appengine"
    33  	"google.golang.org/appengine/aetest"
    34  	"google.golang.org/appengine/datastore"
    35  	"google.golang.org/appengine/memcache"
    36  )
    37  
    38  func init() {
    39  	// The SDK emulators are extremely slow, so we can't use production timeouts
    40  	MemcachePutTimeoutSmall = 10 * time.Second
    41  	MemcacheGetTimeout = 10 * time.Second
    42  	// Make sure to propagate all errors for better testing
    43  	propagateMemcachePutError = true
    44  }
    45  
    46  // *[]S, *[]*S, *[]I, []S, []*S, []I,
    47  // *[]PLS, *[]*PLS, *[]IPLS, []PLS, []*PLS, []IPLS
    48  const (
    49  	ivTypePtrToSliceOfStructs = iota
    50  	ivTypePtrToSliceOfPtrsToStruct
    51  	ivTypePtrToSliceOfInterfaces
    52  	ivTypeSliceOfStructs
    53  	ivTypeSliceOfPtrsToStruct
    54  	ivTypeSliceOfInterfaces
    55  	ivTypePtrToSliceOfPLS
    56  	ivTypePtrToSliceOfPtrsToPLS
    57  	ivTypePtrToSliceOfInterfacesPLS
    58  	ivTypeSliceOfPLS
    59  	ivTypeSliceOfPtrsToPLS
    60  	ivTypeSliceOfInterfacesPLS
    61  	ivTypeTotal
    62  )
    63  
    64  const (
    65  	ivModeDatastore = iota
    66  	ivModeMemcache
    67  	ivModeMemcacheAndDatastore
    68  	ivModeLocalcache
    69  	ivModeLocalcacheAndMemcache
    70  	ivModeLocalcacheAndDatastore
    71  	ivModeLocalcacheAndMemcacheAndDatastore
    72  	ivModeTotal
    73  )
    74  
    75  func cloneKey(key *datastore.Key) *datastore.Key {
    76  	if key == nil {
    77  		return nil
    78  	}
    79  	dupe, err := datastore.DecodeKey(key.Encode())
    80  	if err != nil {
    81  		panic(fmt.Sprintf("Failed to clone key: %v", err))
    82  	}
    83  	return dupe
    84  }
    85  
    86  func cloneKeys(keys []*datastore.Key) []*datastore.Key {
    87  	if keys == nil {
    88  		return nil
    89  	}
    90  	dupe := make([]*datastore.Key, 0, len(keys))
    91  	for _, key := range keys {
    92  		if key == nil {
    93  			dupe = append(dupe, nil)
    94  		} else {
    95  			dupe = append(dupe, cloneKey(key))
    96  		}
    97  	}
    98  	return dupe
    99  }
   100  
   101  func TestCloneIVItem(t *testing.T) {
   102  	c, done, err := aetest.NewContext()
   103  	if err != nil {
   104  		t.Fatalf("Could not start aetest - %v", err)
   105  	}
   106  	defer done()
   107  
   108  	initializeIvItems(c)
   109  
   110  	for i := range ivItems {
   111  		clone := *ivItems[i].clone()
   112  		if !reflect.DeepEqual(ivItems[i], clone) {
   113  			t.Fatalf("ivItem clone failed!\n%v", getDiff(ivItems[i], clone, fmt.Sprintf("ivItems[%d]", i), "clone"))
   114  		}
   115  	}
   116  }
   117  
   118  // Have a bunch of different supported types to detect any wild errors
   119  // https://cloud.google.com/appengine/docs/standard/go/datastore/reference
   120  //
   121  // - signed integers (int, int8, int16, int32 and int64),
   122  // - bool,
   123  // - string,
   124  // - float32 and float64,
   125  // - []byte (up to 1 megabyte in length),
   126  // - any type whose underlying type is one of the above predeclared types,
   127  // - ByteString,
   128  // - *Key,
   129  // - time.Time (stored with microsecond precision),
   130  // - appengine.BlobKey,
   131  // - appengine.GeoPoint,
   132  // - structs whose fields are all valid value types,
   133  // - slices of any of the above.
   134  //
   135  // In addition, although undocumented, there's also support for any type,
   136  // whose underlying type is a legal slice.
   137  type ivItem struct {
   138  	Id           int64                `datastore:"-" goon:"id"`
   139  	Kind         string               `datastore:"-" goon:"kind,ivItem"`
   140  	Int          int                  `datastore:"int,noindex"`
   141  	Int8         int8                 `datastore:"int8,noindex"`
   142  	Int16        int16                `datastore:"int16,noindex"`
   143  	Int32        int32                `datastore:"int32,noindex"`
   144  	Int64        int64                `datastore:"int64,noindex"`
   145  	Bool         bool                 `datastore:"bool,noindex"`
   146  	String       string               `datastore:"string,noindex"`
   147  	Float32      float32              `datastore:"float32,noindex"`
   148  	Float64      float64              `datastore:"float64,noindex"`
   149  	ByteSlice    []byte               `datastore:"byte_slice,noindex"`
   150  	CustomTypes  ivItemCustom         `datastore:"custom,noindex"`
   151  	BString      datastore.ByteString `datastore:"bstr,noindex"`
   152  	Key          *datastore.Key       `datastore:"key,noindex"`
   153  	Time         time.Time            `datastore:"time,noindex"`
   154  	BlobKey      appengine.BlobKey    `datastore:"bk,noindex"`
   155  	GeoPoint     appengine.GeoPoint   `datastore:"gp,noindex"`
   156  	Sub          ivItemSub            `datastore:"sub,noindex"`
   157  	SliceTypes   ivItemSlice          `datastore:"slice,noindex"`
   158  	CustomSlices ivItemSliceCustom    `datastore:"custom_slice,noindex"`
   159  
   160  	NoIndex     int `datastore:",noindex"`
   161  	Casual      string
   162  	Ζεύς        string
   163  	ChildKey    *datastore.Key
   164  	ZeroKey     *datastore.Key
   165  	KeySliceNil []*datastore.Key
   166  
   167  	SaveCount int
   168  	LoadCount int
   169  }
   170  
   171  func (ivi ivItem) clone() *ivItem {
   172  	return &ivItem{
   173  		Id:           ivi.Id,
   174  		Int:          ivi.Int,
   175  		Int8:         ivi.Int8,
   176  		Int16:        ivi.Int16,
   177  		Int32:        ivi.Int32,
   178  		Int64:        ivi.Int64,
   179  		Bool:         ivi.Bool,
   180  		String:       ivi.String,
   181  		Float32:      ivi.Float32,
   182  		Float64:      ivi.Float64,
   183  		ByteSlice:    append(ivi.ByteSlice[:0:0], ivi.ByteSlice...),
   184  		CustomTypes:  *ivi.CustomTypes.clone(),
   185  		BString:      append(ivi.BString[:0:0], ivi.BString...),
   186  		Key:          cloneKey(ivi.Key),
   187  		Time:         ivi.Time,
   188  		BlobKey:      ivi.BlobKey,
   189  		GeoPoint:     ivi.GeoPoint,
   190  		Sub:          *ivi.Sub.clone(),
   191  		SliceTypes:   *ivi.SliceTypes.clone(),
   192  		CustomSlices: *ivi.CustomSlices.clone(),
   193  		NoIndex:      ivi.NoIndex,
   194  		Casual:       ivi.Casual,
   195  		Ζεύς:         ivi.Ζεύς,
   196  		ChildKey:     cloneKey(ivi.ChildKey),
   197  		ZeroKey:      cloneKey(ivi.ZeroKey),
   198  		KeySliceNil:  cloneKeys(ivi.KeySliceNil),
   199  		SaveCount:    ivi.SaveCount,
   200  		LoadCount:    ivi.LoadCount,
   201  	}
   202  }
   203  
   204  type ivItemInt int
   205  type ivItemInt8 int8
   206  type ivItemInt16 int16
   207  type ivItemInt32 int32
   208  type ivItemInt64 int64
   209  type ivItemBool bool
   210  type ivItemString string
   211  type ivItemFloat32 float32
   212  type ivItemFloat64 float64
   213  type ivItemByteSlice []byte
   214  
   215  type ivItemDeepInt ivItemInt
   216  
   217  type ivItemCustom struct {
   218  	Int       ivItemInt
   219  	Int8      ivItemInt8
   220  	Int16     ivItemInt16
   221  	Int32     ivItemInt32
   222  	Int64     ivItemInt64
   223  	Bool      ivItemBool
   224  	String    ivItemString
   225  	Float32   ivItemFloat32
   226  	Float64   ivItemFloat64
   227  	ByteSlice ivItemByteSlice
   228  	DeepInt   ivItemDeepInt
   229  }
   230  
   231  func (ivic ivItemCustom) clone() *ivItemCustom {
   232  	return &ivItemCustom{
   233  		Int:       ivic.Int,
   234  		Int8:      ivic.Int8,
   235  		Int16:     ivic.Int16,
   236  		Int32:     ivic.Int32,
   237  		Int64:     ivic.Int64,
   238  		Bool:      ivic.Bool,
   239  		String:    ivic.String,
   240  		Float32:   ivic.Float32,
   241  		Float64:   ivic.Float64,
   242  		ByteSlice: append(ivic.ByteSlice[:0:0], ivic.ByteSlice...),
   243  		DeepInt:   ivic.DeepInt,
   244  	}
   245  }
   246  
   247  type ivItemSlice struct {
   248  	Int       []int
   249  	Int8      []int8
   250  	Int16     []int16
   251  	Int32     []int32
   252  	Int64     []int64
   253  	Bool      []bool
   254  	String    []string
   255  	Float32   []float32
   256  	Float64   []float64
   257  	BSSlice   [][]byte
   258  	IntC      []ivItemInt
   259  	Int8C     []ivItemInt8
   260  	Int16C    []ivItemInt16
   261  	Int32C    []ivItemInt32
   262  	Int64C    []ivItemInt64
   263  	BoolC     []ivItemBool
   264  	StringC   []ivItemString
   265  	Float32C  []ivItemFloat32
   266  	Float64C  []ivItemFloat64
   267  	BSSliceC  []ivItemByteSlice
   268  	DeepInt   []ivItemDeepInt
   269  	BStrSlice []datastore.ByteString
   270  	KeySlice  []*datastore.Key
   271  	TimeSlice []time.Time
   272  	BKSlice   []appengine.BlobKey
   273  	GPSlice   []appengine.GeoPoint
   274  	Subs      []ivItemSubs
   275  }
   276  
   277  func (ivis ivItemSlice) clone() *ivItemSlice {
   278  	bsSlice := ivis.BSSlice[:0:0]
   279  	for _, bs := range ivis.BSSlice {
   280  		bsSlice = append(bsSlice, append(bs[:0:0], bs...))
   281  	}
   282  	bsSliceC := ivis.BSSliceC[:0:0]
   283  	for _, bsc := range ivis.BSSliceC {
   284  		bsSliceC = append(bsSliceC, append(bsc[:0:0], bsc...))
   285  	}
   286  	bstrSlice := ivis.BStrSlice[:0:0]
   287  	for _, bstr := range ivis.BStrSlice {
   288  		bstrSlice = append(bstrSlice, append(bstr[:0:0], bstr...))
   289  	}
   290  	subs := ivis.Subs[:0:0]
   291  	for _, sub := range ivis.Subs {
   292  		subs = append(subs, *sub.clone())
   293  	}
   294  
   295  	return &ivItemSlice{
   296  		Int:       append(ivis.Int[:0:0], ivis.Int...),
   297  		Int8:      append(ivis.Int8[:0:0], ivis.Int8...),
   298  		Int16:     append(ivis.Int16[:0:0], ivis.Int16...),
   299  		Int32:     append(ivis.Int32[:0:0], ivis.Int32...),
   300  		Int64:     append(ivis.Int64[:0:0], ivis.Int64...),
   301  		Bool:      append(ivis.Bool[:0:0], ivis.Bool...),
   302  		String:    append(ivis.String[:0:0], ivis.String...),
   303  		Float32:   append(ivis.Float32[:0:0], ivis.Float32...),
   304  		Float64:   append(ivis.Float64[:0:0], ivis.Float64...),
   305  		BSSlice:   bsSlice,
   306  		IntC:      append(ivis.IntC[:0:0], ivis.IntC...),
   307  		Int8C:     append(ivis.Int8C[:0:0], ivis.Int8C...),
   308  		Int16C:    append(ivis.Int16C[:0:0], ivis.Int16C...),
   309  		Int32C:    append(ivis.Int32C[:0:0], ivis.Int32C...),
   310  		Int64C:    append(ivis.Int64C[:0:0], ivis.Int64C...),
   311  		BoolC:     append(ivis.BoolC[:0:0], ivis.BoolC...),
   312  		StringC:   append(ivis.StringC[:0:0], ivis.StringC...),
   313  		Float32C:  append(ivis.Float32C[:0:0], ivis.Float32C...),
   314  		Float64C:  append(ivis.Float64C[:0:0], ivis.Float64C...),
   315  		BSSliceC:  bsSliceC,
   316  		DeepInt:   append(ivis.DeepInt[:0:0], ivis.DeepInt...),
   317  		BStrSlice: bstrSlice,
   318  		KeySlice:  cloneKeys(ivis.KeySlice),
   319  		TimeSlice: append(ivis.TimeSlice[:0:0], ivis.TimeSlice...),
   320  		BKSlice:   append(ivis.BKSlice[:0:0], ivis.BKSlice...),
   321  		GPSlice:   append(ivis.GPSlice[:0:0], ivis.GPSlice...),
   322  		Subs:      subs,
   323  	}
   324  }
   325  
   326  type IntS []int
   327  type Int8S []int8
   328  type Int16S []int16
   329  type Int32S []int32
   330  type Int64S []int64
   331  type BoolS []bool
   332  type StringS []string
   333  type Float32S []float32
   334  type Float64S []float64
   335  type BSSliceS [][]byte
   336  type IntCS []ivItemInt
   337  type Int8CS []ivItemInt8
   338  type Int16CS []ivItemInt16
   339  type Int32CS []ivItemInt32
   340  type Int64CS []ivItemInt64
   341  type BoolCS []ivItemBool
   342  type StringCS []ivItemString
   343  type Float32CS []ivItemFloat32
   344  type Float64CS []ivItemFloat64
   345  type BSSliceCS []ivItemByteSlice
   346  type DeepIntS []ivItemDeepInt
   347  type BStrSliceS []datastore.ByteString
   348  type KeySliceS []*datastore.Key
   349  type TimeSliceS []time.Time
   350  type BKSliceS []appengine.BlobKey
   351  type GPSliceS []appengine.GeoPoint
   352  type SubsS []ivItemSubs
   353  
   354  type ivItemSliceCustom struct {
   355  	Int       IntS
   356  	Int8      Int8S
   357  	Int16     Int16S
   358  	Int32     Int32S
   359  	Int64     Int64S
   360  	Bool      BoolS
   361  	String    StringS
   362  	Float32   Float32S
   363  	Float64   Float64S
   364  	BSSlice   BSSliceS
   365  	IntC      IntCS
   366  	Int8C     Int8CS
   367  	Int16C    Int16CS
   368  	Int32C    Int32CS
   369  	Int64C    Int64CS
   370  	BoolC     BoolCS
   371  	StringC   StringCS
   372  	Float32C  Float32CS
   373  	Float64C  Float64CS
   374  	BSSliceC  BSSliceCS
   375  	DeepInt   DeepIntS
   376  	BStrSlice BStrSliceS
   377  	KeySlice  KeySliceS
   378  	TimeSlice TimeSliceS
   379  	BKSlice   BKSliceS
   380  	GPSlice   GPSliceS
   381  	Subs      SubsS
   382  }
   383  
   384  func (ivisc ivItemSliceCustom) clone() *ivItemSliceCustom {
   385  	bsSlice := ivisc.BSSlice[:0:0]
   386  	for _, bs := range ivisc.BSSlice {
   387  		bsSlice = append(bsSlice, append(bs[:0:0], bs...))
   388  	}
   389  	bsSliceC := ivisc.BSSliceC[:0:0]
   390  	for _, bsc := range ivisc.BSSliceC {
   391  		bsSliceC = append(bsSliceC, append(bsc[:0:0], bsc...))
   392  	}
   393  	bstrSlice := ivisc.BStrSlice[:0:0]
   394  	for _, bstr := range ivisc.BStrSlice {
   395  		bstrSlice = append(bstrSlice, append(bstr[:0:0], bstr...))
   396  	}
   397  	subs := ivisc.Subs[:0:0]
   398  	for _, sub := range ivisc.Subs {
   399  		subs = append(subs, *sub.clone())
   400  	}
   401  
   402  	return &ivItemSliceCustom{
   403  		Int:       append(ivisc.Int[:0:0], ivisc.Int...),
   404  		Int8:      append(ivisc.Int8[:0:0], ivisc.Int8...),
   405  		Int16:     append(ivisc.Int16[:0:0], ivisc.Int16...),
   406  		Int32:     append(ivisc.Int32[:0:0], ivisc.Int32...),
   407  		Int64:     append(ivisc.Int64[:0:0], ivisc.Int64...),
   408  		Bool:      append(ivisc.Bool[:0:0], ivisc.Bool...),
   409  		String:    append(ivisc.String[:0:0], ivisc.String...),
   410  		Float32:   append(ivisc.Float32[:0:0], ivisc.Float32...),
   411  		Float64:   append(ivisc.Float64[:0:0], ivisc.Float64...),
   412  		BSSlice:   bsSlice,
   413  		IntC:      append(ivisc.IntC[:0:0], ivisc.IntC...),
   414  		Int8C:     append(ivisc.Int8C[:0:0], ivisc.Int8C...),
   415  		Int16C:    append(ivisc.Int16C[:0:0], ivisc.Int16C...),
   416  		Int32C:    append(ivisc.Int32C[:0:0], ivisc.Int32C...),
   417  		Int64C:    append(ivisc.Int64C[:0:0], ivisc.Int64C...),
   418  		BoolC:     append(ivisc.BoolC[:0:0], ivisc.BoolC...),
   419  		StringC:   append(ivisc.StringC[:0:0], ivisc.StringC...),
   420  		Float32C:  append(ivisc.Float32C[:0:0], ivisc.Float32C...),
   421  		Float64C:  append(ivisc.Float64C[:0:0], ivisc.Float64C...),
   422  		BSSliceC:  bsSliceC,
   423  		DeepInt:   append(ivisc.DeepInt[:0:0], ivisc.DeepInt...),
   424  		BStrSlice: bstrSlice,
   425  		KeySlice:  cloneKeys(ivisc.KeySlice),
   426  		TimeSlice: append(ivisc.TimeSlice[:0:0], ivisc.TimeSlice...),
   427  		BKSlice:   append(ivisc.BKSlice[:0:0], ivisc.BKSlice...),
   428  		GPSlice:   append(ivisc.GPSlice[:0:0], ivisc.GPSlice...),
   429  		Subs:      subs,
   430  	}
   431  }
   432  
   433  type ivItemSub struct {
   434  	Data string `datastore:"data,noindex"`
   435  	Ints []int  `datastore:"ints,noindex"`
   436  }
   437  
   438  func (ivis ivItemSub) clone() *ivItemSub {
   439  	return &ivItemSub{
   440  		Data: ivis.Data,
   441  		Ints: append(ivis.Ints[:0:0], ivis.Ints...),
   442  	}
   443  }
   444  
   445  type ivItemSubs struct {
   446  	Key   *datastore.Key `datastore:"key,noindex"`
   447  	Data  string         `datastore:"data,noindex"`
   448  	Extra string         `datastore:",noindex"`
   449  }
   450  
   451  func (ivis ivItemSubs) clone() *ivItemSubs {
   452  	return &ivItemSubs{
   453  		Key:   cloneKey(ivis.Key),
   454  		Data:  ivis.Data,
   455  		Extra: ivis.Extra,
   456  	}
   457  }
   458  
   459  func (ivi *ivItem) ForInterface()    {}
   460  func (ivi *ivItemPLS) ForInterface() {}
   461  
   462  type ivItemI interface {
   463  	ForInterface()
   464  }
   465  
   466  // Implement the PropertyLoadSave interface for ivItem
   467  type ivItemPLS ivItem
   468  
   469  func (ivi *ivItemPLS) Save() ([]datastore.Property, error) {
   470  	ivi.SaveCount++
   471  	return datastore.SaveStruct(ivi)
   472  }
   473  
   474  func (ivi *ivItemPLS) Load(props []datastore.Property) error {
   475  	err := datastore.LoadStruct(ivi, props)
   476  	ivi.LoadCount++
   477  	return err
   478  }
   479  
   480  var ivItems []ivItem
   481  var ivItemKeys []*datastore.Key
   482  
   483  func initializeIvItems(c context.Context) {
   484  	// We force UTC, because the datastore API will always return UTC
   485  	t1 := time.Now().UTC().Truncate(time.Microsecond)
   486  	t2 := t1.Add(time.Second * 1)
   487  	t3 := t1.Add(time.Second * 2)
   488  
   489  	ivi1 := &ivItem{
   490  		Id:        1,
   491  		Int:       123,
   492  		Int8:      77,
   493  		Int16:     13001,
   494  		Int32:     1234567890,
   495  		Int64:     123456789012345,
   496  		Bool:      true,
   497  		String:    "one",
   498  		Float32:   (float32(10) / float32(3)),
   499  		Float64:   (float64(10000000) / float64(9998)),
   500  		ByteSlice: []byte{0xDE, 0xAD},
   501  		CustomTypes: ivItemCustom{
   502  			Int:       123,
   503  			Int8:      77,
   504  			Int16:     13001,
   505  			Int32:     1234567890,
   506  			Int64:     123456789012345,
   507  			Bool:      true,
   508  			String:    "one",
   509  			Float32:   ivItemFloat32(float32(10) / float32(3)),
   510  			Float64:   ivItemFloat64(float64(10000000) / float64(9998)),
   511  			ByteSlice: ivItemByteSlice([]byte{0x01, 0x02, 0xAA}),
   512  			DeepInt:   1,
   513  		},
   514  		BString:  datastore.ByteString([]byte{0xAB}),
   515  		Key:      datastore.NewKey(c, "Fruit", "Apple", 0, nil),
   516  		Time:     t1,
   517  		BlobKey:  appengine.BlobKey("fake #1"),
   518  		GeoPoint: appengine.GeoPoint{Lat: 1.1, Lng: 2.2},
   519  		Sub: ivItemSub{
   520  			Data: "yay #1",
   521  			Ints: []int{1, 2, 3},
   522  		},
   523  		SliceTypes: ivItemSlice{
   524  			Int:       []int{1, 2},
   525  			Int8:      []int8{1, 2},
   526  			Int16:     []int16{1, 2},
   527  			Int32:     []int32{1, 2},
   528  			Int64:     []int64{1, 2},
   529  			Bool:      []bool{true, false},
   530  			String:    []string{"one", "two"},
   531  			Float32:   []float32{1.0, 2.0},
   532  			Float64:   []float64{1.0, 2.0},
   533  			BSSlice:   [][]byte{{0x01, 0x02}, {0x03, 0x04}},
   534  			IntC:      []ivItemInt{1, 2},
   535  			Int8C:     []ivItemInt8{1, 2},
   536  			Int16C:    []ivItemInt16{1, 2},
   537  			Int32C:    []ivItemInt32{1, 2},
   538  			Int64C:    []ivItemInt64{1, 2},
   539  			BoolC:     []ivItemBool{true, false},
   540  			StringC:   []ivItemString{"one", "two"},
   541  			Float32C:  []ivItemFloat32{1.0, 2.0},
   542  			Float64C:  []ivItemFloat64{1.0, 2.0},
   543  			BSSliceC:  []ivItemByteSlice{{0x01, 0x02}, {0x03, 0x04}},
   544  			DeepInt:   []ivItemDeepInt{1, 2},
   545  			BStrSlice: []datastore.ByteString{datastore.ByteString("one"), datastore.ByteString("two")},
   546  			KeySlice:  []*datastore.Key{datastore.NewKey(c, "Key", "", 1, nil), datastore.NewKey(c, "Key", "", 2, nil), datastore.NewKey(c, "Key", "", 3, nil)},
   547  			TimeSlice: []time.Time{t1, t2, t3},
   548  			BKSlice:   []appengine.BlobKey{appengine.BlobKey("fake #1.1"), appengine.BlobKey("fake #1.2")},
   549  			GPSlice:   []appengine.GeoPoint{{Lat: 1.1, Lng: -2.2}, {Lat: -3.3, Lng: 4.4}},
   550  			Subs: []ivItemSubs{
   551  				{Key: datastore.NewKey(c, "Fruit", "Banana", 0, nil), Data: "sub #1.1", Extra: "xtra #1.1"},
   552  				{Key: nil, Data: "sub #1.2", Extra: "xtra #1.2"},
   553  				{Key: datastore.NewKey(c, "Fruit", "Cherry", 0, nil), Data: "sub #1.3", Extra: "xtra #1.3"},
   554  			},
   555  		},
   556  		CustomSlices: ivItemSliceCustom{
   557  			Int:       IntS{1, 2},
   558  			Int8:      Int8S{1, 2},
   559  			Int16:     Int16S{1, 2},
   560  			Int32:     Int32S{1, 2},
   561  			Int64:     Int64S{1, 2},
   562  			Bool:      BoolS{true, false},
   563  			String:    StringS{"one", "two"},
   564  			Float32:   Float32S{1.0, 2.0},
   565  			Float64:   Float64S{1.0, 2.0},
   566  			BSSlice:   BSSliceS{[]byte{0x01, 0x02}, []byte{0x03, 0x04}},
   567  			IntC:      IntCS{1, 2},
   568  			Int8C:     Int8CS{1, 2},
   569  			Int16C:    Int16CS{1, 2},
   570  			Int32C:    Int32CS{1, 2},
   571  			Int64C:    Int64CS{1, 2},
   572  			BoolC:     BoolCS{true, false},
   573  			StringC:   StringCS{"one", "two"},
   574  			Float32C:  Float32CS{1.0, 2.0},
   575  			Float64C:  Float64CS{1.0, 2.0},
   576  			BSSliceC:  BSSliceCS{ivItemByteSlice{0x01, 0x02}, ivItemByteSlice{0x03, 0x04}},
   577  			DeepInt:   DeepIntS{1, 2},
   578  			BStrSlice: BStrSliceS{datastore.ByteString("one"), datastore.ByteString("two")},
   579  			KeySlice:  KeySliceS{datastore.NewKey(c, "Key", "", 1, nil), datastore.NewKey(c, "Key", "", 2, nil), datastore.NewKey(c, "Key", "", 3, nil)},
   580  			TimeSlice: TimeSliceS{t1, t2, t3},
   581  			BKSlice:   BKSliceS{appengine.BlobKey("fake #1.1"), appengine.BlobKey("fake #1.2")},
   582  			GPSlice:   GPSliceS{appengine.GeoPoint{Lat: 1.1, Lng: -2.2}, appengine.GeoPoint{Lat: -3.3, Lng: 4.4}},
   583  			Subs: SubsS{
   584  				{Key: datastore.NewKey(c, "Fruit", "Banana", 0, nil), Data: "sub #1.1", Extra: "xtra #1.1"},
   585  				{Key: datastore.NewKey(c, "Fruit", "Cherry", 0, nil), Data: "sub #1.2", Extra: "xtra #1.2"},
   586  				{Key: nil, Data: "sub #1.3", Extra: "xtra #1.3"},
   587  			},
   588  		},
   589  		NoIndex:     1,
   590  		Casual:      "clothes",
   591  		Ζεύς:        "Zeus",
   592  		ChildKey:    datastore.NewKey(c, "Person", "Jane", 0, datastore.NewKey(c, "Person", "John", 0, datastore.NewKey(c, "Person", "Jack", 0, nil))),
   593  		ZeroKey:     nil,
   594  		KeySliceNil: []*datastore.Key{datastore.NewKey(c, "Number", "", 1, nil), nil, datastore.NewKey(c, "Number", "", 2, nil)},
   595  	}
   596  
   597  	ivi2 := ivi1.clone()
   598  	ivi2.Id = 2
   599  
   600  	ivi3 := ivi1.clone()
   601  	ivi3.Id = 3
   602  
   603  	ivItems = append(ivItems, *ivi1)
   604  	ivItems = append(ivItems, *ivi2)
   605  	ivItems = append(ivItems, *ivi3)
   606  
   607  	g := FromContext(c)
   608  	for i := range ivItems {
   609  		ivItemKeys = append(ivItemKeys, g.Key(&ivItems[i]))
   610  	}
   611  }
   612  
   613  func getInputVarietyItem(t *testing.T, g *Goon, ivType int, empty bool, indices ...int) interface{} {
   614  	var result interface{}
   615  
   616  	getItem := func(index int) *ivItem {
   617  		if empty {
   618  			return &ivItem{Id: ivItems[index].Id}
   619  		}
   620  		return ivItems[index].clone()
   621  	}
   622  
   623  	switch ivType {
   624  	case ivTypePtrToSliceOfStructs:
   625  		s := []ivItem{}
   626  		for _, index := range indices {
   627  			s = append(s, *getItem(index))
   628  		}
   629  		result = &s
   630  	case ivTypePtrToSliceOfPtrsToStruct:
   631  		s := []*ivItem{}
   632  		for _, index := range indices {
   633  			s = append(s, getItem(index))
   634  		}
   635  		result = &s
   636  	case ivTypePtrToSliceOfInterfaces:
   637  		s := []ivItemI{}
   638  		for _, index := range indices {
   639  			s = append(s, getItem(index))
   640  		}
   641  		result = &s
   642  	case ivTypeSliceOfStructs:
   643  		s := []ivItem{}
   644  		for _, index := range indices {
   645  			s = append(s, *getItem(index))
   646  		}
   647  		result = s
   648  	case ivTypeSliceOfPtrsToStruct:
   649  		s := []*ivItem{}
   650  		for _, index := range indices {
   651  			s = append(s, getItem(index))
   652  		}
   653  		result = s
   654  	case ivTypeSliceOfInterfaces:
   655  		s := []ivItemI{}
   656  		for _, index := range indices {
   657  			s = append(s, getItem(index))
   658  		}
   659  		result = s
   660  	case ivTypePtrToSliceOfPLS:
   661  		s := []ivItemPLS{}
   662  		for _, index := range indices {
   663  			s = append(s, (ivItemPLS)(*getItem(index)))
   664  		}
   665  		result = &s
   666  	case ivTypePtrToSliceOfPtrsToPLS:
   667  		s := []*ivItemPLS{}
   668  		for _, index := range indices {
   669  			s = append(s, (*ivItemPLS)(getItem(index)))
   670  		}
   671  		result = &s
   672  	case ivTypePtrToSliceOfInterfacesPLS:
   673  		s := []ivItemI{}
   674  		for _, index := range indices {
   675  			s = append(s, (*ivItemPLS)(getItem(index)))
   676  		}
   677  		result = &s
   678  	case ivTypeSliceOfPLS:
   679  		s := []ivItemPLS{}
   680  		for _, index := range indices {
   681  			s = append(s, (ivItemPLS)(*getItem(index)))
   682  		}
   683  		result = s
   684  	case ivTypeSliceOfPtrsToPLS:
   685  		s := []*ivItemPLS{}
   686  		for _, index := range indices {
   687  			s = append(s, (*ivItemPLS)(getItem(index)))
   688  		}
   689  		result = s
   690  	case ivTypeSliceOfInterfacesPLS:
   691  		s := []ivItemI{}
   692  		for _, index := range indices {
   693  			s = append(s, (*ivItemPLS)(getItem(index)))
   694  		}
   695  		result = s
   696  	default:
   697  		t.Fatalf("Invalid input variety type! %v", ivType)
   698  		return nil
   699  	}
   700  
   701  	return result
   702  }
   703  
   704  func getPrettyIVMode(ivMode int) string {
   705  	result := "N/A"
   706  
   707  	switch ivMode {
   708  	case ivModeDatastore:
   709  		result = "DS"
   710  	case ivModeMemcache:
   711  		result = "MC"
   712  	case ivModeMemcacheAndDatastore:
   713  		result = "DS+MC"
   714  	case ivModeLocalcache:
   715  		result = "LC"
   716  	case ivModeLocalcacheAndMemcache:
   717  		result = "MC+LC"
   718  	case ivModeLocalcacheAndDatastore:
   719  		result = "DS+LC"
   720  	case ivModeLocalcacheAndMemcacheAndDatastore:
   721  		result = "DS+MC+LC"
   722  	}
   723  
   724  	return result
   725  }
   726  
   727  func getPrettyIVType(ivType int) string {
   728  	result := "N/A"
   729  
   730  	switch ivType {
   731  	case ivTypePtrToSliceOfStructs:
   732  		result = "*[]S"
   733  	case ivTypePtrToSliceOfPtrsToStruct:
   734  		result = "*[]*S"
   735  	case ivTypePtrToSliceOfInterfaces:
   736  		result = "*[]I"
   737  	case ivTypeSliceOfStructs:
   738  		result = "[]S"
   739  	case ivTypeSliceOfPtrsToStruct:
   740  		result = "[]*S"
   741  	case ivTypeSliceOfInterfaces:
   742  		result = "[]I"
   743  	case ivTypePtrToSliceOfPLS:
   744  		result = "*[]PLS"
   745  	case ivTypePtrToSliceOfPtrsToPLS:
   746  		result = "*[]*PLS"
   747  	case ivTypePtrToSliceOfInterfacesPLS:
   748  		result = "*[]IPLS"
   749  	case ivTypeSliceOfPLS:
   750  		result = "[]PLS"
   751  	case ivTypeSliceOfPtrsToPLS:
   752  		result = "[]*PLS"
   753  	case ivTypeSliceOfInterfacesPLS:
   754  		result = "[]IPLS"
   755  	}
   756  
   757  	return result
   758  }
   759  
   760  func isIVTypePLS(ivType int) bool {
   761  	switch ivType {
   762  	case ivTypePtrToSliceOfPLS,
   763  		ivTypePtrToSliceOfPtrsToPLS,
   764  		ivTypePtrToSliceOfInterfacesPLS,
   765  		ivTypeSliceOfPLS,
   766  		ivTypeSliceOfPtrsToPLS,
   767  		ivTypeSliceOfInterfacesPLS:
   768  		return true
   769  	}
   770  	return false
   771  }
   772  
   773  // getDiff is a helper function that returns string lines describing the differences between a & b
   774  func getDiff(a, b interface{}, aName, bName string) string {
   775  	var buf bytes.Buffer
   776  
   777  	av := reflect.Indirect(reflect.ValueOf(a))
   778  	bv := reflect.Indirect(reflect.ValueOf(b))
   779  
   780  	switch av.Kind() {
   781  	case reflect.Slice:
   782  		if av.Len() != bv.Len() {
   783  			buf.WriteString(fmt.Sprintf("%v has len %v, but %v has len %v\n", aName, av.Len(), bName, bv.Len()))
   784  		} else {
   785  			for i := 0; i < av.Len(); i++ {
   786  				avi := av.Index(i).Interface()
   787  				bvi := bv.Index(i).Interface()
   788  				buf.WriteString(getDiff(avi, bvi, fmt.Sprintf("%s[%d]", aName, i), fmt.Sprintf("%s[%d]", bName, i)))
   789  			}
   790  		}
   791  	case reflect.Struct:
   792  		if av.NumField() != bv.NumField() {
   793  			buf.WriteString(fmt.Sprintf("%v has %v fields, but %v has %v fields\n", aName, av.NumField(), bName, bv.NumField()))
   794  		} else {
   795  			for i := 0; i < av.NumField(); i++ {
   796  				avf := av.Field(i)
   797  				bvf := bv.Field(i)
   798  
   799  				avft := av.Type().Field(i)
   800  				bvft := bv.Type().Field(i)
   801  
   802  				avftName := fmt.Sprintf("%s.%s", aName, avft.Name)
   803  				bvftName := fmt.Sprintf("%s.%s", bName, bvft.Name)
   804  
   805  				if avft.Type != bvft.Type {
   806  					buf.WriteString(fmt.Sprintf("%v has type %v, but %v has type %v\n", avftName, avft.Type, bvftName, bvft.Type))
   807  				} else {
   808  					if avft.PkgPath == "" || avft.Anonymous || bvft.PkgPath == "" || bvft.Anonymous {
   809  						buf.WriteString(getDiff(avf.Interface(), bvf.Interface(), avftName, bvftName))
   810  					}
   811  				}
   812  			}
   813  		}
   814  	default:
   815  		if !reflect.DeepEqual(a, b) {
   816  			buf.WriteString(fmt.Sprintf("MISMATCH: %v == %v | %v == %v\n", aName, a, bName, b))
   817  		}
   818  	}
   819  
   820  	return buf.String()
   821  }
   822  
   823  func onlyErrNoSuchEntity(err error) bool {
   824  	if err == nil {
   825  		return false
   826  	}
   827  	merr, ok := err.(appengine.MultiError)
   828  	if !ok || len(merr) == 0 {
   829  		return false
   830  	}
   831  	for i := 0; i < len(merr); i++ {
   832  		if merr[i] != datastore.ErrNoSuchEntity {
   833  			return false
   834  		}
   835  	}
   836  	return true
   837  }
   838  
   839  func ivGetMulti(t *testing.T, g *Goon, ref, dst interface{}, prettyInfo string) error {
   840  	// Get our data back and make sure it's correct
   841  	if err := g.GetMulti(dst); err != nil {
   842  		t.Fatalf("%s > Unexpected error on GetMulti - %v", prettyInfo, err)
   843  		return err
   844  	} else {
   845  		dstLen := reflect.Indirect(reflect.ValueOf(dst)).Len()
   846  		refLen := reflect.Indirect(reflect.ValueOf(ref)).Len()
   847  
   848  		if dstLen != refLen {
   849  			t.Fatalf("%s > Unexpected dst len (%v) doesn't match ref len (%v)", prettyInfo, dstLen, refLen)
   850  		} else if !reflect.DeepEqual(ref, dst) {
   851  			t.Fatalf("%s > ivGetMulti didn't return what was expected:\n%s", prettyInfo, getDiff(ref, dst, "ref", "dst"))
   852  		}
   853  	}
   854  	return nil
   855  }
   856  
   857  func setPLSCounts(ref interface{}, saveCount, loadCount bool) {
   858  	// Confirm that Save() and Load() are called as specified
   859  	v := reflect.Indirect(reflect.ValueOf(ref))
   860  	for i := 0; i < v.Len(); i++ {
   861  		vi := reflect.Indirect(v.Index(i))
   862  		if vi.Kind() == reflect.Interface {
   863  			vi = reflect.Indirect(vi.Elem())
   864  		}
   865  		if saveCount {
   866  			vi.FieldByName("SaveCount").SetInt(1)
   867  		}
   868  		if loadCount {
   869  			vi.FieldByName("LoadCount").SetInt(1)
   870  		}
   871  	}
   872  }
   873  
   874  // This function marks either all or the provided indices of target as dirty.
   875  // It's purpose is to use it on entities fetched via Get,
   876  // and then see if refetching those entities returns the dirty entities.
   877  func makeDirty(target interface{}, indices ...int) {
   878  	if target == nil {
   879  		return
   880  	}
   881  	v := reflect.Indirect(reflect.ValueOf(target))
   882  	for i := 0; i < v.Len(); i++ {
   883  		found := (len(indices) == 0) // If no indices are provided, we dirty everything
   884  		for _, index := range indices {
   885  			if index == i {
   886  				found = true
   887  				break
   888  			}
   889  		}
   890  		if !found {
   891  			continue
   892  		}
   893  		vi := reflect.Indirect(v.Index(i))
   894  		if vi.Kind() == reflect.Interface {
   895  			vi = reflect.Indirect(vi.Elem())
   896  		}
   897  		vi.FieldByName("String").SetString("dirty")
   898  	}
   899  }
   900  
   901  func validateInputVariety(t *testing.T, g *Goon, srcType, dstType, mode int, txn bool) {
   902  	if mode >= ivModeTotal {
   903  		t.Fatalf("Invalid input variety mode! %v >= %v", mode, ivModeTotal)
   904  		return
   905  	}
   906  
   907  	// Generate a nice debug info string for clear logging
   908  	prettyInfo := getPrettyIVType(srcType) + " " + getPrettyIVType(dstType) + " " + getPrettyIVMode(mode)
   909  	if txn {
   910  		prettyInfo += " TXN"
   911  	}
   912  
   913  	// Generate test data with the specified types
   914  	src := getInputVarietyItem(t, g, srcType, false, 0, 1, 2)
   915  	ref := getInputVarietyItem(t, g, dstType, false, 0, 1, 2)
   916  	dstA := getInputVarietyItem(t, g, dstType, true, 0, 1, 2)
   917  	dstB := getInputVarietyItem(t, g, dstType, true, 0, 1, 2)
   918  	dstC := getInputVarietyItem(t, g, dstType, true, 0, 1, 2)
   919  
   920  	setPLSCounts(ref, isIVTypePLS(srcType), isIVTypePLS(dstType))
   921  
   922  	// Save our test data
   923  	if txn {
   924  		if err := g.RunInTransaction(func(tg *Goon) error {
   925  			_, err := tg.PutMulti(src)
   926  			return err
   927  		}, &datastore.TransactionOptions{XG: true}); err != nil {
   928  			t.Fatalf("%s > Unexpected error on PutMulti - %v", prettyInfo, err)
   929  		}
   930  	} else {
   931  		if _, err := g.PutMulti(src); err != nil {
   932  			t.Fatalf("%s > Unexpected error on PutMulti - %v", prettyInfo, err)
   933  		}
   934  	}
   935  
   936  	// Attempt an immediate get, which should catch any faulty Put-based caching
   937  	ivGetMulti(t, g, ref, dstA, prettyInfo+" PC")
   938  
   939  	// Clear the caches, as we're going to precisely set the caches via loadIVItem
   940  	// TODO: Instead of clear, fill the caches with invalid data
   941  	g.FlushLocalCache()
   942  	memcache.Flush(g.Context)
   943  
   944  	// This function just populates the cache via GetMulti
   945  	loadIVItem := func(indices ...int) {
   946  		dst := getInputVarietyItem(t, g, dstType, true, indices...)
   947  		if err := g.GetMulti(dst); err != nil {
   948  			t.Fatalf("%s > Unexpected error on GetMulti - %v", prettyInfo, err)
   949  		}
   950  		makeDirty(dst) // Make these dirty to confirm the cache doesn't reflect it
   951  	}
   952  
   953  	// Set the caches into proper state based on given mode
   954  	switch mode {
   955  	case ivModeDatastore:
   956  		// Caches already clear
   957  	case ivModeMemcache:
   958  		loadIVItem(0, 1, 2) // Left in memcache
   959  		g.FlushLocalCache()
   960  	case ivModeMemcacheAndDatastore:
   961  		loadIVItem(0, 1) // Left in memcache
   962  		g.FlushLocalCache()
   963  	case ivModeLocalcache:
   964  		loadIVItem(0, 1, 2) // Left in local cache
   965  	case ivModeLocalcacheAndMemcache:
   966  		loadIVItem(0) // Left in memcache
   967  		g.FlushLocalCache()
   968  		loadIVItem(1, 2) // Left in local cache
   969  	case ivModeLocalcacheAndDatastore:
   970  		loadIVItem(0, 1) // Left in local cache
   971  	case ivModeLocalcacheAndMemcacheAndDatastore:
   972  		loadIVItem(0) // Left in memcache
   973  		g.FlushLocalCache()
   974  		loadIVItem(1) // Left in local cache
   975  	}
   976  
   977  	// Get our data back and make sure it's correct
   978  	if txn {
   979  		if err := g.RunInTransaction(func(tg *Goon) error {
   980  			return ivGetMulti(t, tg, ref, dstB, prettyInfo+" GC")
   981  		}, &datastore.TransactionOptions{XG: true}); err != nil {
   982  			t.Fatalf("%s > Unexpected error on transaction - %v", prettyInfo, err)
   983  		}
   984  	} else {
   985  		ivGetMulti(t, g, ref, dstB, prettyInfo+" GC")
   986  	}
   987  
   988  	// Delete our data
   989  	if txn {
   990  		if err := g.RunInTransaction(func(tg *Goon) error {
   991  			return tg.DeleteMulti(ivItemKeys)
   992  		}, &datastore.TransactionOptions{XG: true}); err != nil {
   993  			t.Fatalf("%s > Unexpected error on DeleteMulti - %v", prettyInfo, err)
   994  		}
   995  	} else {
   996  		if err := g.DeleteMulti(ivItemKeys); err != nil {
   997  			t.Fatalf("%s > Unexpected error on DeleteMulti - %v", prettyInfo, err)
   998  		}
   999  	}
  1000  
  1001  	// Make sure our data isn't retrievable from any layer
  1002  	if err := g.GetMulti(dstC); !onlyErrNoSuchEntity(err) {
  1003  		t.Fatalf("%s > Expected ErrNoSuchEntity but got %v", prettyInfo, err)
  1004  	}
  1005  
  1006  	// Do final clean-up of any negative cache
  1007  	g.FlushLocalCache()
  1008  	memcache.Flush(g.Context)
  1009  }
  1010  
  1011  func TestInputVariety(t *testing.T) {
  1012  	c, done, err := aetest.NewContext()
  1013  	if err != nil {
  1014  		t.Fatalf("Could not start aetest - %v", err)
  1015  	}
  1016  	defer done()
  1017  	g := FromContext(c)
  1018  
  1019  	initializeIvItems(c)
  1020  
  1021  	for srcType := 0; srcType < ivTypeTotal; srcType++ {
  1022  		for dstType := 0; dstType < ivTypeTotal; dstType++ {
  1023  			for mode := 0; mode < ivModeTotal; mode++ {
  1024  				for txn := 0; txn < 2; txn++ {
  1025  					validateInputVariety(t, g, srcType, dstType, mode, txn == 1)
  1026  				}
  1027  			}
  1028  		}
  1029  	}
  1030  }
  1031  
  1032  func TestSerialization(t *testing.T) {
  1033  	c, done, err := aetest.NewContext()
  1034  	if err != nil {
  1035  		t.Fatalf("Could not start aetest - %v", err)
  1036  	}
  1037  	defer done()
  1038  
  1039  	initializeIvItems(c)
  1040  
  1041  	// Test that size is the same in back-to-back
  1042  	iviOut := ivItems[0].clone()
  1043  	iviOut.SaveCount = 1 // Set it to non-zero so that serialized size will match PLS
  1044  	data, err := serializeStruct(iviOut)
  1045  	if err != nil {
  1046  		t.Fatalf("Failed to serialize iviOut: %v", err)
  1047  	}
  1048  	dataB, err := serializeStruct(iviOut)
  1049  	if err != nil {
  1050  		t.Fatalf("Failed to serialize iviOut: %v", err)
  1051  	}
  1052  	if len(data) != len(dataB) {
  1053  		t.Fatalf("Back-to-back serialization returned different length data: %v != %v", len(data), len(dataB))
  1054  	}
  1055  
  1056  	// Test that we can deserialize back to the struct
  1057  	iviIn := &ivItem{}
  1058  	err = deserializeStruct(iviIn, data)
  1059  	if err != nil {
  1060  		t.Fatalf("Failed to deserialize to iviIn: %v", err)
  1061  	}
  1062  	iviIn.Id = iviOut.Id // Manually set the id
  1063  	if !reflect.DeepEqual(iviOut, iviIn) {
  1064  		t.Errorf("Invalid result!\n%v", getDiff(iviOut, iviIn, "iviOut", "iviIn"))
  1065  	}
  1066  
  1067  	// PropertyLoadSaver serialization
  1068  	var iviplsOut *ivItemPLS
  1069  	iviplsOut = (*ivItemPLS)(ivItems[0].clone())
  1070  	dataPLS, err := serializeStruct(iviplsOut)
  1071  	if err != nil {
  1072  		t.Fatalf("Failed to serialize iviplsOut: %v", err)
  1073  	}
  1074  	iviplsIn := &ivItemPLS{}
  1075  	err = deserializeStruct(iviplsIn, dataPLS)
  1076  	if err != nil {
  1077  		t.Fatalf("Failed to deserialize to iviplsIn: %v", err)
  1078  	}
  1079  	iviplsIn.Id = iviplsOut.Id               // Manually set the id
  1080  	iviplsIn.LoadCount = iviplsOut.LoadCount // Reset the load count
  1081  	if !reflect.DeepEqual(iviplsOut, iviplsIn) {
  1082  		t.Errorf("Invalid PLS result!\n%v", getDiff(iviplsOut, iviplsIn, "iviplsOut", "iviplsIn"))
  1083  
  1084  	}
  1085  
  1086  	// Make sure both normal & PLS result in the same length data
  1087  	if len(data) != len(dataPLS) {
  1088  		t.Fatalf("Serialization returned different length data for normal vs PLS: %v != %v", len(data), len(dataPLS))
  1089  	}
  1090  
  1091  	t.Logf("data size: %v", len(data))
  1092  
  1093  	// Test that the retrieved data is stable
  1094  	s1, s2 := &HasId{Id: 1, Name: "qqq"}, &HasId{Id: 2, Name: "zzzz"}
  1095  	d1, err := serializeStruct(s1)
  1096  	if err != nil {
  1097  		t.Fatalf("Failed to serialize: %v", err)
  1098  	}
  1099  	d1Copy := make([]byte, len(d1))
  1100  	copy(d1Copy, d1)
  1101  	_, err = serializeStruct(s2)
  1102  	if err != nil {
  1103  		t.Fatalf("Failed to serialize: %v", err)
  1104  	}
  1105  	if !bytes.Equal(d1, d1Copy) {
  1106  		t.Fatalf("Serialization bytes are not stable! Expected %x but got %x", d1Copy, d1)
  1107  	}
  1108  }
  1109  
  1110  type dummyPLS struct {
  1111  	Id     int64  `datastore:"-" goon:"id"`
  1112  	ValueA string `datastore:"a"`
  1113  	ValueB string `datastore:"-"`
  1114  }
  1115  
  1116  func (d *dummyPLS) Save() ([]datastore.Property, error) {
  1117  	props, err := datastore.SaveStruct(d)
  1118  	if err != nil {
  1119  		return nil, err
  1120  	}
  1121  	props = append([]datastore.Property{{Name: "ValueB" + d.ValueB, NoIndex: true, Multiple: true, Value: nil}}, props...)
  1122  	return props, nil
  1123  }
  1124  
  1125  func (d *dummyPLS) Load(props []datastore.Property) error {
  1126  	for _, prop := range props {
  1127  		if strings.HasPrefix(prop.Name, "ValueB") && prop.NoIndex && prop.Multiple && prop.Value == nil {
  1128  			d.ValueB = prop.Name[len("ValueB"):]
  1129  			break
  1130  		}
  1131  	}
  1132  	return datastore.LoadStruct(d, props)
  1133  }
  1134  
  1135  // Tests that only matter for PLS and can't be done via ivItem
  1136  func TestPropertyLoadSaver(t *testing.T) {
  1137  	c, done, err := aetest.NewContext()
  1138  	if err != nil {
  1139  		t.Fatalf("Could not start aetest - %v", err)
  1140  	}
  1141  	defer done()
  1142  	g := FromContext(c)
  1143  
  1144  	// Save the entity
  1145  	dA := &dummyPLS{Id: 1, ValueA: "one", ValueB: "two"}
  1146  	if _, err := g.Put(dA); err != nil {
  1147  		t.Fatalf("Unexpected error on Put: %v", err)
  1148  	}
  1149  
  1150  	for i := 0; i < 5; i++ {
  1151  		switch i {
  1152  		case 0:
  1153  			// Test immediately after Put, leave caches as is
  1154  		case 1:
  1155  			// Clear the local cache to test memcache
  1156  			g.FlushLocalCache()
  1157  		case 2:
  1158  			// Clear both caches to test datastore
  1159  			g.FlushLocalCache()
  1160  			memcache.Flush(g.Context)
  1161  		case 3:
  1162  			// Test local cache from the Get
  1163  		case 4:
  1164  			// Clear the local cache to test memcache from the Get
  1165  			g.FlushLocalCache()
  1166  		}
  1167  
  1168  		dB := &dummyPLS{Id: dA.Id}
  1169  		if err := g.Get(dB); err != nil {
  1170  			t.Fatalf("Unexpected error on Get #%d: %v", i, err)
  1171  		}
  1172  		if !reflect.DeepEqual(dA, dB) {
  1173  			t.Errorf("dA & dB don't match #%d!\n%s", i, getDiff(dA, dB, "dA", "dB"))
  1174  		}
  1175  	}
  1176  }
  1177  
  1178  type MigrationEntity interface {
  1179  	number() int32
  1180  	parent() *datastore.Key
  1181  }
  1182  
  1183  type MigrationA struct {
  1184  	_kind            string            `goon:"kind,Migration"`
  1185  	Parent           *datastore.Key    `datastore:"-" goon:"parent"`
  1186  	Id               int64             `datastore:"-" goon:"id"`
  1187  	Number           int32             `datastore:"number"`
  1188  	Word             string            `datastore:"word,noindex"`
  1189  	Car              string            `datastore:"car,noindex"`
  1190  	Holiday          time.Time         `datastore:"holiday,noindex"`
  1191  	α                int               `datastore:",noindex"`
  1192  	Level            MigrationIntA     `datastore:"level,noindex"`
  1193  	Floor            MigrationIntA     `datastore:"floor,noindex"`
  1194  	BunchOfBytes     MigrationBSSA     `datastore:"bb,noindex"`
  1195  	Sub              MigrationSub      `datastore:"sub,noindex"`
  1196  	Son              MigrationPerson   `datastore:"son,noindex"`
  1197  	Daughter         MigrationPerson   `datastore:"daughter,noindex"`
  1198  	Parents          []MigrationPerson `datastore:"parents,noindex"`
  1199  	DeepSlice        MigrationDeepA    `datastore:"deep,noindex"`
  1200  	ZZs              []ZigZag          `datastore:"zigzag,noindex"`
  1201  	ZeroKey          *datastore.Key    `datastore:",noindex"`
  1202  	File             []byte
  1203  	LoadCount        int
  1204  	DeprecatedField  string       `datastore:"depf,noindex"`
  1205  	DeprecatedStruct MigrationSub `datastore:"deps,noindex"`
  1206  	FinalField       string       `datastore:"final,noindex"` // This should always be last, to test deprecating middle properties
  1207  }
  1208  
  1209  func (m MigrationA) parent() *datastore.Key {
  1210  	return m.Parent
  1211  }
  1212  
  1213  func (m MigrationA) number() int32 {
  1214  	return m.Number
  1215  }
  1216  
  1217  type MigrationSub struct {
  1218  	Data  string          `datastore:"data,noindex"`
  1219  	Noise []int           `datastore:"noise,noindex"`
  1220  	Sub   MigrationSubSub `datastore:"sub,noindex"`
  1221  }
  1222  
  1223  type MigrationSubSub struct {
  1224  	Data string `datastore:"data,noindex"`
  1225  }
  1226  
  1227  type MigrationPerson struct {
  1228  	Name string `datastore:"name,noindex"`
  1229  	Age  int    `datastore:"age,noindex"`
  1230  }
  1231  
  1232  type MigrationDeepA struct {
  1233  	Deep MigrationDeepB `datastore:"deep,noindex"`
  1234  }
  1235  
  1236  type MigrationDeepB struct {
  1237  	Deep MigrationDeepC `datastore:"deep,noindex"`
  1238  }
  1239  
  1240  type MigrationDeepC struct {
  1241  	Slice []int `datastore:"slice,noindex"`
  1242  }
  1243  
  1244  type ZigZag struct {
  1245  	Zig int `datastore:"zig,noindex"`
  1246  	Zag int `datastore:"zag,noindex"`
  1247  }
  1248  
  1249  type ZigZags struct {
  1250  	Zig []int `datastore:"zig,noindex"`
  1251  	Zag []int `datastore:"zag,noindex"`
  1252  }
  1253  
  1254  type MigrationIntA int
  1255  type MigrationIntB int
  1256  
  1257  type MigrationBSA []byte
  1258  type MigrationBSB []byte
  1259  type MigrationBSSA []MigrationBSA
  1260  type MigrationBSSB []MigrationBSB
  1261  
  1262  type MigrationB struct {
  1263  	_kind          string            `goon:"kind,Migration"`
  1264  	Parent         *datastore.Key    `datastore:"-" goon:"parent"`
  1265  	Identification int64             `datastore:"-" goon:"id"`
  1266  	FancyNumber    int32             `datastore:"number"`
  1267  	Slang          string            `datastore:"word,noindex"`
  1268  	Cars           []string          `datastore:"car,noindex"`
  1269  	Holidays       []time.Time       `datastore:"holiday,noindex"`
  1270  	β              int               `datastore:"α,noindex"`
  1271  	Level          MigrationIntB     `datastore:"level,noindex"`
  1272  	Floors         []MigrationIntB   `datastore:"floor,noindex"`
  1273  	BunchOfBytes   MigrationBSSB     `datastore:"bb,noindex"`
  1274  	Animal         string            `datastore:"sub.data,noindex"`
  1275  	Music          []int             `datastore:"sub.noise,noindex"`
  1276  	Flower         string            `datastore:"sub.sub.data,noindex"`
  1277  	Sons           []MigrationPerson `datastore:"son,noindex"`
  1278  	DaughterName   string            `datastore:"daughter.name,noindex"`
  1279  	DaughterAge    int               `datastore:"daughter.age,noindex"`
  1280  	OldFolks       []MigrationPerson `datastore:"parents,noindex"`
  1281  	FarSlice       MigrationDeepA    `datastore:"deep,noindex"`
  1282  	ZZs            ZigZags           `datastore:"zigzag,noindex"`
  1283  	Keys           []*datastore.Key  `datastore:"ZeroKey,noindex"`
  1284  	Files          [][]byte          `datastore:"File,noindex"`
  1285  	LoadCount      int               `datastore:"LoadCount,noindex"`
  1286  	FinalField     string            `datastore:"final,noindex"`
  1287  }
  1288  
  1289  func (m MigrationB) parent() *datastore.Key {
  1290  	return m.Parent
  1291  }
  1292  
  1293  func (m MigrationB) number() int32 {
  1294  	return m.FancyNumber
  1295  }
  1296  
  1297  // MigrationA with PropertyLoadSaver interface
  1298  type MigrationPlsA MigrationA
  1299  
  1300  // MigrationB with PropertyLoadSaver interface
  1301  type MigrationPlsB MigrationB
  1302  
  1303  func (m MigrationPlsA) parent() *datastore.Key {
  1304  	return m.Parent
  1305  }
  1306  func (m MigrationPlsA) number() int32 {
  1307  	return m.Number
  1308  }
  1309  func (m *MigrationPlsA) Save() ([]datastore.Property, error) {
  1310  	return datastore.SaveStruct(m)
  1311  }
  1312  func (m *MigrationPlsA) Load(props []datastore.Property) error {
  1313  	err := datastore.LoadStruct(m, props)
  1314  	m.LoadCount++
  1315  	return err
  1316  }
  1317  
  1318  func (m MigrationPlsB) parent() *datastore.Key {
  1319  	return m.Parent
  1320  }
  1321  func (m MigrationPlsB) number() int32 {
  1322  	return m.FancyNumber
  1323  }
  1324  func (m *MigrationPlsB) Save() ([]datastore.Property, error) {
  1325  	return datastore.SaveStruct(m)
  1326  }
  1327  func (m *MigrationPlsB) Load(props []datastore.Property) error {
  1328  	err := datastore.LoadStruct(m, props)
  1329  	m.LoadCount++
  1330  	return err
  1331  }
  1332  
  1333  // Make sure these implement datastore.PropertyLoadSaver
  1334  var _, _ datastore.PropertyLoadSaver = &MigrationPlsA{}, &MigrationPlsB{}
  1335  
  1336  const (
  1337  	migrationMethodGet = iota
  1338  	migrationMethodGetAll
  1339  	migrationMethodGetAllMulti
  1340  	migrationMethodNext
  1341  	migrationMethodCount
  1342  )
  1343  
  1344  func TestMigration(t *testing.T) {
  1345  	c, done, err := aetest.NewContext()
  1346  	if err != nil {
  1347  		t.Fatalf("Could not start aetest - %v", err)
  1348  	}
  1349  	defer done()
  1350  
  1351  	origIFM := IgnoreFieldMismatch
  1352  	defer func() {
  1353  		IgnoreFieldMismatch = origIFM
  1354  	}()
  1355  
  1356  	g := FromContext(c)
  1357  
  1358  	// Create & save an entity with the original structure
  1359  	parentKey := g.Key(&HasId{Id: 9999})
  1360  	migA := &MigrationA{Parent: parentKey, Id: 1, Number: 123, Word: "rabbit", Car: "BMW",
  1361  		Holiday: time.Now().UTC().Truncate(time.Microsecond), α: 1, Level: 9001, Floor: 5,
  1362  		BunchOfBytes: MigrationBSSA{MigrationBSA{0x01, 0x02}, MigrationBSA{0x03, 0x04}},
  1363  		Sub:          MigrationSub{Data: "fox", Noise: []int{1, 2, 3}, Sub: MigrationSubSub{Data: "rose"}},
  1364  		Son:          MigrationPerson{Name: "John", Age: 5}, Daughter: MigrationPerson{Name: "Nancy", Age: 6},
  1365  		Parents:   []MigrationPerson{{Name: "Sven", Age: 56}, {Name: "Sonya", Age: 49}},
  1366  		DeepSlice: MigrationDeepA{Deep: MigrationDeepB{Deep: MigrationDeepC{Slice: []int{1, 2, 3}}}},
  1367  		ZZs:       []ZigZag{{Zig: 1}, {Zag: 1}}, File: []byte{0xF0, 0x0D},
  1368  		DeprecatedField: "dep", DeprecatedStruct: MigrationSub{Data: "dep", Noise: []int{1, 2, 3}}, FinalField: "fin"}
  1369  	if _, err := g.Put(migA); err != nil {
  1370  		t.Fatalf("Unexpected error on Put: %v", err)
  1371  	}
  1372  	// Also save an already migrated structure
  1373  	migB := &MigrationB{Parent: migA.Parent, Identification: migA.Id + 1, FancyNumber: migA.Number + 1}
  1374  	if _, err := g.Put(migB); err != nil {
  1375  		t.Fatalf("Unexpected error on Put: %v", err)
  1376  	}
  1377  
  1378  	// Run migration tests with both IgnoreFieldMismatch on & off
  1379  	for _, IgnoreFieldMismatch = range []bool{true, false} {
  1380  		testcase := []struct {
  1381  			name string
  1382  			src  MigrationEntity
  1383  			dst  MigrationEntity
  1384  		}{
  1385  			{
  1386  				name: "NormalCache -> NormalCache",
  1387  				src:  &MigrationA{Parent: parentKey, Id: 1},
  1388  				dst:  &MigrationB{Parent: parentKey, Identification: 1},
  1389  			},
  1390  			{
  1391  				name: "PropertyListCache -> NormalCache",
  1392  				src:  &MigrationPlsA{Parent: parentKey, Id: 1},
  1393  				dst:  &MigrationB{Parent: parentKey, Identification: 1},
  1394  			},
  1395  			{
  1396  				name: "PropertyListCache -> PropertyListCache",
  1397  				src:  &MigrationPlsA{Parent: parentKey, Id: 1},
  1398  				dst:  &MigrationPlsB{Parent: parentKey, Identification: 1},
  1399  			},
  1400  			{
  1401  				name: "NormalCache -> PropertyListCache",
  1402  				src:  &MigrationA{Parent: parentKey, Id: 1},
  1403  				dst:  &MigrationPlsB{Parent: parentKey, Identification: 1},
  1404  			},
  1405  		}
  1406  		for _, tt := range testcase {
  1407  			// Clear all the caches
  1408  			g.FlushLocalCache()
  1409  			memcache.Flush(c)
  1410  
  1411  			// Get it back, so it's in the cache
  1412  			if err := g.Get(tt.src); err != nil {
  1413  				t.Fatalf("Unexpected error on Get: %v", err)
  1414  			}
  1415  
  1416  			// Test whether local cache supports migration
  1417  			var fetched MigrationEntity
  1418  			debugInfo := fmt.Sprintf("%s LC-%v", tt.name, IgnoreFieldMismatch)
  1419  			fetched = verifyMigration(t, g, tt.src, tt.dst, migrationMethodGet, debugInfo)
  1420  			checkMigrationResult(t, g, tt.src, fetched, debugInfo)
  1421  
  1422  			// Clear the local cache, to test memcache
  1423  			g.FlushLocalCache()
  1424  
  1425  			// Test whether memcache supports migration
  1426  			debugInfo = fmt.Sprintf("%s MC-%v", tt.name, IgnoreFieldMismatch)
  1427  			fetched = verifyMigration(t, g, tt.src, tt.dst, migrationMethodGet, debugInfo)
  1428  			checkMigrationResult(t, g, tt.src, fetched, debugInfo)
  1429  
  1430  			// Test whether datastore supports migration
  1431  			for method := 0; method < migrationMethodCount; method++ {
  1432  				// Test both inside a transaction and outside
  1433  				for tx := 0; tx < 2; tx++ {
  1434  					// Clear all the caches
  1435  					g.FlushLocalCache()
  1436  					memcache.Flush(c)
  1437  
  1438  					debugInfo := fmt.Sprintf("%s DS-%v-%v-%v", tt.name, tx, method, IgnoreFieldMismatch)
  1439  					if tx == 1 {
  1440  						if err := g.RunInTransaction(func(tg *Goon) error {
  1441  							fetched = verifyMigration(t, tg, tt.src, tt.dst, method, debugInfo)
  1442  							return nil
  1443  						}, &datastore.TransactionOptions{XG: false}); err != nil {
  1444  							t.Fatalf("Unexpected error with TXN - %v", err)
  1445  						}
  1446  					} else {
  1447  						fetched = verifyMigration(t, g, tt.src, tt.dst, method, debugInfo)
  1448  					}
  1449  					checkMigrationResult(t, g, tt.src, fetched, debugInfo)
  1450  				}
  1451  			}
  1452  		}
  1453  	}
  1454  }
  1455  
  1456  func verifyMigration(t *testing.T, g *Goon, src, dst MigrationEntity, method int, debugInfo string) (fetched MigrationEntity) {
  1457  	sliceType := reflect.SliceOf(reflect.TypeOf(dst))
  1458  	slicePtr := reflect.New(sliceType)
  1459  	slicePtr.Elem().Set(reflect.MakeSlice(sliceType, 0, 10))
  1460  	slice := slicePtr.Interface()
  1461  	sliceVal := slicePtr.Elem()
  1462  
  1463  	switch method {
  1464  	case migrationMethodGet:
  1465  		if err := g.Get(dst); err != nil && (IgnoreFieldMismatch || !errFieldMismatch(err)) {
  1466  			t.Fatalf("%v > Unexpected error on Get: %v", debugInfo, err)
  1467  			return
  1468  		}
  1469  		return dst
  1470  	case migrationMethodGetAll:
  1471  		if _, err := g.GetAll(datastore.NewQuery("Migration").Ancestor(src.parent()).Filter("number=", src.number()), slice); err != nil && (IgnoreFieldMismatch || !errFieldMismatch(err)) {
  1472  			t.Fatalf("%v > Unexpected error on GetAll: %v", debugInfo, err)
  1473  			return
  1474  		} else if sliceVal.Len() != 1 {
  1475  			t.Fatalf("%v > Unexpected query result, expected %v entities, got %v", debugInfo, 1, sliceVal.Len())
  1476  			return
  1477  		}
  1478  		return sliceVal.Index(0).Interface().(MigrationEntity)
  1479  	case migrationMethodGetAllMulti:
  1480  		// Get both Migration entities
  1481  		if _, err := g.GetAll(datastore.NewQuery("Migration").Ancestor(src.parent()).Order("number"), slice); err != nil && (IgnoreFieldMismatch || !errFieldMismatch(err)) {
  1482  			t.Fatalf("%v > Unexpected error on GetAll: %v", debugInfo, err)
  1483  			return
  1484  		} else if sliceVal.Len() != 2 {
  1485  			t.Fatalf("%v > Unexpected query result, expected %v entities, got %v", debugInfo, 2, sliceVal.Len())
  1486  			return
  1487  		}
  1488  		return sliceVal.Index(0).Interface().(MigrationEntity)
  1489  	case migrationMethodNext:
  1490  		it := g.Run(datastore.NewQuery("Migration").Ancestor(src.parent()).Filter("number=", src.number()))
  1491  		if _, err := it.Next(dst); err != nil && (IgnoreFieldMismatch || !errFieldMismatch(err)) {
  1492  			t.Fatalf("%v > Unexpected error on Next: %v", debugInfo, err)
  1493  			return
  1494  		}
  1495  		// Make sure the iterator ends correctly
  1496  		if _, err := it.Next(dst); err != datastore.Done {
  1497  			t.Fatalf("Next: expected iterator to end with the error datastore.Done, got %v", err)
  1498  		}
  1499  		return dst
  1500  	}
  1501  	return nil
  1502  }
  1503  
  1504  func checkMigrationResult(t *testing.T, g *Goon, src, fetched interface{}, debugInfo string) {
  1505  	var migA *MigrationA
  1506  	switch v := src.(type) {
  1507  	case *MigrationA:
  1508  		migA = v
  1509  	case *MigrationPlsA:
  1510  		migA = (*MigrationA)(v)
  1511  	}
  1512  	var migB *MigrationB
  1513  	switch v := fetched.(type) {
  1514  	case *MigrationB:
  1515  		migB = v
  1516  	case *MigrationPlsB:
  1517  		migB = (*MigrationB)(v)
  1518  
  1519  		if migB.LoadCount != 1 {
  1520  			t.Errorf("%v > Expected LoadCount 1 but got %d", debugInfo, migB.LoadCount)
  1521  		}
  1522  	}
  1523  
  1524  	if migA.Id != migB.Identification {
  1525  		t.Errorf("%v > Ids don't match: %v != %v", debugInfo, migA.Id, migB.Identification)
  1526  	} else if migA.Number != migB.FancyNumber {
  1527  		t.Errorf("%v > Numbers don't match: %v != %v", debugInfo, migA.Number, migB.FancyNumber)
  1528  	} else if migA.Word != migB.Slang {
  1529  		t.Errorf("%v > Words don't match: %v != %v", debugInfo, migA.Word, migB.Slang)
  1530  	} else if len(migB.Cars) != 1 {
  1531  		t.Fatalf("%v > Expected 1 car! Got: %v", debugInfo, len(migB.Cars))
  1532  	} else if migA.Car != migB.Cars[0] {
  1533  		t.Errorf("%v > Cars don't match: %v != %v", debugInfo, migA.Car, migB.Cars[0])
  1534  	} else if len(migB.Holidays) != 1 {
  1535  		t.Fatalf("%v > Expected 1 holiday! Got: %v", debugInfo, len(migB.Holidays))
  1536  	} else if migA.Holiday != migB.Holidays[0] {
  1537  		t.Errorf("%v > Holidays don't match: %v != %v", debugInfo, migA.Holiday, migB.Holidays[0])
  1538  	} else if migA.α != migB.β {
  1539  		t.Errorf("%v > Greek doesn't match: %v != %v", debugInfo, migA.α, migB.β)
  1540  	} else if int(migA.Level) != int(migB.Level) {
  1541  		t.Errorf("%v > Level doesn't match: %v != %v", debugInfo, migA.Level, migB.Level)
  1542  	} else if len(migB.Floors) != 1 {
  1543  		t.Fatalf("%v > Expected 1 floor! Got: %v", debugInfo, len(migB.Floors))
  1544  	} else if int(migA.Floor) != int(migB.Floors[0]) {
  1545  		t.Errorf("%v > Floor doesn't match: %v != %v", debugInfo, migA.Floor, migB.Floors[0])
  1546  	} else if len(migA.BunchOfBytes) != len(migB.BunchOfBytes) || len(migA.BunchOfBytes) != 2 {
  1547  		t.Fatalf("%v > BunchOfBytes len doesn't match (expected 2): %v != %v", debugInfo, len(migA.BunchOfBytes), len(migB.BunchOfBytes))
  1548  	} else if !reflect.DeepEqual([]byte(migA.BunchOfBytes[0]), []byte(migB.BunchOfBytes[0])) ||
  1549  		!reflect.DeepEqual([]byte(migA.BunchOfBytes[1]), []byte(migB.BunchOfBytes[1])) {
  1550  		t.Errorf("%v > BunchOfBytes doesn't match: %+v != %+v", debugInfo, migA.BunchOfBytes, migB.BunchOfBytes)
  1551  	} else if migA.Sub.Data != migB.Animal {
  1552  		t.Errorf("%v > Animal doesn't match: %v != %v", debugInfo, migA.Sub.Data, migB.Animal)
  1553  	} else if !reflect.DeepEqual(migA.Sub.Noise, migB.Music) {
  1554  		t.Errorf("%v > Music doesn't match: %v != %v", debugInfo, migA.Sub.Noise, migB.Music)
  1555  	} else if migA.Sub.Sub.Data != migB.Flower {
  1556  		t.Errorf("%v > Flower doesn't match: %v != %v", debugInfo, migA.Sub.Sub.Data, migB.Flower)
  1557  	} else if len(migB.Sons) != 1 {
  1558  		t.Fatalf("%v > Expected 1 son! Got: %v", debugInfo, len(migB.Sons))
  1559  	} else if migA.Son.Name != migB.Sons[0].Name {
  1560  		t.Errorf("%v > Son names don't match: %v != %v", debugInfo, migA.Son.Name, migB.Sons[0].Name)
  1561  	} else if migA.Son.Age != migB.Sons[0].Age {
  1562  		t.Errorf("%v > Son ages don't match: %v != %v", debugInfo, migA.Son.Age, migB.Sons[0].Age)
  1563  	} else if migA.Daughter.Name != migB.DaughterName {
  1564  		t.Errorf("%v > Daughter names don't match: %v != %v", debugInfo, migA.Daughter.Name, migB.DaughterName)
  1565  	} else if migA.Daughter.Age != migB.DaughterAge {
  1566  		t.Errorf("%v > Daughter ages don't match: %v != %v", debugInfo, migA.Daughter.Age, migB.DaughterAge)
  1567  	} else if !reflect.DeepEqual(migA.Parents, migB.OldFolks) {
  1568  		t.Errorf("%v > Parents don't match: %v != %v", debugInfo, migA.Parents, migB.OldFolks)
  1569  	} else if !reflect.DeepEqual(migA.DeepSlice, migB.FarSlice) {
  1570  		t.Errorf("%v > Deep slice doesn't match: %v != %v", debugInfo, migA.DeepSlice, migB.FarSlice)
  1571  	} else if len(migB.ZZs.Zig) != 2 {
  1572  		t.Fatalf("%v > Expected 2 Zigs, got: %v", debugInfo, len(migB.ZZs.Zig))
  1573  	} else if len(migB.ZZs.Zag) != 2 {
  1574  		t.Fatalf("%v > Expected 2 Zags, got: %v", debugInfo, len(migB.ZZs.Zag))
  1575  	} else if migA.ZZs[0].Zig != migB.ZZs.Zig[0] {
  1576  		t.Errorf("%v > Invalid zig #1: %v != %v", debugInfo, migA.ZZs[0].Zig, migB.ZZs.Zig[0])
  1577  	} else if migA.ZZs[1].Zig != migB.ZZs.Zig[1] {
  1578  		t.Errorf("%v > Invalid zig #2: %v != %v", debugInfo, migA.ZZs[1].Zig, migB.ZZs.Zig[1])
  1579  	} else if migA.ZZs[0].Zag != migB.ZZs.Zag[0] {
  1580  		t.Errorf("%v > Invalid zag #1: %v != %v", debugInfo, migA.ZZs[0].Zag, migB.ZZs.Zag[0])
  1581  	} else if migA.ZZs[1].Zag != migB.ZZs.Zag[1] {
  1582  		t.Errorf("%v > Invalid zag #2: %v != %v", debugInfo, migA.ZZs[1].Zag, migB.ZZs.Zag[1])
  1583  	} else if len(migB.Keys) != 1 {
  1584  		t.Fatalf("%v > Expected 1 keys, got %v", debugInfo, len(migB.Keys))
  1585  	} else if len(migB.Files) != 1 {
  1586  		t.Fatalf("%v > Expected 1 file, got %v", debugInfo, len(migB.Files))
  1587  	} else if !reflect.DeepEqual(migA.File, migB.Files[0]) {
  1588  		t.Errorf("%v > Files don't match: %v != %v", debugInfo, migA.File, migB.Files[0])
  1589  	} else if migA.FinalField != migB.FinalField {
  1590  		t.Errorf("%v > FinalField doesn't match: %v != %v", debugInfo, migA.FinalField, migB.FinalField)
  1591  	}
  1592  }
  1593  
  1594  func TestTXNRace(t *testing.T) {
  1595  	c, done, err := aetest.NewContext()
  1596  	if err != nil {
  1597  		t.Fatalf("Could not start aetest - %v", err)
  1598  	}
  1599  	defer done()
  1600  	g := FromContext(c)
  1601  
  1602  	// Create & store some test data
  1603  	hid := &HasId{Id: 1, Name: "foo"}
  1604  	if _, err := g.Put(hid); err != nil {
  1605  		t.Fatalf("Unexpected error on Put %v", err)
  1606  	}
  1607  
  1608  	// Make sure the local cache is empty
  1609  	g.FlushLocalCache()
  1610  
  1611  	// Get this data back, to populate caches
  1612  	if err := g.Get(hid); err != nil {
  1613  		t.Fatalf("Unexpected error on Get %v", err)
  1614  	}
  1615  
  1616  	// Clear the local cache, as we are testing for proper memcache usage
  1617  	g.FlushLocalCache()
  1618  
  1619  	// Update the test data inside a transction
  1620  	if err := g.RunInTransaction(func(tg *Goon) error {
  1621  		// Get the current data
  1622  		thid := &HasId{Id: 1}
  1623  		if err := tg.Get(thid); err != nil {
  1624  			t.Fatalf("Unexpected error on TXN Get %v", err)
  1625  			return err
  1626  		}
  1627  
  1628  		// Update the data
  1629  		thid.Name = "bar"
  1630  		if _, err := tg.Put(thid); err != nil {
  1631  			t.Fatalf("Unexpected error on TXN Put %v", err)
  1632  			return err
  1633  		}
  1634  
  1635  		// Concurrent request emulation
  1636  		//   We are running this inside the transaction block to always get the correct timing for testing.
  1637  		//   In the real world, this concurrent request may run in another instance.
  1638  		//   The transaction block may contain multiple other operations after the preceding Put(),
  1639  		//   allowing for ample time for the concurrent request to run before the transaction is committed.
  1640  		hid = &HasId{Id: 1}
  1641  		if err := g.Get(hid); err != nil {
  1642  			t.Fatalf("Unexpected error on Get %v", err)
  1643  		} else if hid.Name != "foo" {
  1644  			t.Fatalf("Expected 'foo', got %v", hid.Name)
  1645  		}
  1646  
  1647  		// Commit the transaction
  1648  		return nil
  1649  	}, &datastore.TransactionOptions{XG: false}); err != nil {
  1650  		t.Fatalf("Unexpected error with TXN - %v", err)
  1651  	}
  1652  
  1653  	// Clear the local cache, as we are testing for proper memcache usage
  1654  	g.FlushLocalCache()
  1655  
  1656  	// Get the data back again, to confirm it was changed in the transaction
  1657  	hid = &HasId{Id: 1}
  1658  	if err := g.Get(hid); err != nil {
  1659  		t.Fatalf("Unexpected error on Get %v", err)
  1660  	} else if hid.Name != "bar" {
  1661  		t.Fatalf("Expected 'bar', got %v", hid.Name)
  1662  	}
  1663  
  1664  	// Clear the local cache, as we are testing for proper memcache usage
  1665  	g.FlushLocalCache()
  1666  
  1667  	// Delete the test data inside a transction
  1668  	if err := g.RunInTransaction(func(tg *Goon) error {
  1669  		thid := &HasId{Id: 1}
  1670  		if err := tg.Delete(thid); err != nil {
  1671  			t.Fatalf("Unexpected error on TXN Delete %v", err)
  1672  			return err
  1673  		}
  1674  
  1675  		// Concurrent request emulation
  1676  		hid = &HasId{Id: 1}
  1677  		if err := g.Get(hid); err != nil {
  1678  			t.Fatalf("Unexpected error on Get %v", err)
  1679  		} else if hid.Name != "bar" {
  1680  			t.Fatalf("Expected 'bar', got %v", hid.Name)
  1681  		}
  1682  
  1683  		// Commit the transaction
  1684  		return nil
  1685  	}, &datastore.TransactionOptions{XG: false}); err != nil {
  1686  		t.Fatalf("Unexpected error with TXN - %v", err)
  1687  	}
  1688  
  1689  	// Clear the local cache, as we are testing for proper memcache usage
  1690  	g.FlushLocalCache()
  1691  
  1692  	// Attempt to get the data back again, to confirm it was deleted in the transaction
  1693  	hid = &HasId{Id: 1}
  1694  	if err := g.Get(hid); err != datastore.ErrNoSuchEntity {
  1695  		t.Errorf("Expected ErrNoSuchEntity, got %v", err)
  1696  	}
  1697  }
  1698  
  1699  func TestNegativeCacheHit(t *testing.T) {
  1700  	c, done, err := aetest.NewContext()
  1701  	if err != nil {
  1702  		t.Fatalf("Could not start aetest - %v", err)
  1703  	}
  1704  	defer done()
  1705  	g := FromContext(c)
  1706  
  1707  	// Run twice to test local cache & memcache
  1708  	for _, mode := range []string{"MC", "LC"} {
  1709  		debugInfo := fmt.Sprintf("%s", mode)
  1710  
  1711  		hid := &HasId{Id: rand.Int63()}
  1712  		if err := g.Get(hid); err != datastore.ErrNoSuchEntity {
  1713  			t.Fatalf("%s > Expected ErrNoSuchEntity, got %v", debugInfo, err)
  1714  		}
  1715  
  1716  		// Do a sneaky save straight to the datastore
  1717  		if _, err := datastore.Put(c, datastore.NewKey(c, "HasId", "", hid.Id, nil), &HasId{Id: hid.Id, Name: "one"}); err != nil {
  1718  			t.Fatalf("%s > Unexpected error on datastore.Put: %v", debugInfo, err)
  1719  		}
  1720  
  1721  		switch mode {
  1722  		case "MC":
  1723  			g.FlushLocalCache()
  1724  		case "LC":
  1725  			memcache.Flush(c)
  1726  		}
  1727  
  1728  		// Get the entity again via goon, to make sure we cached the non-existance
  1729  		if err := g.Get(hid); err != datastore.ErrNoSuchEntity {
  1730  			t.Errorf("%s > Expected ErrNoSuchEntity, got %v", debugInfo, err)
  1731  		}
  1732  	}
  1733  }
  1734  
  1735  func TestNegativeCacheClear(t *testing.T) {
  1736  	c, done, err := aetest.NewContext()
  1737  	if err != nil {
  1738  		t.Fatalf("Could not start aetest - %v", err)
  1739  	}
  1740  	defer done()
  1741  	g := FromContext(c)
  1742  
  1743  	// Run twice to test local cache & memcache
  1744  	for _, mode := range []string{"MC", "LC"} {
  1745  		for tx := 0; tx < 2; tx++ {
  1746  			debugInfo := fmt.Sprintf("%s-%v", mode, tx == 1)
  1747  
  1748  			hid := &HasId{Name: "one"}
  1749  			var id int64
  1750  
  1751  			ided := make(chan bool)
  1752  			cached := make(chan bool)
  1753  			ended := make(chan bool)
  1754  
  1755  			go func() {
  1756  				var err error
  1757  				if tx == 0 {
  1758  					id = rand.Int63()
  1759  					hid.Id = id
  1760  					ided <- true
  1761  					<-cached
  1762  					_, err = g.Put(hid)
  1763  				} else {
  1764  					err = g.RunInTransaction(func(tg *Goon) error {
  1765  						_, err := tg.Put(hid)
  1766  						id = hid.Id
  1767  						ided <- true
  1768  						<-cached
  1769  						return err
  1770  					}, nil)
  1771  				}
  1772  				if err != nil {
  1773  					t.Errorf("%s > Unexpected error on RunInTransaction: %v", debugInfo, err)
  1774  				}
  1775  				ended <- true
  1776  			}()
  1777  
  1778  			// Populate the cache with a negative hit
  1779  			{
  1780  				<-ided
  1781  				negative := &HasId{Id: id}
  1782  				if err := g.Get(negative); err != datastore.ErrNoSuchEntity {
  1783  					t.Fatalf("%s > Expected ErrNoSuchEntity, got %v", debugInfo, err)
  1784  				}
  1785  				cached <- true
  1786  			}
  1787  
  1788  			// Make sure the negative cache no longer exists
  1789  			{
  1790  				<-ended
  1791  				want := &HasId{Id: id}
  1792  				if mode == "MC" {
  1793  					g.FlushLocalCache()
  1794  				}
  1795  				if err := g.Get(want); err != nil {
  1796  					t.Fatalf("%s > Unexpected error on get: %v", debugInfo, err)
  1797  				}
  1798  				if want.Name != hid.Name {
  1799  					t.Fatalf("%s > Expected '%v' but got '%v'", debugInfo, hid.Name, want.Name)
  1800  				}
  1801  			}
  1802  		}
  1803  	}
  1804  }
  1805  
  1806  func TestCaches(t *testing.T) {
  1807  	c, done, err := aetest.NewContext()
  1808  	if err != nil {
  1809  		t.Fatalf("Could not start aetest - %v", err)
  1810  	}
  1811  	defer done()
  1812  	g := FromContext(c)
  1813  
  1814  	// Put *struct{}
  1815  	phid := &HasId{Name: "cacheFail"}
  1816  	_, err = g.Put(phid)
  1817  	if err != nil {
  1818  		t.Fatalf("Unexpected error on put - %v", err)
  1819  	}
  1820  
  1821  	// Test the scenario where Put would populate local cache
  1822  
  1823  	// fetch *struct{} from cache
  1824  	ghid := &HasId{Id: phid.Id}
  1825  	err = g.Get(ghid)
  1826  	if err != nil {
  1827  		t.Fatalf("Unexpected error on get - %v", err)
  1828  	}
  1829  	if !reflect.DeepEqual(phid, ghid) {
  1830  		t.Fatalf("Invalid result!\n%s", getDiff(phid, ghid, "phid", "ghid"))
  1831  	}
  1832  
  1833  	// fetch []struct{} from cache
  1834  	ghids := []HasId{{Id: phid.Id}}
  1835  	err = g.GetMulti(&ghids)
  1836  	if err != nil {
  1837  		t.Fatalf("Unexpected error on get - %v", err)
  1838  	}
  1839  	if !reflect.DeepEqual(*phid, ghids[0]) {
  1840  		t.Fatalf("Invalid result!\n%s", getDiff(*phid, ghids[0], "*phid", "ghids[0]"))
  1841  	}
  1842  
  1843  	// Now flush localcache and fetch them again to test memcache
  1844  	g.FlushLocalCache()
  1845  
  1846  	// fetch *struct{} from memcache
  1847  	ghid = &HasId{Id: phid.Id}
  1848  	err = g.Get(ghid)
  1849  	if err != nil {
  1850  		t.Fatalf("Unexpected error on get - %v", err)
  1851  	}
  1852  	if !reflect.DeepEqual(phid, ghid) {
  1853  		t.Fatalf("Invalid result!\n%s", getDiff(phid, ghid, "phid", "ghid"))
  1854  	}
  1855  
  1856  	// Need to flush local cache again because it was populated by Get
  1857  	g.FlushLocalCache()
  1858  
  1859  	// fetch []struct{} from memcache
  1860  	ghids = []HasId{{Id: phid.Id}}
  1861  	err = g.GetMulti(&ghids)
  1862  	if err != nil {
  1863  		t.Fatalf("Unexpected error on get - %v", err)
  1864  	}
  1865  	if !reflect.DeepEqual(*phid, ghids[0]) {
  1866  		t.Fatalf("Invalid result!\n%s", getDiff(*phid, ghids[0], "*phid", "ghids[0]"))
  1867  	}
  1868  
  1869  	// Do a sneaky save straight to the datastore
  1870  	mhid := &HasId{Id: phid.Id, Name: "modified"}
  1871  	if _, err := datastore.Put(c, datastore.NewKey(c, "HasId", "", phid.Id, nil), mhid); err != nil {
  1872  		t.Fatalf("Unexpected error on datastore.Put: %v", err)
  1873  	}
  1874  
  1875  	// Clear the memcache entry specifically
  1876  	if err := g.ClearCache(phid, true, false); err != nil {
  1877  		t.Fatalf("Failed to clear cache: %v", err)
  1878  	}
  1879  
  1880  	// fetch *struct{} from cache
  1881  	ghid = &HasId{Id: phid.Id}
  1882  	err = g.Get(ghid)
  1883  	if err != nil {
  1884  		t.Fatalf("Unexpected error on get - %v", err)
  1885  	}
  1886  	if !reflect.DeepEqual(phid, ghid) {
  1887  		t.Fatalf("Invalid result!\n%s", getDiff(phid, ghid, "phid", "ghid"))
  1888  	}
  1889  
  1890  	// Clear the local cache entry specifically
  1891  	if err := g.ClearCache(phid, false, true); err != nil {
  1892  		t.Fatalf("Failed to clear cache: %v", err)
  1893  	}
  1894  
  1895  	// fetch *struct{} from datastore
  1896  	ghid = &HasId{Id: phid.Id}
  1897  	err = g.Get(ghid)
  1898  	if err != nil {
  1899  		t.Fatalf("Unexpected error on get - %v", err)
  1900  	}
  1901  	if !reflect.DeepEqual(mhid, ghid) {
  1902  		t.Fatalf("Invalid result!\n%s", getDiff(mhid, ghid, "mhid", "ghid"))
  1903  	}
  1904  
  1905  	// Do a sneaky save straight to the datastore
  1906  	nhid := &HasId{Id: phid.Id, Name: "nudged"}
  1907  	if _, err := datastore.Put(c, datastore.NewKey(c, "HasId", "", phid.Id, nil), nhid); err != nil {
  1908  		t.Fatalf("Unexpected error on datastore.Put: %v", err)
  1909  	}
  1910  
  1911  	// Clear the local cache entry specifically
  1912  	if err := g.ClearCache(phid, false, true); err != nil {
  1913  		t.Fatalf("Failed to clear cache: %v", err)
  1914  	}
  1915  
  1916  	// fetch *struct{} from memcache
  1917  	ghid = &HasId{Id: phid.Id}
  1918  	err = g.Get(ghid)
  1919  	if err != nil {
  1920  		t.Fatalf("Unexpected error on get - %v", err)
  1921  	}
  1922  	if !reflect.DeepEqual(mhid, ghid) {
  1923  		t.Fatalf("Invalid result!\n%s", getDiff(mhid, ghid, "mhid", "ghid"))
  1924  	}
  1925  }
  1926  
  1927  func TestGoon(t *testing.T) {
  1928  	c, done, err := aetest.NewContext()
  1929  	if err != nil {
  1930  		t.Fatalf("Could not start aetest - %v", err)
  1931  	}
  1932  	defer done()
  1933  	n := FromContext(c)
  1934  
  1935  	// key tests
  1936  	noid := NoId{}
  1937  	if k, err := n.KeyError(noid); err == nil && !k.Incomplete() {
  1938  		t.Fatalf("expected incomplete on noid")
  1939  	}
  1940  	if n.Key(noid) == nil {
  1941  		t.Fatalf("expected to find a key")
  1942  	}
  1943  
  1944  	var keyTests = []keyTest{
  1945  		{
  1946  			HasDefaultKind{},
  1947  			datastore.NewKey(c, "DefaultKind", "", 0, nil),
  1948  		},
  1949  		{
  1950  			HasId{Id: 1},
  1951  			datastore.NewKey(c, "HasId", "", 1, nil),
  1952  		},
  1953  		{
  1954  			HasKind{Id: 1, Kind: "OtherKind"},
  1955  			datastore.NewKey(c, "OtherKind", "", 1, nil),
  1956  		},
  1957  
  1958  		{
  1959  			HasDefaultKind{Id: 1, Kind: "OtherKind"},
  1960  			datastore.NewKey(c, "OtherKind", "", 1, nil),
  1961  		},
  1962  		{
  1963  			HasDefaultKind{Id: 1},
  1964  			datastore.NewKey(c, "DefaultKind", "", 1, nil),
  1965  		},
  1966  		{
  1967  			HasString{Id: "new"},
  1968  			datastore.NewKey(c, "HasString", "new", 0, nil),
  1969  		},
  1970  	}
  1971  
  1972  	for _, kt := range keyTests {
  1973  		if k, err := n.KeyError(kt.obj); err != nil {
  1974  			t.Fatalf("error: %v", err)
  1975  		} else if !k.Equal(kt.key) {
  1976  			t.Fatalf("keys not equal")
  1977  		}
  1978  	}
  1979  
  1980  	if _, err := n.KeyError(TwoId{IntId: 1, StringId: "1"}); err == nil {
  1981  		t.Fatalf("expected key error")
  1982  	}
  1983  
  1984  	// datastore tests
  1985  	keys, _ := datastore.NewQuery("HasId").KeysOnly().GetAll(c, nil)
  1986  	datastore.DeleteMulti(c, keys)
  1987  	memcache.Flush(c)
  1988  	if err := n.Get(&HasId{Id: 0}); err == nil {
  1989  		t.Fatalf("ds: expected error, we're fetching from the datastore on an incomplete key!")
  1990  	}
  1991  	if err := n.Get(&HasId{Id: 1}); err != datastore.ErrNoSuchEntity {
  1992  		t.Fatalf("ds: expected no such entity")
  1993  	}
  1994  	es := []*HasId{
  1995  		{Id: 1, Name: "one"},
  1996  		{Id: 2, Name: "two"},
  1997  	}
  1998  	var esk []*datastore.Key
  1999  	for _, e := range es {
  2000  		esk = append(esk, n.Key(e))
  2001  	}
  2002  	nes := []*HasId{
  2003  		{Id: 1},
  2004  		{Id: 2},
  2005  	}
  2006  	if err := n.GetMulti(es); err == nil {
  2007  		t.Fatalf("ds: expected error")
  2008  	} else if !NotFound(err, 0) {
  2009  		t.Fatalf("ds: not found error 0")
  2010  	} else if !NotFound(err, 1) {
  2011  		t.Fatalf("ds: not found error 1")
  2012  	} else if NotFound(err, 2) {
  2013  		t.Fatalf("ds: not found error 2")
  2014  	}
  2015  
  2016  	if keys, err := n.PutMulti(es); err != nil {
  2017  		t.Fatalf("put: unexpected error")
  2018  	} else if len(keys) != len(esk) {
  2019  		t.Fatalf("put: got unexpected number of keys")
  2020  	} else {
  2021  		for i, k := range keys {
  2022  			if !k.Equal(esk[i]) {
  2023  				t.Fatalf("put: got unexpected keys")
  2024  			}
  2025  		}
  2026  	}
  2027  	if err := n.GetMulti(nes); err != nil {
  2028  		t.Fatalf("put: unexpected error")
  2029  	} else if !reflect.DeepEqual(es, nes) {
  2030  		t.Fatalf("put: bad results\n%s", getDiff(es, nes, "es", "nes"))
  2031  	} else {
  2032  		nesk0 := n.Key(nes[0])
  2033  		if !nesk0.Equal(datastore.NewKey(c, "HasId", "", 1, nil)) {
  2034  			t.Fatalf("put: bad key")
  2035  		}
  2036  		nesk1 := n.Key(nes[1])
  2037  		if !nesk1.Equal(datastore.NewKey(c, "HasId", "", 2, nil)) {
  2038  			t.Fatalf("put: bad key")
  2039  		}
  2040  	}
  2041  	if _, err := n.Put(HasId{Id: 3}); err == nil {
  2042  		t.Fatalf("put: expected error")
  2043  	}
  2044  	// force partial fetch from memcache and then datastore
  2045  	memcache.Flush(c)
  2046  	if err := n.Get(nes[0]); err != nil {
  2047  		t.Fatalf("get: unexpected error")
  2048  	}
  2049  	if err := n.GetMulti(nes); err != nil {
  2050  		t.Fatalf("get: unexpected error")
  2051  	}
  2052  
  2053  	// put a HasId resource, then test pulling it from memory, memcache, and datastore
  2054  	hi := &HasId{Name: "hasid"} // no id given, should be automatically created by the datastore
  2055  	if _, err := n.Put(hi); err != nil {
  2056  		t.Fatalf("put: unexpected error - %v", err)
  2057  	}
  2058  	if n.Key(hi) == nil {
  2059  		t.Fatalf("key should not be nil")
  2060  	} else if n.Key(hi).Incomplete() {
  2061  		t.Fatalf("key should not be incomplete")
  2062  	}
  2063  
  2064  	hi2 := &HasId{Id: hi.Id}
  2065  	if err := n.Get(hi2); err != nil {
  2066  		t.Fatalf("get: unexpected error - %v", err)
  2067  	}
  2068  	if hi2.Name != hi.Name {
  2069  		t.Fatalf("Could not fetch HasId object from memory - %#v != %#v", hi, hi2)
  2070  	}
  2071  
  2072  	hi3 := &HasId{Id: hi.Id}
  2073  	n.cache.Delete(cacheKey(n.Key(hi)))
  2074  	if err := n.Get(hi3); err != nil {
  2075  		t.Fatalf("get: unexpected error - %v", err)
  2076  	}
  2077  	if hi3.Name != hi.Name {
  2078  		t.Fatalf("Could not fetch HasId object from memory - %#v != %#v", hi, hi3)
  2079  	}
  2080  
  2081  	hi4 := &HasId{Id: hi.Id}
  2082  	n.cache.Delete(cacheKey(n.Key(hi4)))
  2083  	if memcache.Flush(n.Context) != nil {
  2084  		t.Fatalf("Unable to flush memcache")
  2085  	}
  2086  	if err := n.Get(hi4); err != nil {
  2087  		t.Fatalf("get: unexpected error - %v", err)
  2088  	}
  2089  	if hi4.Name != hi.Name {
  2090  		t.Fatalf("Could not fetch HasId object from datastore- %#v != %#v", hi, hi4)
  2091  	}
  2092  
  2093  	// Now do the opposite also using hi
  2094  	// Test pulling from local cache and memcache when datastore result is different
  2095  	// Note that this shouldn't happen with real goon usage,
  2096  	//   but this tests that goon isn't still pulling from the datastore (or memcache) unnecessarily
  2097  	// hi in datastore Name = hasid
  2098  	hiPull := &HasId{Id: hi.Id}
  2099  	{
  2100  		ckey := cacheKey(n.Key(hi))
  2101  		hiTamper := &HasId{Id: hi.Id, Name: "changedincache"}
  2102  		data, err := serializeStruct(hiTamper)
  2103  		if err != nil {
  2104  			t.Fatalf("Unexpected error serializing: %v", err)
  2105  		}
  2106  		n.cache.Set(&cacheItem{key: ckey, value: data})
  2107  	}
  2108  	if err := n.Get(hiPull); err != nil {
  2109  		t.Fatalf("get: unexpected error - %v", err)
  2110  	}
  2111  	if hiPull.Name != "changedincache" {
  2112  		t.Fatalf("hiPull.Name should be 'changedincache' but got %s", hiPull.Name)
  2113  	}
  2114  	{
  2115  		ckey := cacheKey(n.Key(hi))
  2116  		hiPush := &HasId{Id: hi.Id, Name: "changedinmemcache"}
  2117  		data, err := serializeStruct(hiPush)
  2118  		if err != nil {
  2119  			t.Fatalf("Unexpected error serializing: %v", err)
  2120  		}
  2121  		n.putMemcache([]*cacheItem{{key: ckey, value: data}})
  2122  		n.cache.Delete(ckey)
  2123  	}
  2124  
  2125  	hiPull = &HasId{Id: hi.Id}
  2126  	if err := n.Get(hiPull); err != nil {
  2127  		t.Fatalf("get: unexpected error - %v", err)
  2128  	}
  2129  	if hiPull.Name != "changedinmemcache" {
  2130  		t.Fatalf("hiPull.Name should be 'changedinmemcache' but got %s", hiPull.Name)
  2131  	}
  2132  
  2133  	// Since the datastore can't assign a key to a String ID, test to make sure goon stops it from happening
  2134  	hasString := new(HasString)
  2135  	_, err = n.Put(hasString)
  2136  	if err == nil {
  2137  		t.Fatalf("Cannot put an incomplete string Id object as the datastore will populate an int64 id instead- %v", hasString)
  2138  	}
  2139  	hasString.Id = "hello"
  2140  	_, err = n.Put(hasString)
  2141  	if err != nil {
  2142  		t.Fatalf("Error putting hasString object - %v", hasString)
  2143  	}
  2144  
  2145  	// Test various ways to delete an entity
  2146  	for i := int64(0); i < 4; i++ {
  2147  		hid := &HasId{Id: 551 + i}
  2148  		key, err := n.Put(hid)
  2149  		if err != nil {
  2150  			t.Fatalf("Put failed: %v", err)
  2151  		}
  2152  		switch i {
  2153  		case 0:
  2154  			err = n.Delete(hid)
  2155  		case 1:
  2156  			err = n.DeleteMulti([]*HasId{hid})
  2157  		case 2:
  2158  			err = n.Delete(key)
  2159  		case 3:
  2160  			err = n.DeleteMulti([]*datastore.Key{key})
  2161  		}
  2162  		if err != nil {
  2163  			t.Fatalf("Delete %d failed: %v", i, err)
  2164  		}
  2165  		if err := n.Get(hid); err != datastore.ErrNoSuchEntity {
  2166  			t.Fatalf("Expected ErrNoSuchEntity but got %v", err)
  2167  		}
  2168  	}
  2169  
  2170  	// Test queries!
  2171  
  2172  	// Test that zero result queries work properly
  2173  	qiZRes := []QueryItem{}
  2174  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem"), &qiZRes); err != nil {
  2175  		t.Fatalf("GetAll Zero: unexpected error: %v", err)
  2176  	} else if len(dskeys) != 0 {
  2177  		t.Fatalf("GetAll Zero: expected 0 keys, got %v", len(dskeys))
  2178  	}
  2179  
  2180  	createEntities := func() {
  2181  		// Create some entities that we will query for
  2182  		if getKeys, err := n.PutMulti([]*QueryItem{{Id: 1, Data: "one"}, {Id: 2, Data: "two"}}); err != nil {
  2183  			t.Fatalf("PutMulti: unexpected error: %v", err)
  2184  		} else {
  2185  			// do a datastore Get by *Key so that data is written to the datstore and indexes generated before subsequent query
  2186  			if err := datastore.GetMulti(c, getKeys, make([]QueryItem, 2)); err != nil {
  2187  				t.Error(err)
  2188  			}
  2189  		}
  2190  		// Make sure we clear the cache, as we will fill it later with a query
  2191  		n.FlushLocalCache()
  2192  	}
  2193  	deleteEntities := func(ids ...int64) {
  2194  		if len(ids) == 0 {
  2195  			ids = []int64{1, 2}
  2196  		}
  2197  		var keys []*datastore.Key
  2198  		for _, id := range ids {
  2199  			keys = append(keys, datastore.NewKey(c, "QueryItem", "", id, nil))
  2200  		}
  2201  		if err := datastore.DeleteMulti(c, keys); err != nil {
  2202  			t.Error(err)
  2203  		}
  2204  	}
  2205  
  2206  	createEntities()
  2207  
  2208  	// Get the entity using a slice of structs that needs to be appended but has garbage data
  2209  	qiSGRes := []QueryItem{{Id: 1, Data: "invalid cache"}, {Garbage: "uninitialized memory"}}[:1]
  2210  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "two"), &qiSGRes); err != nil {
  2211  		t.Fatalf("GetAll SoS: unexpected error: %v", err)
  2212  	} else if len(dskeys) != 1 {
  2213  		t.Fatalf("GetAll SoS: expected 1 key, got %v", len(dskeys))
  2214  	} else if dskeys[0].IntID() != 2 {
  2215  		t.Fatalf("GetAll SoS: expected key IntID to be 2, got %v", dskeys[0].IntID())
  2216  	} else if len(qiSGRes) != 2 {
  2217  		t.Fatalf("GetAll SoS: expected 2 results, got %v", len(qiSGRes))
  2218  	} else if qiSGRes[1].Id != 2 {
  2219  		t.Fatalf("GetAll SoS: expected entity id to be 2, got %v", qiSGRes[1].Id)
  2220  	} else if qiSGRes[1].Data != "two" {
  2221  		t.Fatalf("GetAll SoS: expected entity data to be 'two', got '%v'", qiSGRes[1].Data)
  2222  	} else if qiSGRes[1].Garbage != "" {
  2223  		t.Fatalf("GetAll SoS: expected no garbage data, but got '%v'", qiSGRes[1].Garbage)
  2224  	}
  2225  
  2226  	n.FlushLocalCache()
  2227  
  2228  	// Get the entity using a slice of structs
  2229  	qiSRes := []QueryItem{}
  2230  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "one"), &qiSRes); err != nil {
  2231  		t.Fatalf("GetAll SoS: unexpected error: %v", err)
  2232  	} else if len(dskeys) != 1 {
  2233  		t.Fatalf("GetAll SoS: expected 1 key, got %v", len(dskeys))
  2234  	} else if dskeys[0].IntID() != 1 {
  2235  		t.Fatalf("GetAll SoS: expected key IntID to be 1, got %v", dskeys[0].IntID())
  2236  	} else if len(qiSRes) != 1 {
  2237  		t.Fatalf("GetAll SoS: expected 1 result, got %v", len(qiSRes))
  2238  	} else if qiSRes[0].Id != 1 {
  2239  		t.Fatalf("GetAll SoS: expected entity id to be 1, got %v", qiSRes[0].Id)
  2240  	} else if qiSRes[0].Data != "one" {
  2241  		t.Fatalf("GetAll SoS: expected entity data to be 'one', got '%v'", qiSRes[0].Data)
  2242  	}
  2243  
  2244  	deleteEntities()
  2245  
  2246  	// Get the entity using normal Get to test local cache
  2247  	qiS := &QueryItem{Id: 1}
  2248  	if err := n.Get(qiS); err != nil {
  2249  		t.Fatalf("Get SoS: unexpected error: %v", err)
  2250  	} else if qiS.Id != 1 {
  2251  		t.Fatalf("Get SoS: expected entity id to be 1, got %v", qiS.Id)
  2252  	} else if qiS.Data != "one" {
  2253  		t.Fatalf("Get SoS: expected entity data to be 'one', got '%v'", qiS.Data)
  2254  	}
  2255  
  2256  	createEntities()
  2257  
  2258  	// Get the entity using a slice of pointers to struct
  2259  	qiPRes := []*QueryItem{}
  2260  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "one"), &qiPRes); err != nil {
  2261  		t.Fatalf("GetAll SoPtS: unexpected error: %v", err)
  2262  	} else if len(dskeys) != 1 {
  2263  		t.Fatalf("GetAll SoPtS: expected 1 key, got %v", len(dskeys))
  2264  	} else if dskeys[0].IntID() != 1 {
  2265  		t.Fatalf("GetAll SoPtS: expected key IntID to be 1, got %v", dskeys[0].IntID())
  2266  	} else if len(qiPRes) != 1 {
  2267  		t.Fatalf("GetAll SoPtS: expected 1 result, got %v", len(qiPRes))
  2268  	} else if qiPRes[0].Id != 1 {
  2269  		t.Fatalf("GetAll SoPtS: expected entity id to be 1, got %v", qiPRes[0].Id)
  2270  	} else if qiPRes[0].Data != "one" {
  2271  		t.Fatalf("GetAll SoPtS: expected entity data to be 'one', got '%v'", qiPRes[0].Data)
  2272  	}
  2273  
  2274  	deleteEntities()
  2275  
  2276  	// Get the entity using normal Get to test local cache
  2277  	qiP := &QueryItem{Id: 1}
  2278  	if err := n.Get(qiP); err != nil {
  2279  		t.Fatalf("Get SoPtS: unexpected error: %v", err)
  2280  	} else if qiP.Id != 1 {
  2281  		t.Fatalf("Get SoPtS: expected entity id to be 1, got %v", qiP.Id)
  2282  	} else if qiP.Data != "one" {
  2283  		t.Fatalf("Get SoPtS: expected entity data to be 'one', got '%v'", qiP.Data)
  2284  	}
  2285  
  2286  	createEntities()
  2287  
  2288  	// Get the entity using an iterator
  2289  	qiIt := n.Run(datastore.NewQuery("QueryItem").Filter("data=", "one"))
  2290  
  2291  	qiItRes := &QueryItem{}
  2292  	if dskey, err := qiIt.Next(qiItRes); err != nil {
  2293  		t.Fatalf("Next: unexpected error: %v", err)
  2294  	} else if dskey.IntID() != 1 {
  2295  		t.Fatalf("Next: expected key IntID to be 1, got %v", dskey.IntID())
  2296  	} else if qiItRes.Id != 1 {
  2297  		t.Fatalf("Next: expected entity id to be 1, got %v", qiItRes.Id)
  2298  	} else if qiItRes.Data != "one" {
  2299  		t.Fatalf("Next: expected entity data to be 'one', got '%v'", qiItRes.Data)
  2300  	}
  2301  
  2302  	// Make sure the iterator ends correctly
  2303  	if _, err := qiIt.Next(&QueryItem{}); err != datastore.Done {
  2304  		t.Fatalf("Next: expected iterator to end with the error datastore.Done, got %v", err)
  2305  	}
  2306  
  2307  	deleteEntities()
  2308  
  2309  	// Get the entity using normal Get to test local cache
  2310  	qiI := &QueryItem{Id: 1}
  2311  	if err := n.Get(qiI); err != nil {
  2312  		t.Fatalf("Get Iterator: unexpected error: %v", err)
  2313  	} else if qiI.Id != 1 {
  2314  		t.Fatalf("Get Iterator: expected entity id to be 1, got %v", qiI.Id)
  2315  	} else if qiI.Data != "one" {
  2316  		t.Fatalf("Get Iterator: expected entity data to be 'one', got '%v'", qiI.Data)
  2317  	}
  2318  
  2319  	createEntities()
  2320  
  2321  	// Get the entity using a non-zero slice of structs, to also test the cache being filled incorrectly
  2322  	qiNZSRes := []QueryItem{{Id: 1, Data: "invalid cache"}}
  2323  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "two"), &qiNZSRes); err != nil {
  2324  		t.Fatalf("GetAll NZSoS: unexpected error: %v", err)
  2325  	} else if len(dskeys) != 1 {
  2326  		t.Fatalf("GetAll NZSoS: expected 1 key, got %v", len(dskeys))
  2327  	} else if dskeys[0].IntID() != 2 {
  2328  		t.Fatalf("GetAll NZSoS: expected key IntID to be 2, got %v", dskeys[0].IntID())
  2329  	} else if len(qiNZSRes) != 2 {
  2330  		t.Fatalf("GetAll NZSoS: expected slice len to be 2, got %v", len(qiNZSRes))
  2331  	} else if qiNZSRes[0].Id != 1 {
  2332  		t.Fatalf("GetAll NZSoS: expected entity id to be 1, got %v", qiNZSRes[0].Id)
  2333  	} else if qiNZSRes[0].Data != "invalid cache" {
  2334  		t.Fatalf("GetAll NZSoS: expected entity data to be 'invalid cache', got '%v'", qiNZSRes[0].Data)
  2335  	} else if qiNZSRes[1].Id != 2 {
  2336  		t.Fatalf("GetAll NZSoS: expected entity id to be 2, got %v", qiNZSRes[1].Id)
  2337  	} else if qiNZSRes[1].Data != "two" {
  2338  		t.Fatalf("GetAll NZSoS: expected entity data to be 'two', got '%v'", qiNZSRes[1].Data)
  2339  	}
  2340  
  2341  	deleteEntities(2)
  2342  
  2343  	// Get the entities using normal GetMulti to test local cache
  2344  	qiNZSs := []QueryItem{{Id: 1}, {Id: 2}}
  2345  	if err := n.GetMulti(qiNZSs); err != nil {
  2346  		t.Fatalf("GetMulti NZSoS: unexpected error: %v", err)
  2347  	} else if len(qiNZSs) != 2 {
  2348  		t.Fatalf("GetMulti NZSoS: expected slice len to be 2, got %v", len(qiNZSs))
  2349  	} else if qiNZSs[0].Id != 1 {
  2350  		t.Fatalf("GetMulti NZSoS: expected entity id to be 1, got %v", qiNZSs[0].Id)
  2351  	} else if qiNZSs[0].Data != "one" {
  2352  		t.Fatalf("GetMulti NZSoS: expected entity data to be 'one', got '%v'", qiNZSs[0].Data)
  2353  	} else if qiNZSs[1].Id != 2 {
  2354  		t.Fatalf("GetMulti NZSoS: expected entity id to be 2, got %v", qiNZSs[1].Id)
  2355  	} else if qiNZSs[1].Data != "two" {
  2356  		t.Fatalf("GetMulti NZSoS: expected entity data to be 'two', got '%v'", qiNZSs[1].Data)
  2357  	}
  2358  
  2359  	createEntities()
  2360  
  2361  	// Get the entity using a non-zero slice of pointers to struct, to also test the cache being filled incorrectly
  2362  	qiNZPRes := []*QueryItem{{Id: 1, Data: "invalid cache"}}
  2363  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "two"), &qiNZPRes); err != nil {
  2364  		t.Fatalf("GetAll NZSoPtS: unexpected error: %v", err)
  2365  	} else if len(dskeys) != 1 {
  2366  		t.Fatalf("GetAll NZSoPtS: expected 1 key, got %v", len(dskeys))
  2367  	} else if dskeys[0].IntID() != 2 {
  2368  		t.Fatalf("GetAll NZSoPtS: expected key IntID to be 2, got %v", dskeys[0].IntID())
  2369  	} else if len(qiNZPRes) != 2 {
  2370  		t.Fatalf("GetAll NZSoPtS: expected slice len to be 2, got %v", len(qiNZPRes))
  2371  	} else if qiNZPRes[0].Id != 1 {
  2372  		t.Fatalf("GetAll NZSoPtS: expected entity id to be 1, got %v", qiNZPRes[0].Id)
  2373  	} else if qiNZPRes[0].Data != "invalid cache" {
  2374  		t.Fatalf("GetAll NZSoPtS: expected entity data to be 'invalid cache', got '%v'", qiNZPRes[0].Data)
  2375  	} else if qiNZPRes[1].Id != 2 {
  2376  		t.Fatalf("GetAll NZSoPtS: expected entity id to be 2, got %v", qiNZPRes[1].Id)
  2377  	} else if qiNZPRes[1].Data != "two" {
  2378  		t.Fatalf("GetAll NZSoPtS: expected entity data to be 'two', got '%v'", qiNZPRes[1].Data)
  2379  	}
  2380  
  2381  	deleteEntities(2)
  2382  
  2383  	// Get the entities using normal GetMulti to test local cache
  2384  	qiNZPs := []*QueryItem{{Id: 1}, {Id: 2}}
  2385  	if err := n.GetMulti(qiNZPs); err != nil {
  2386  		t.Fatalf("GetMulti NZSoPtS: unexpected error: %v", err)
  2387  	} else if len(qiNZPs) != 2 {
  2388  		t.Fatalf("GetMulti NZSoPtS: expected slice len to be 2, got %v", len(qiNZPs))
  2389  	} else if qiNZPs[0].Id != 1 {
  2390  		t.Fatalf("GetMulti NZSoPtS: expected entity id to be 1, got %v", qiNZPs[0].Id)
  2391  	} else if qiNZPs[0].Data != "one" {
  2392  		t.Fatalf("GetMulti NZSoPtS: expected entity data to be 'one', got '%v'", qiNZPs[0].Data)
  2393  	} else if qiNZPs[1].Id != 2 {
  2394  		t.Fatalf("GetMulti NZSoPtS: expected entity id to be 2, got %v", qiNZPs[1].Id)
  2395  	} else if qiNZPs[1].Data != "two" {
  2396  		t.Fatalf("GetMulti NZSoPtS: expected entity data to be 'two', got '%v'", qiNZPs[1].Data)
  2397  	}
  2398  
  2399  	createEntities()
  2400  
  2401  	// Get the entity using a keys-only iterator
  2402  	qiItKO := n.Run(datastore.NewQuery("QueryItem").Filter("data=", "one").KeysOnly())
  2403  
  2404  	qiItKORes := &QueryItem{}
  2405  	if dskey, err := qiItKO.Next(qiItKORes); err != nil {
  2406  		t.Fatalf("Next: unexpected error: %v", err)
  2407  	} else if dskey.IntID() != 1 {
  2408  		t.Fatalf("Next: expected key IntID to be 1, got %v", dskey.IntID())
  2409  	} else if qiItKORes.Id != 1 {
  2410  		t.Fatalf("Next: expected entity id to be 1, got %v", qiItKORes.Id)
  2411  	} else if qiItKORes.Data != "" {
  2412  		t.Fatalf("Next: expected entity data to be empty, got '%v'", qiItKORes.Data)
  2413  	}
  2414  
  2415  	// Make sure the iterator ends correctly
  2416  	if _, err := qiItKO.Next(&QueryItem{}); err != datastore.Done {
  2417  		t.Fatalf("Next: expected iterator to end with the error datastore.Done, got %v", err)
  2418  	}
  2419  
  2420  	// Get the entity using normal Get to test local cache
  2421  	qiIKO := &QueryItem{Id: 1}
  2422  	if err := n.Get(qiIKO); err != nil {
  2423  		t.Fatalf("Get Iterator: unexpected error: %v", err)
  2424  	} else if qiIKO.Id != 1 {
  2425  		t.Fatalf("Get Iterator: expected entity id to be 1, got %v", qiIKO.Id)
  2426  	} else if qiIKO.Data != "one" {
  2427  		t.Fatalf("Get Iterator: expected entity data to be 'one', got '%v'", qiIKO.Data)
  2428  	}
  2429  
  2430  	n.FlushLocalCache()
  2431  
  2432  	// Test the simplest keys-only query, also test the cache not being filled incorrectly by a keys-only query
  2433  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "one").KeysOnly(), nil); err != nil {
  2434  		t.Fatalf("GetAll KeysOnly: unexpected error: %v", err)
  2435  	} else if len(dskeys) != 1 {
  2436  		t.Fatalf("GetAll KeysOnly: expected 1 key, got %v", len(dskeys))
  2437  	} else if dskeys[0].IntID() != 1 {
  2438  		t.Fatalf("GetAll KeysOnly: expected key IntID to be 1, got %v", dskeys[0].IntID())
  2439  	}
  2440  
  2441  	// Get the entity using normal Get to test that the local cache wasn't filled with incomplete data
  2442  	qiKO := &QueryItem{Id: 1}
  2443  	if err := n.Get(qiKO); err != nil {
  2444  		t.Fatalf("Get KeysOnly: unexpected error: %v", err)
  2445  	} else if qiKO.Id != 1 {
  2446  		t.Fatalf("Get KeysOnly: expected entity id to be 1, got %v", qiKO.Id)
  2447  	} else if qiKO.Data != "one" {
  2448  		t.Fatalf("Get KeysOnly: expected entity data to be 'one', got '%v'", qiKO.Data)
  2449  	}
  2450  
  2451  	n.FlushLocalCache()
  2452  
  2453  	// Test the keys-only query with slice of structs, also test the cache not being filled incorrectly by a keys-only query
  2454  	qiKOSRes := []QueryItem{}
  2455  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "one").KeysOnly(), &qiKOSRes); err != nil {
  2456  		t.Fatalf("GetAll KeysOnly SoS: unexpected error: %v", err)
  2457  	} else if len(dskeys) != 1 {
  2458  		t.Fatalf("GetAll KeysOnly SoS: expected 1 key, got %v", len(dskeys))
  2459  	} else if dskeys[0].IntID() != 1 {
  2460  		t.Fatalf("GetAll KeysOnly SoS: expected key IntID to be 1, got %v", dskeys[0].IntID())
  2461  	} else if len(qiKOSRes) != 1 {
  2462  		t.Fatalf("GetAll KeysOnly SoS: expected 1 result, got %v", len(qiKOSRes))
  2463  	} else if k := reflect.TypeOf(qiKOSRes[0]).Kind(); k != reflect.Struct {
  2464  		t.Fatalf("GetAll KeysOnly SoS: expected struct, got %v", k)
  2465  	} else if qiKOSRes[0].Id != 1 {
  2466  		t.Fatalf("GetAll KeysOnly SoS: expected entity id to be 1, got %v", qiKOSRes[0].Id)
  2467  	} else if qiKOSRes[0].Data != "" {
  2468  		t.Fatalf("GetAll KeysOnly SoS: expected entity data to be empty, got '%v'", qiKOSRes[0].Data)
  2469  	}
  2470  
  2471  	// Get the entity using normal Get to test that the local cache wasn't filled with incomplete data
  2472  	if err := n.GetMulti(qiKOSRes); err != nil {
  2473  		t.Fatalf("Get KeysOnly SoS: unexpected error: %v", err)
  2474  	} else if qiKOSRes[0].Id != 1 {
  2475  		t.Fatalf("Get KeysOnly SoS: expected entity id to be 1, got %v", qiKOSRes[0].Id)
  2476  	} else if qiKOSRes[0].Data != "one" {
  2477  		t.Fatalf("Get KeysOnly SoS: expected entity data to be 'one', got '%v'", qiKOSRes[0].Data)
  2478  	}
  2479  
  2480  	n.FlushLocalCache()
  2481  
  2482  	// Test the keys-only query with slice of pointers to struct, also test the cache not being filled incorrectly by a keys-only query
  2483  	qiKOPRes := []*QueryItem{}
  2484  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "one").KeysOnly(), &qiKOPRes); err != nil {
  2485  		t.Fatalf("GetAll KeysOnly SoPtS: unexpected error: %v", err)
  2486  	} else if len(dskeys) != 1 {
  2487  		t.Fatalf("GetAll KeysOnly SoPtS: expected 1 key, got %v", len(dskeys))
  2488  	} else if dskeys[0].IntID() != 1 {
  2489  		t.Fatalf("GetAll KeysOnly SoPtS: expected key IntID to be 1, got %v", dskeys[0].IntID())
  2490  	} else if len(qiKOPRes) != 1 {
  2491  		t.Fatalf("GetAll KeysOnly SoPtS: expected 1 result, got %v", len(qiKOPRes))
  2492  	} else if k := reflect.TypeOf(qiKOPRes[0]).Kind(); k != reflect.Ptr {
  2493  		t.Fatalf("GetAll KeysOnly SoPtS: expected pointer, got %v", k)
  2494  	} else if qiKOPRes[0].Id != 1 {
  2495  		t.Fatalf("GetAll KeysOnly SoPtS: expected entity id to be 1, got %v", qiKOPRes[0].Id)
  2496  	} else if qiKOPRes[0].Data != "" {
  2497  		t.Fatalf("GetAll KeysOnly SoPtS: expected entity data to be empty, got '%v'", qiKOPRes[0].Data)
  2498  	}
  2499  
  2500  	// Get the entity using normal Get to test that the local cache wasn't filled with incomplete data
  2501  	if err := n.GetMulti(qiKOPRes); err != nil {
  2502  		t.Fatalf("Get KeysOnly SoPtS: unexpected error: %v", err)
  2503  	} else if qiKOPRes[0].Id != 1 {
  2504  		t.Fatalf("Get KeysOnly SoPtS: expected entity id to be 1, got %v", qiKOPRes[0].Id)
  2505  	} else if qiKOPRes[0].Data != "one" {
  2506  		t.Fatalf("Get KeysOnly SoPtS: expected entity data to be 'one', got '%v'", qiKOPRes[0].Data)
  2507  	}
  2508  
  2509  	n.FlushLocalCache()
  2510  
  2511  	// Test the keys-only query with non-zero slice of structs
  2512  	qiKONZSRes := []QueryItem{{Id: 1, Data: "invalid cache"}}
  2513  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "two").KeysOnly(), &qiKONZSRes); err != nil {
  2514  		t.Fatalf("GetAll KeysOnly NZSoS: unexpected error: %v", err)
  2515  	} else if len(dskeys) != 1 {
  2516  		t.Fatalf("GetAll KeysOnly NZSoS: expected 1 key, got %v", len(dskeys))
  2517  	} else if dskeys[0].IntID() != 2 {
  2518  		t.Fatalf("GetAll KeysOnly NZSoS: expected key IntID to be 2, got %v", dskeys[0].IntID())
  2519  	} else if len(qiKONZSRes) != 2 {
  2520  		t.Fatalf("GetAll KeysOnly NZSoS: expected slice len to be 2, got %v", len(qiKONZSRes))
  2521  	} else if qiKONZSRes[0].Id != 1 {
  2522  		t.Fatalf("GetAll KeysOnly NZSoS: expected entity id to be 1, got %v", qiKONZSRes[0].Id)
  2523  	} else if qiKONZSRes[0].Data != "invalid cache" {
  2524  		t.Fatalf("GetAll KeysOnly NZSoS: expected entity data to be 'invalid cache', got '%v'", qiKONZSRes[0].Data)
  2525  	} else if k := reflect.TypeOf(qiKONZSRes[1]).Kind(); k != reflect.Struct {
  2526  		t.Fatalf("GetAll KeysOnly NZSoS: expected struct, got %v", k)
  2527  	} else if qiKONZSRes[1].Id != 2 {
  2528  		t.Fatalf("GetAll KeysOnly NZSoS: expected entity id to be 2, got %v", qiKONZSRes[1].Id)
  2529  	} else if qiKONZSRes[1].Data != "" {
  2530  		t.Fatalf("GetAll KeysOnly NZSoS: expected entity data to be empty, got '%v'", qiKONZSRes[1].Data)
  2531  	}
  2532  
  2533  	// Get the entities using normal GetMulti to test local cache
  2534  	if err := n.GetMulti(qiKONZSRes); err != nil {
  2535  		t.Fatalf("GetMulti NZSoS: unexpected error: %v", err)
  2536  	} else if len(qiKONZSRes) != 2 {
  2537  		t.Fatalf("GetMulti NZSoS: expected slice len to be 2, got %v", len(qiKONZSRes))
  2538  	} else if qiKONZSRes[0].Id != 1 {
  2539  		t.Fatalf("GetMulti NZSoS: expected entity id to be 1, got %v", qiKONZSRes[0].Id)
  2540  	} else if qiKONZSRes[0].Data != "one" {
  2541  		t.Fatalf("GetMulti NZSoS: expected entity data to be 'one', got '%v'", qiKONZSRes[0].Data)
  2542  	} else if qiKONZSRes[1].Id != 2 {
  2543  		t.Fatalf("GetMulti NZSoS: expected entity id to be 2, got %v", qiKONZSRes[1].Id)
  2544  	} else if qiKONZSRes[1].Data != "two" {
  2545  		t.Fatalf("GetMulti NZSoS: expected entity data to be 'two', got '%v'", qiKONZSRes[1].Data)
  2546  	}
  2547  
  2548  	n.FlushLocalCache()
  2549  
  2550  	// Test the keys-only query with non-zero slice of pointers to struct
  2551  	qiKONZPRes := []*QueryItem{{Id: 1, Data: "invalid cache"}}
  2552  	if dskeys, err := n.GetAll(datastore.NewQuery("QueryItem").Filter("data=", "two").KeysOnly(), &qiKONZPRes); err != nil {
  2553  		t.Fatalf("GetAll KeysOnly NZSoPtS: unexpected error: %v", err)
  2554  	} else if len(dskeys) != 1 {
  2555  		t.Fatalf("GetAll KeysOnly NZSoPtS: expected 1 key, got %v", len(dskeys))
  2556  	} else if dskeys[0].IntID() != 2 {
  2557  		t.Fatalf("GetAll KeysOnly NZSoPtS: expected key IntID to be 2, got %v", dskeys[0].IntID())
  2558  	} else if len(qiKONZPRes) != 2 {
  2559  		t.Fatalf("GetAll KeysOnly NZSoPtS: expected slice len to be 2, got %v", len(qiKONZPRes))
  2560  	} else if qiKONZPRes[0].Id != 1 {
  2561  		t.Fatalf("GetAll KeysOnly NZSoPtS: expected entity id to be 1, got %v", qiKONZPRes[0].Id)
  2562  	} else if qiKONZPRes[0].Data != "invalid cache" {
  2563  		t.Fatalf("GetAll KeysOnly NZSoPtS: expected entity data to be 'invalid cache', got '%v'", qiKONZPRes[0].Data)
  2564  	} else if k := reflect.TypeOf(qiKONZPRes[1]).Kind(); k != reflect.Ptr {
  2565  		t.Fatalf("GetAll KeysOnly NZSoPtS: expected pointer, got %v", k)
  2566  	} else if qiKONZPRes[1].Id != 2 {
  2567  		t.Fatalf("GetAll KeysOnly NZSoPtS: expected entity id to be 2, got %v", qiKONZPRes[1].Id)
  2568  	} else if qiKONZPRes[1].Data != "" {
  2569  		t.Fatalf("GetAll KeysOnly NZSoPtS: expected entity data to be empty, got '%v'", qiKONZPRes[1].Data)
  2570  	}
  2571  
  2572  	// Get the entities using normal GetMulti to test local cache
  2573  	if err := n.GetMulti(qiKONZPRes); err != nil {
  2574  		t.Fatalf("GetMulti NZSoPtS: unexpected error: %v", err)
  2575  	} else if len(qiKONZPRes) != 2 {
  2576  		t.Fatalf("GetMulti NZSoPtS: expected slice len to be 2, got %v", len(qiKONZPRes))
  2577  	} else if qiKONZPRes[0].Id != 1 {
  2578  		t.Fatalf("GetMulti NZSoPtS: expected entity id to be 1, got %v", qiKONZPRes[0].Id)
  2579  	} else if qiKONZPRes[0].Data != "one" {
  2580  		t.Fatalf("GetMulti NZSoPtS: expected entity data to be 'one', got '%v'", qiKONZPRes[0].Data)
  2581  	} else if qiKONZPRes[1].Id != 2 {
  2582  		t.Fatalf("GetMulti NZSoPtS: expected entity id to be 2, got %v", qiKONZPRes[1].Id)
  2583  	} else if qiKONZPRes[1].Data != "two" {
  2584  		t.Fatalf("GetMulti NZSoPtS: expected entity data to be 'two', got '%v'", qiKONZPRes[1].Data)
  2585  	}
  2586  }
  2587  
  2588  type keyTest struct {
  2589  	obj interface{}
  2590  	key *datastore.Key
  2591  }
  2592  
  2593  type NoId struct {
  2594  }
  2595  
  2596  type HasId struct {
  2597  	Id   int64 `datastore:"-" goon:"id"`
  2598  	Name string
  2599  }
  2600  
  2601  type HasKind struct {
  2602  	Id   int64  `datastore:"-" goon:"id"`
  2603  	Kind string `datastore:"-" goon:"kind"`
  2604  	Name string
  2605  }
  2606  
  2607  type HasDefaultKind struct {
  2608  	Id   int64  `datastore:"-" goon:"id"`
  2609  	Kind string `datastore:"-" goon:"kind,DefaultKind"`
  2610  	Name string
  2611  }
  2612  
  2613  type QueryItem struct {
  2614  	Id      int64  `datastore:"-" goon:"id"`
  2615  	Data    string `datastore:"data"`
  2616  	Extra   string `datastore:"extra"`
  2617  	Garbage string `datastore:"-"`
  2618  }
  2619  
  2620  type HasString struct {
  2621  	Id string `datastore:"-" goon:"id"`
  2622  }
  2623  
  2624  type TwoId struct {
  2625  	IntId    int64  `goon:"id"`
  2626  	StringId string `goon:"id"`
  2627  }
  2628  
  2629  type PutGet struct {
  2630  	ID    int64 `datastore:"-" goon:"id"`
  2631  	Value int32
  2632  }
  2633  
  2634  type HasData struct {
  2635  	Id   int64 `datastore:"-" goon:"id"`
  2636  	Data []byte
  2637  }
  2638  
  2639  // This test won't fail but if run with -race flag, it will show known race conditions
  2640  // Using multiple goroutines per http request is recommended here:
  2641  // http://talks.golang.org/2013/highperf.slide#22
  2642  func TestRace(t *testing.T) {
  2643  	c, done, err := aetest.NewContext()
  2644  	if err != nil {
  2645  		t.Fatalf("Could not start aetest - %v", err)
  2646  	}
  2647  	defer done()
  2648  	g := FromContext(c)
  2649  
  2650  	var hasIdSlice []*HasId
  2651  	for x := 1; x <= 4000; x++ {
  2652  		hasIdSlice = append(hasIdSlice, &HasId{Id: int64(x), Name: "Race"})
  2653  	}
  2654  	_, err = g.PutMulti(hasIdSlice)
  2655  	if err != nil {
  2656  		t.Fatalf("Could not put Race entities - %v", err)
  2657  	}
  2658  	hasIdSlice = hasIdSlice[:0]
  2659  	for x := 1; x <= 4000; x++ {
  2660  		hasIdSlice = append(hasIdSlice, &HasId{Id: int64(x)})
  2661  	}
  2662  	var wg sync.WaitGroup
  2663  	wg.Add(3)
  2664  	go func() {
  2665  		err := g.Get(hasIdSlice[0])
  2666  		if err != nil {
  2667  			t.Errorf("Error fetching id #0 - %v", err)
  2668  		}
  2669  		wg.Done()
  2670  	}()
  2671  	go func() {
  2672  		err := g.GetMulti(hasIdSlice[1:1500])
  2673  		if err != nil {
  2674  			t.Errorf("Error fetching ids 1 through 1499 - %v", err)
  2675  		}
  2676  		wg.Done()
  2677  	}()
  2678  	go func() {
  2679  		err := g.GetMulti(hasIdSlice[1500:])
  2680  		if err != nil {
  2681  			t.Errorf("Error fetching id #1500 through 4000 - %v", err)
  2682  		}
  2683  		wg.Done()
  2684  	}()
  2685  	wg.Wait()
  2686  	for x, hi := range hasIdSlice {
  2687  		if hi.Name != "Race" {
  2688  			t.Fatalf("Object #%d not fetched properly, fetched instead - %v", x, hi)
  2689  		}
  2690  	}
  2691  
  2692  	// in case of datastore failure
  2693  	errInternalCall := errors.New("internal call error")
  2694  	withErrorContext := func(ctx context.Context, multiLimit int) context.Context {
  2695  		return appengine.WithAPICallFunc(ctx, func(ctx context.Context, service, method string, in, out proto.Message) error {
  2696  			if service != "datastore_v3" || (method != "Put" && method != "Get" && method != "Delete") {
  2697  				return appengine.APICall(ctx, service, method, in, out)
  2698  			}
  2699  			errs := make(appengine.MultiError, multiLimit)
  2700  			for x := 0; x < multiLimit; x++ {
  2701  				errs[x] = errInternalCall
  2702  			}
  2703  			return errs
  2704  		})
  2705  	}
  2706  
  2707  	g.Context = withErrorContext(g.Context, datastorePutMultiMaxItems)
  2708  	_, err = g.PutMulti(hasIdSlice)
  2709  	if err != errInternalCall {
  2710  		t.Fatalf("Expected %v, got %v", errInternalCall, err)
  2711  	}
  2712  
  2713  	g.FlushLocalCache()
  2714  	g.Context = withErrorContext(g.Context, datastoreGetMultiMaxItems)
  2715  	err = g.GetMulti(hasIdSlice)
  2716  	if err != errInternalCall {
  2717  		t.Fatalf("Expected %v, got %v", errInternalCall, err)
  2718  	}
  2719  
  2720  	g.Context = withErrorContext(g.Context, datastoreDeleteMultiMaxItems)
  2721  	err = g.DeleteMulti(hasIdSlice)
  2722  	if err != errInternalCall {
  2723  		t.Fatalf("Expected %v, got %v", errInternalCall, err)
  2724  	}
  2725  }
  2726  
  2727  func TestPutGet(t *testing.T) {
  2728  	c, done, err := aetest.NewContext()
  2729  	if err != nil {
  2730  		t.Fatalf("Could not start aetest - %v", err)
  2731  	}
  2732  	defer done()
  2733  	g := FromContext(c)
  2734  
  2735  	key, err := g.Put(&PutGet{ID: 12, Value: 15})
  2736  	if err != nil {
  2737  		t.Fatal(err)
  2738  	}
  2739  	if key.IntID() != 12 {
  2740  		t.Fatal("ID should be 12 but is", key.IntID())
  2741  	}
  2742  
  2743  	// Datastore Get
  2744  	dsPutGet := &PutGet{}
  2745  	err = datastore.Get(c,
  2746  		datastore.NewKey(c, "PutGet", "", 12, nil), dsPutGet)
  2747  	if err != nil {
  2748  		t.Fatal(err)
  2749  	}
  2750  	if dsPutGet.Value != 15 {
  2751  		t.Fatal("dsPutGet.Value should be 15 but is",
  2752  			dsPutGet.Value)
  2753  	}
  2754  
  2755  	// Goon Get
  2756  	goonPutGet := &PutGet{ID: 12}
  2757  	err = g.Get(goonPutGet)
  2758  	if err != nil {
  2759  		t.Fatal(err)
  2760  	}
  2761  	if goonPutGet.ID != 12 {
  2762  		t.Fatal("goonPutGet.ID should be 12 but is", goonPutGet.ID)
  2763  	}
  2764  	if goonPutGet.Value != 15 {
  2765  		t.Fatal("goonPutGet.Value should be 15 but is",
  2766  			goonPutGet.Value)
  2767  	}
  2768  }
  2769  
  2770  func prefixKindName(src interface{}) string {
  2771  	return "prefix." + DefaultKindName(src)
  2772  }
  2773  
  2774  func TestCustomKindName(t *testing.T) {
  2775  	c, done, err := aetest.NewContext()
  2776  	if err != nil {
  2777  		t.Fatalf("Could not start aetest - %v", err)
  2778  	}
  2779  	defer done()
  2780  	g := FromContext(c)
  2781  
  2782  	hi := HasId{Name: "Foo"}
  2783  
  2784  	//gate
  2785  	if kind := g.Kind(hi); kind != "HasId" {
  2786  		t.Fatal("HasId King should not have a prefix, but instead is, ", kind)
  2787  	}
  2788  
  2789  	g.KindNameResolver = prefixKindName
  2790  
  2791  	if kind := g.Kind(hi); kind != "prefix.HasId" {
  2792  		t.Fatal("HasId King should have a prefix, but instead is, ", kind)
  2793  	}
  2794  
  2795  	_, err = g.Put(&hi)
  2796  
  2797  	if err != nil {
  2798  		t.Fatal("Should be able to put a record: ", err)
  2799  	}
  2800  
  2801  	// Due to eventual consistency, we need to wait a bit. The old aetest package
  2802  	// had an option to enable strong consistency that has been removed. This
  2803  	// is currently the best way I'm aware of to do this.
  2804  	time.Sleep(time.Second)
  2805  	reget1 := []HasId{}
  2806  	query := datastore.NewQuery("prefix.HasId")
  2807  	query.GetAll(c, &reget1)
  2808  	if len(reget1) != 1 {
  2809  		t.Fatal("Should have 1 record stored in datastore ", reget1)
  2810  	}
  2811  	if reget1[0].Name != "Foo" {
  2812  		t.Fatal("Name should be Foo ", reget1[0].Name)
  2813  	}
  2814  }
  2815  
  2816  func TestMultis(t *testing.T) {
  2817  	c, done, err := aetest.NewContext()
  2818  	if err != nil {
  2819  		t.Fatalf("Could not start aetest - %v", err)
  2820  	}
  2821  	defer done()
  2822  	n := FromContext(c)
  2823  
  2824  	testAmounts := []int{1, 999, 1000, 1001, 1999, 2000, 2001, 2510}
  2825  	for _, x := range testAmounts {
  2826  		memcache.Flush(c)
  2827  		objects := make([]*HasId, x)
  2828  		for y := 0; y < x; y++ {
  2829  			objects[y] = &HasId{Id: int64(y + 1)}
  2830  		}
  2831  		if keys, err := n.PutMulti(objects); err != nil {
  2832  			t.Fatalf("Error in PutMulti for %d objects - %v", x, err)
  2833  		} else if len(keys) != len(objects) {
  2834  			t.Fatalf("Expected %v keys, got %v", len(objects), len(keys))
  2835  		} else {
  2836  			for i, key := range keys {
  2837  				if key.IntID() != int64(i+1) {
  2838  					t.Fatalf("Expected object #%v key to be %v, got %v", i, (i + 1), key.IntID())
  2839  				}
  2840  			}
  2841  		}
  2842  		n.FlushLocalCache() // Put just put them in the local cache, get rid of it before doing the Get
  2843  		if err := n.GetMulti(objects); err != nil {
  2844  			t.Fatalf("Error in GetMulti - %v", err)
  2845  		}
  2846  	}
  2847  
  2848  	// check if the returned keys match the struct keys for autogenerated keys
  2849  	for _, x := range testAmounts {
  2850  		memcache.Flush(c)
  2851  		objects := make([]*HasId, x)
  2852  		for y := 0; y < x; y++ {
  2853  			objects[y] = &HasId{}
  2854  		}
  2855  		if keys, err := n.PutMulti(objects); err != nil {
  2856  			t.Fatalf("Error in PutMulti for %d objects - %v", x, err)
  2857  		} else if len(keys) != len(objects) {
  2858  			t.Fatalf("Expected %v keys, got %v", len(objects), len(keys))
  2859  		} else {
  2860  			for i, key := range keys {
  2861  				if key.IntID() != objects[i].Id {
  2862  					t.Errorf("Expected object #%v key to be %v, got %v", i, objects[i].Id, key.IntID())
  2863  				}
  2864  			}
  2865  		}
  2866  		n.FlushLocalCache()
  2867  	}
  2868  
  2869  	// do it again, but only write numbers divisible by 100
  2870  	for _, x := range testAmounts {
  2871  		memcache.Flush(c)
  2872  		getobjects := make([]*HasId, 0, x)
  2873  		putobjects := make([]*HasId, 0, x/100+1)
  2874  		keys := make([]*datastore.Key, x)
  2875  		for y := 0; y < x; y++ {
  2876  			keys[y] = datastore.NewKey(c, "HasId", "", int64(y+1), nil)
  2877  		}
  2878  		if err := n.DeleteMulti(keys); err != nil {
  2879  			t.Fatalf("Error deleting keys - %v", err)
  2880  		}
  2881  		for y := 0; y < x; y++ {
  2882  			getobjects = append(getobjects, &HasId{Id: int64(y + 1)})
  2883  			if y%100 == 0 {
  2884  				putobjects = append(putobjects, &HasId{Id: int64(y + 1)})
  2885  			}
  2886  		}
  2887  
  2888  		_, err := n.PutMulti(putobjects)
  2889  		if err != nil {
  2890  			t.Fatalf("Error in PutMulti for %d objects - %v", x, err)
  2891  		}
  2892  		n.FlushLocalCache() // Put just put them in the local cache, get rid of it before doing the Get
  2893  		err = n.GetMulti(getobjects)
  2894  		if err == nil && x != 1 { // a test size of 1 has no objects divisible by 100, so there's no cache miss to return
  2895  			t.Fatalf("Should be receiving a multiError on %d objects, but got no errors", x)
  2896  			continue
  2897  		}
  2898  
  2899  		merr, ok := err.(appengine.MultiError)
  2900  		if ok {
  2901  			if len(merr) != len(getobjects) {
  2902  				t.Fatalf("Should have received a MultiError object of length %d but got length %d instead", len(getobjects), len(merr))
  2903  			}
  2904  			for x := range merr {
  2905  				switch { // record good conditions, fail in other conditions
  2906  				case merr[x] == nil && x%100 == 0:
  2907  				case merr[x] != nil && x%100 != 0:
  2908  				default:
  2909  					t.Fatalf("Found bad condition on object[%d] and error %v", x+1, merr[x])
  2910  				}
  2911  			}
  2912  		} else if x != 1 {
  2913  			t.Fatalf("Did not return a multierror on fetch but when fetching %d objects, received - %v", x, merr)
  2914  		}
  2915  	}
  2916  }
  2917  
  2918  type root struct {
  2919  	Id   int64 `datastore:"-" goon:"id"`
  2920  	Data int
  2921  }
  2922  
  2923  type normalChild struct {
  2924  	Id     int64          `datastore:"-" goon:"id"`
  2925  	Parent *datastore.Key `datastore:"-" goon:"parent"`
  2926  	Data   int
  2927  }
  2928  
  2929  type coolKey *datastore.Key
  2930  
  2931  type derivedChild struct {
  2932  	Id     int64   `datastore:"-" goon:"id"`
  2933  	Parent coolKey `datastore:"-" goon:"parent"`
  2934  	Data   int
  2935  }
  2936  
  2937  func TestParents(t *testing.T) {
  2938  	c, done, err := aetest.NewContext()
  2939  	if err != nil {
  2940  		t.Fatalf("Could not start aetest - %v", err)
  2941  	}
  2942  	defer done()
  2943  	n := FromContext(c)
  2944  
  2945  	r := &root{1, 10}
  2946  	rootKey, err := n.Put(r)
  2947  	if err != nil {
  2948  		t.Fatalf("couldn't Put(%+v)", r)
  2949  	}
  2950  
  2951  	// Put exercises both get and set, since Id is uninitialized
  2952  	nc := &normalChild{0, rootKey, 20}
  2953  	nk, err := n.Put(nc)
  2954  	if err != nil {
  2955  		t.Fatalf("couldn't Put(%+v)", nc)
  2956  	}
  2957  	if nc.Parent == rootKey {
  2958  		t.Fatalf("derived parent key pointer value didn't change")
  2959  	}
  2960  	if !(*datastore.Key)(nc.Parent).Equal(rootKey) {
  2961  		t.Fatalf("parent of key not equal '%s' v '%s'! ", (*datastore.Key)(nc.Parent), rootKey)
  2962  	}
  2963  	if !nk.Parent().Equal(rootKey) {
  2964  		t.Fatalf("parent of key not equal '%s' v '%s'! ", nk, rootKey)
  2965  	}
  2966  
  2967  	dc := &derivedChild{0, (coolKey)(rootKey), 12}
  2968  	dk, err := n.Put(dc)
  2969  	if err != nil {
  2970  		t.Fatalf("couldn't Put(%+v)", dc)
  2971  	}
  2972  	if dc.Parent == rootKey {
  2973  		t.Fatalf("derived parent key pointer value didn't change")
  2974  	}
  2975  	if !(*datastore.Key)(dc.Parent).Equal(rootKey) {
  2976  		t.Fatalf("parent of key not equal '%s' v '%s'! ", (*datastore.Key)(dc.Parent), rootKey)
  2977  	}
  2978  	if !dk.Parent().Equal(rootKey) {
  2979  		t.Fatalf("parent of key not equal '%s' v '%s'! ", dk, rootKey)
  2980  	}
  2981  }
  2982  
  2983  func TestProjection(t *testing.T) {
  2984  	c, done, err := aetest.NewContext()
  2985  	if err != nil {
  2986  		t.Fatalf("Could not start aetest - %v", err)
  2987  	}
  2988  	defer done()
  2989  	g := FromContext(c)
  2990  
  2991  	// Store some items
  2992  	if _, err := g.PutMulti([]*QueryItem{{Id: 1, Data: "foo", Extra: "zoo"}, {Id: 2, Data: "bar", Extra: "woo"}}); err != nil {
  2993  		t.Fatalf("failed to put query items: %v", err)
  2994  	}
  2995  
  2996  	// Helps test if the cache has been poisoned by the incomplete results of the projection query
  2997  	testCache := func(spot string) {
  2998  		// Get these items by key
  2999  		qis := []*QueryItem{{Id: 1}, {Id: 2}}
  3000  		if err := g.GetMulti(qis); err != nil {
  3001  			t.Fatalf("[%v] failed to fetch query items by key: %v", spot, err)
  3002  		}
  3003  		// Make sure the data is correct
  3004  		if qis[0].Data != "foo" {
  3005  			t.Errorf("[%v] first query item's data is incorrect: %v", spot, qis[0].Data)
  3006  		}
  3007  		if qis[1].Data != "bar" {
  3008  			t.Errorf("[%v] second query item's data is incorrect: %v", spot, qis[1].Data)
  3009  		}
  3010  		if qis[0].Extra != "zoo" {
  3011  			t.Errorf("[%v] unexpected extra value: %v", spot, qis[0].Extra)
  3012  		}
  3013  		if qis[1].Extra != "woo" {
  3014  			t.Errorf("[%v] unexpected extra value: %v", spot, qis[1].Extra)
  3015  		}
  3016  	}
  3017  
  3018  	// Clear the caches
  3019  	g.FlushLocalCache()
  3020  	memcache.Flush(c)
  3021  
  3022  	// Need to wait due to eventual consistency
  3023  	time.Sleep(time.Second)
  3024  
  3025  	// Do a projection query for these
  3026  	qis := []*QueryItem{}
  3027  	if _, err := g.GetAll(datastore.NewQuery("QueryItem").Project("data").Order("-data"), &qis); err != nil {
  3028  		t.Fatalf("failed to fetch query items: %v", err)
  3029  	}
  3030  
  3031  	if len(qis) != 2 {
  3032  		t.Fatalf("failed to fetch both query items (%v)", len(qis))
  3033  	}
  3034  	if qis[0].Data != "foo" {
  3035  		t.Errorf("first query item's data is incorrect: %v", qis[0].Data)
  3036  	}
  3037  	if qis[1].Data != "bar" {
  3038  		t.Errorf("second query item's data is incorrect: %v", qis[1].Data)
  3039  	}
  3040  	if qis[0].Extra != "" {
  3041  		t.Errorf("unexpected extra value: %v", qis[0].Extra)
  3042  	}
  3043  	if qis[1].Extra != "" {
  3044  		t.Errorf("unexpected extra value: %v", qis[1].Extra)
  3045  	}
  3046  
  3047  	testCache("GetAll")
  3048  
  3049  	// Clear the caches
  3050  	g.FlushLocalCache()
  3051  	memcache.Flush(c)
  3052  
  3053  	// Do another projection query
  3054  	it := g.Run(datastore.NewQuery("QueryItem").Project("data").Order("-data"))
  3055  
  3056  	qi1 := &QueryItem{}
  3057  	if _, err := it.Next(qi1); err != nil {
  3058  		t.Fatalf("failed to fetch first query item: %v", err)
  3059  	}
  3060  	qi2 := &QueryItem{}
  3061  	if _, err := it.Next(qi2); err != nil {
  3062  		t.Fatalf("failed to fetch second query item: %v", err)
  3063  	}
  3064  	if _, err := it.Next(nil); err != datastore.Done {
  3065  		t.Errorf("query didn't properly finish: %v", err)
  3066  	}
  3067  
  3068  	if qi1.Data != "foo" {
  3069  		t.Errorf("first query item's data is incorrect: %v", qi1.Data)
  3070  	}
  3071  	if qi2.Data != "bar" {
  3072  		t.Errorf("second query item's data is incorrect: %v", qi2.Data)
  3073  	}
  3074  	if qi1.Extra != "" {
  3075  		t.Errorf("unexpected extra value: %v", qi1.Extra)
  3076  	}
  3077  	if qi2.Extra != "" {
  3078  		t.Errorf("unexpected extra value: %v", qi2.Extra)
  3079  	}
  3080  
  3081  	testCache("Next")
  3082  }
  3083  
  3084  type ContainerStruct struct {
  3085  	Id string `datastore:"-" goon:"id"`
  3086  	embeddedStructA
  3087  	embeddedStructB `datastore:"w"`
  3088  }
  3089  
  3090  type embeddedStructA struct {
  3091  	X int
  3092  	y int
  3093  }
  3094  
  3095  type embeddedStructB struct {
  3096  	Z1 int
  3097  	Z2 int `datastore:"z2fancy"`
  3098  }
  3099  
  3100  func TestEmbeddedStruct(t *testing.T) {
  3101  	c, done, err := aetest.NewContext()
  3102  	if err != nil {
  3103  		t.Fatalf("Could not start aetest - %v", err)
  3104  	}
  3105  	defer done()
  3106  	g := FromContext(c)
  3107  
  3108  	// Store some data with an embedded unexported struct
  3109  	pcs := &ContainerStruct{Id: "foo"}
  3110  	pcs.X, pcs.y, pcs.Z1, pcs.Z2 = 1, 2, 3, 4
  3111  	_, err = g.Put(pcs)
  3112  	if err != nil {
  3113  		t.Fatalf("Unexpected error on put - %v", err)
  3114  	}
  3115  
  3116  	// First run fetches from the datastore (as Put() only caches to the local cache)
  3117  	// Second run fetches from memcache (as our first run here called Get() which caches into memcache)
  3118  	for i := 1; i <= 2; i++ {
  3119  		// Clear the local cache
  3120  		g.FlushLocalCache()
  3121  
  3122  		// Fetch it and confirm the values
  3123  		gcs := &ContainerStruct{Id: pcs.Id}
  3124  		err = g.Get(gcs)
  3125  		if err != nil {
  3126  			t.Fatalf("#%v - Unexpected error on get - %v", i, err)
  3127  		}
  3128  		// The exported field must have the correct value
  3129  		if gcs.X != pcs.X {
  3130  			t.Fatalf("#%v - Expected - %v, got %v", i, pcs.X, gcs.X)
  3131  		}
  3132  		if gcs.Z1 != pcs.Z1 {
  3133  			t.Fatalf("#%v - Expected - %v, got %v", i, pcs.Z1, gcs.Z1)
  3134  		}
  3135  		if gcs.Z2 != pcs.Z2 {
  3136  			t.Fatalf("#%v - Expected - %v, got %v", i, pcs.Z2, gcs.Z2)
  3137  		}
  3138  		// The unexported field must be zero-valued
  3139  		if gcs.y != 0 {
  3140  			t.Fatalf("#%v - Expected - %v, got %v", i, 0, gcs.y)
  3141  		}
  3142  	}
  3143  }
  3144  
  3145  func TestMemcachePutTimeout(t *testing.T) {
  3146  	c, done, err := aetest.NewContext()
  3147  	if err != nil {
  3148  		t.Fatalf("Could not start aetest - %v", err)
  3149  	}
  3150  	defer done()
  3151  
  3152  	origMPTS := MemcachePutTimeoutSmall
  3153  	origMPTL := MemcachePutTimeoutLarge
  3154  	origMPTT := MemcachePutTimeoutThreshold
  3155  	origMGT := MemcacheGetTimeout
  3156  	origPMPE := propagateMemcachePutError
  3157  	defer func() {
  3158  		MemcachePutTimeoutSmall = origMPTS
  3159  		MemcachePutTimeoutLarge = origMPTL
  3160  		MemcachePutTimeoutThreshold = origMPTT
  3161  		MemcacheGetTimeout = origMGT
  3162  		propagateMemcachePutError = origPMPE
  3163  	}()
  3164  
  3165  	propagateMemcachePutError = false
  3166  
  3167  	g := FromContext(c)
  3168  	MemcachePutTimeoutSmall = time.Second
  3169  	// put a HasId resource, then test pulling it from memory, memcache, and datastore
  3170  	hi := &HasId{Name: "hasid"} // no id given, should be automatically created by the datastore
  3171  	if _, err := g.Put(hi); err != nil {
  3172  		t.Fatalf("put: unexpected error - %v", err)
  3173  	}
  3174  
  3175  	// Generate the cache entry
  3176  	data, err := serializeStruct(hi)
  3177  	if err != nil {
  3178  		t.Fatalf("Unexpected error on serialize: %v", err)
  3179  	}
  3180  	ci := &cacheItem{
  3181  		key:   cacheKey(g.Key(hi)),
  3182  		value: data,
  3183  	}
  3184  	cis := []*cacheItem{ci}
  3185  
  3186  	MemcachePutTimeoutSmall = 0
  3187  	MemcachePutTimeoutLarge = 0
  3188  	MemcachePutTimeoutThreshold = 1
  3189  	if err := g.putMemcache(cis); !appengine.IsTimeoutError(err) {
  3190  		t.Fatalf("Request should timeout - err = %v", err)
  3191  	}
  3192  
  3193  	MemcachePutTimeoutLarge = time.Second
  3194  	if err := g.putMemcache(cis); err != nil {
  3195  		t.Fatalf("putMemcache: unexpected error - %v", err)
  3196  	}
  3197  
  3198  	g.FlushLocalCache()
  3199  	memcache.Flush(c)
  3200  	// time out Get
  3201  	MemcacheGetTimeout = 0
  3202  	// time out Put too
  3203  	MemcachePutTimeoutSmall = 0
  3204  	MemcachePutTimeoutThreshold = 1
  3205  	MemcachePutTimeoutLarge = 0
  3206  	hiResult := &HasId{Id: hi.Id}
  3207  	if err := g.Get(hiResult); err != nil {
  3208  		t.Fatalf("Request should not timeout cause we'll fetch from the datastore but got error  %v", err)
  3209  		// Put timing out should also error, but it won't be returned here, just logged
  3210  	}
  3211  	if !reflect.DeepEqual(hi, hiResult) {
  3212  		t.Fatalf("Fetched object isn't accurate\n%s", getDiff(hi, hiResult, "hi", "hiResult"))
  3213  	}
  3214  
  3215  	hiResult = &HasId{Id: hi.Id}
  3216  	g.FlushLocalCache()
  3217  	MemcacheGetTimeout = time.Second
  3218  	if err := g.Get(hiResult); err != nil {
  3219  		t.Fatalf("Request should not timeout cause we'll fetch from memcache successfully but got error %v", err)
  3220  	}
  3221  	if !reflect.DeepEqual(hi, hiResult) {
  3222  		t.Fatalf("Fetched object isn't accurate\n%s", getDiff(hi, hiResult, "hi", "hiResult"))
  3223  	}
  3224  }
  3225  
  3226  func TestChangeMemcacheKey(t *testing.T) {
  3227  	c, done, err := aetest.NewContext()
  3228  	if err != nil {
  3229  		t.Fatalf("Could not start aetest - %v", err)
  3230  	}
  3231  	defer done()
  3232  
  3233  	originalMemcacheKey := MemcacheKey
  3234  	defer func() {
  3235  		MemcacheKey = originalMemcacheKey
  3236  	}()
  3237  	verID := "some-version"
  3238  	MemcacheKey = func(k *datastore.Key) string {
  3239  		return "custom:" + verID + ":" + k.Encode()
  3240  	}
  3241  
  3242  	g := FromContext(c)
  3243  
  3244  	key, err := g.Put(&PutGet{ID: 12, Value: 15})
  3245  	if err != nil {
  3246  		t.Fatal(err)
  3247  	}
  3248  	g.FlushLocalCache()
  3249  	err = g.Get(&PutGet{ID: 12})
  3250  	if err != nil {
  3251  		t.Fatal(err)
  3252  	}
  3253  
  3254  	ckey := cacheKey(key)
  3255  
  3256  	_, err = memcache.Get(c, ckey)
  3257  	if err != nil {
  3258  		t.Fatal(err)
  3259  	}
  3260  
  3261  	if !strings.HasSuffix(ckey, "custom:"+verID+":"+key.Encode()) {
  3262  		t.Fatal("cache key should have 'custom:`versionID`:`encodedKey` suffix", err)
  3263  	}
  3264  }
  3265  
  3266  func TestMemcacheLimits(t *testing.T) {
  3267  	c, done, err := aetest.NewContext()
  3268  	if err != nil {
  3269  		t.Fatalf("Could not start aetest - %v", err)
  3270  	}
  3271  	defer done()
  3272  
  3273  	// Confirm that the cacheKey function respects the output limit
  3274  	pk := datastore.NewKey(c, "Parent", strings.Repeat("p", memcacheMaxKeySize/2), 0, nil)
  3275  	ck := datastore.NewKey(c, "Child", strings.Repeat("c", memcacheMaxKeySize/2), 0, pk)
  3276  	if mk := cacheKey(ck); len(mk) > memcacheMaxKeySize {
  3277  		t.Fatalf("cacheKey returned key with a length of %d exceeding the maximum of %d", len(mk), memcacheMaxKeySize)
  3278  	}
  3279  
  3280  	// Confirm that single maximum size fits
  3281  	maxItem := &memcache.Item{
  3282  		Key:        strings.Repeat("k", memcacheMaxKeySize),
  3283  		Value:      make([]byte, memcacheMaxValueSize),
  3284  		Flags:      1<<32 - 1,
  3285  		Expiration: time.Duration(1<<63 - 1),
  3286  	}
  3287  	if err := memcache.Set(c, maxItem); err != nil {
  3288  		t.Fatalf("Expected maximum sized item to pass! Got error: %v", err)
  3289  	}
  3290  	if _, err := memcache.Get(c, maxItem.Key); err != nil {
  3291  		t.Fatalf("Unexpected error on Get: %v", err)
  3292  	}
  3293  
  3294  	// Helper function to test RPC batch size limit
  3295  	testRPCBatchSize := func(maxValueSize int, readBack bool) {
  3296  		var items []*memcache.Item
  3297  		itemSize := memcacheOverhead + memcacheMaxKeySize + maxValueSize
  3298  		extra := memcacheOverhead + memcacheMaxKeySize + 1 // at least 1 byte for value
  3299  		count := (memcacheMaxRPCSize - extra) / itemSize
  3300  		extra += (memcacheMaxRPCSize - extra) % itemSize
  3301  		for i := 0; i < count; i++ {
  3302  			items = append(items, &memcache.Item{
  3303  				Key:        strings.Repeat("k", memcacheMaxKeySize-30) + fmt.Sprintf("%030d", i),
  3304  				Value:      make([]byte, maxValueSize),
  3305  				Flags:      1<<32 - 1,
  3306  				Expiration: time.Duration(1<<63 - 1),
  3307  			})
  3308  		}
  3309  		extraValueSize := extra - memcacheOverhead - memcacheMaxKeySize
  3310  		items = append(items, &memcache.Item{
  3311  			Key:        strings.Repeat("x", memcacheMaxKeySize),
  3312  			Value:      make([]byte, extraValueSize),
  3313  			Flags:      1<<32 - 1,
  3314  			Expiration: time.Duration(1<<63 - 1),
  3315  		})
  3316  		if err := memcache.SetMulti(c, items); err != nil {
  3317  			t.Fatalf("Expected maximum sized batch to pass with maxValueSize %d! Got error: %v", maxValueSize, err)
  3318  		}
  3319  		if readBack {
  3320  			var keys []string
  3321  			for _, item := range items {
  3322  				keys = append(keys, item.Key)
  3323  			}
  3324  			if results, err := memcache.GetMulti(c, keys); err != nil {
  3325  				t.Fatalf("Unexpected error on GetMulti with maxValueSize %d: %v", maxValueSize, err)
  3326  			} else if len(keys) != len(results) {
  3327  				t.Fatalf("Expected %d results but got %d with maxValueSize %d", len(keys), len(results), maxValueSize)
  3328  			}
  3329  		}
  3330  	}
  3331  
  3332  	// Confirm that large entities filling up RPC works
  3333  	testRPCBatchSize(memcacheMaxValueSize, true)
  3334  
  3335  	// Confirm with middle-sized entities
  3336  	testRPCBatchSize(100000, true)
  3337  
  3338  	// Confirm overhead not exceeding our expectations by filling up RPC with tiny entities
  3339  	// NOTE: We don't read back because the memcache emulator is way too slow to do this
  3340  	testRPCBatchSize(1000, false)
  3341  
  3342  	// Test RPC limit handling via goon
  3343  	g := FromContext(c)
  3344  
  3345  	// Aim for roughly 80% of max entity size
  3346  	data := bytes.Repeat([]byte{1, 2, 3, 4}, memcacheMaxItemSize/5)
  3347  	// Aim for the maximum RPC size that we can get with datastore GetMulti,
  3348  	// which should still be larger than the maximum memcache RPC size
  3349  	count := int64(datastoreGetMultiMaxRPCSize / len(data))
  3350  	hds := make([]*HasData, 0, count)
  3351  	for i := int64(1); i <= count; i++ {
  3352  		hds = append(hds, &HasData{Id: i, Data: data})
  3353  	}
  3354  
  3355  	// Save all of these
  3356  	// TODO: Use PutMulti once goon supports splitting up too large PutMulti requests
  3357  	// keys, err := g.PutMulti(hds)
  3358  	// if err != nil {
  3359  	// 	t.Fatalf("Unexpected error on PutMulti: %v", err)
  3360  	// }
  3361  	var keys []*datastore.Key
  3362  	for _, hd := range hds {
  3363  		if key, err := g.Put(hd); err != nil {
  3364  			t.Fatalf("Unexpected error on Put: %v", err)
  3365  		} else {
  3366  			keys = append(keys, key)
  3367  		}
  3368  	}
  3369  
  3370  	// Flush all the caches
  3371  	g.FlushLocalCache()
  3372  	memcache.Flush(c)
  3373  
  3374  	// Fetch them all back to confirm datastore works,
  3375  	// and to also populate memcache
  3376  	for _, hd := range hds {
  3377  		hd.Data = nil
  3378  	}
  3379  	if err := g.GetMulti(hds); err != nil {
  3380  		t.Fatalf("Unexpected error on GetMulti: %v", err)
  3381  	}
  3382  	for _, hd := range hds {
  3383  		if !bytes.Equal(hd.Data, data) {
  3384  			t.Fatalf("Invalid data! %v", hd.Id)
  3385  		}
  3386  		hd.Data = nil
  3387  	}
  3388  
  3389  	// Flush local cache & datastore
  3390  	g.FlushLocalCache()
  3391  	if err := datastore.DeleteMulti(c, keys); err != nil {
  3392  		t.Fatalf("Unexpected error on DeleteMulti: %v", err)
  3393  	}
  3394  
  3395  	// Fetch them all back again, this time from memcache
  3396  	if err := g.GetMulti(hds); err != nil {
  3397  		t.Fatalf("Unexpected error on GetMulti: %v", err)
  3398  	}
  3399  	for _, hd := range hds {
  3400  		if !bytes.Equal(hd.Data, data) {
  3401  			t.Fatalf("Invalid data! %v", hd.Id)
  3402  		}
  3403  		hd.Data = nil
  3404  	}
  3405  }