go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/impl/memory/testing_utils_test.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package memory
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"strings"
    21  	"time"
    22  
    23  	"go.chromium.org/luci/common/data/cmpbin"
    24  
    25  	ds "go.chromium.org/luci/gae/service/datastore"
    26  )
    27  
    28  func init() {
    29  	serializationDeterministic = true
    30  	ds.WritePropertyMapDeterministic = true
    31  }
    32  
    33  var nextMarker = "NEXT MARKER"
    34  var Next = &nextMarker
    35  
    36  var multiMarker = "MULTI MARKER"
    37  var Multi = &multiMarker
    38  
    39  // If you want an entry that is single to be treated as multi-, prepend it
    40  // with Multi. Terminate each set of property tokens with Next.
    41  //
    42  //	pmap(
    43  //	  "prop", "val", 0, 100, Next,
    44  //	  "other", "val", 0, 100, Next,
    45  //	  "name", Multi, "value", Next,
    46  //	)
    47  func pmap(stuff ...any) ds.PropertyMap {
    48  	ret := ds.PropertyMap{}
    49  
    50  	nom := func() (name string, toks []any, multi bool) {
    51  		var i int
    52  
    53  		for i = 0; i < len(stuff); i++ {
    54  			e := stuff[i]
    55  			switch {
    56  			case e == Next:
    57  				stuff = stuff[i+1:]
    58  				return
    59  			case e == Multi:
    60  				multi = true
    61  			case i == 0:
    62  				name = e.(string)
    63  			default:
    64  				toks = append(toks, e)
    65  			}
    66  		}
    67  
    68  		stuff = nil
    69  		return
    70  	}
    71  
    72  	for len(stuff) > 0 {
    73  		pname, toks, multi := nom()
    74  
    75  		var mp func(any) ds.Property
    76  		if pname[0] == '$' || (strings.HasPrefix(pname, "__") && strings.HasSuffix(pname, "__")) {
    77  			mp = propNI
    78  		} else {
    79  			mp = prop
    80  		}
    81  
    82  		if len(toks) == 1 && !multi {
    83  			if pname == "$key" {
    84  				// This will also populate $kind, $id and $parent.
    85  				ds.PopulateKey(ret, toks[0].(*ds.Key))
    86  			} else {
    87  				ret[pname] = mp(toks[0])
    88  			}
    89  		} else {
    90  			pslice := make(ds.PropertySlice, len(toks))
    91  			for i, tok := range toks {
    92  				pslice[i] = mp(tok)
    93  			}
    94  			ret[pname] = pslice
    95  		}
    96  	}
    97  
    98  	return ret
    99  }
   100  
   101  func nq(kindMaybe ...string) *ds.Query {
   102  	kind := "Foo"
   103  	if len(kindMaybe) == 1 {
   104  		kind = kindMaybe[0]
   105  	}
   106  	return ds.NewQuery(kind)
   107  }
   108  
   109  func indx(kind string, orders ...string) *ds.IndexDefinition {
   110  	ancestor := false
   111  	if kind[len(kind)-1] == '!' {
   112  		ancestor = true
   113  		kind = kind[:len(kind)-1]
   114  	}
   115  	ret := &ds.IndexDefinition{Kind: kind, Ancestor: ancestor}
   116  	for _, o := range orders {
   117  		col, err := ds.ParseIndexColumn(o)
   118  		if err != nil {
   119  			panic(err)
   120  		}
   121  		ret.SortBy = append(ret.SortBy, col)
   122  	}
   123  	return ret
   124  }
   125  
   126  var (
   127  	prop   = ds.MkProperty
   128  	propNI = ds.MkPropertyNI
   129  )
   130  
   131  func key(elems ...any) *ds.Key {
   132  	return ds.MkKeyContext("dev~app", "ns").MakeKey(elems...)
   133  }
   134  
   135  func die(err error) {
   136  	if err != nil {
   137  		panic(err)
   138  	}
   139  }
   140  
   141  // cat is a convenience method for concatenating anything with an underlying
   142  // byte representation into a single []byte.
   143  func cat(bytethings ...any) []byte {
   144  	err := error(nil)
   145  	buf := &bytes.Buffer{}
   146  	for _, thing := range bytethings {
   147  		switch x := thing.(type) {
   148  		case int64:
   149  			_, err = cmpbin.WriteInt(buf, x)
   150  		case int:
   151  			_, err = cmpbin.WriteInt(buf, int64(x))
   152  		case uint64:
   153  			_, err = cmpbin.WriteUint(buf, x)
   154  		case uint:
   155  			_, err = cmpbin.WriteUint(buf, uint64(x))
   156  		case float64:
   157  			_, err = cmpbin.WriteFloat64(buf, x)
   158  		case byte:
   159  			err = buf.WriteByte(x)
   160  		case ds.PropertyType:
   161  			err = buf.WriteByte(byte(x))
   162  		case string:
   163  			_, err = cmpbin.WriteString(buf, x)
   164  		case []byte:
   165  			_, err = buf.Write(x)
   166  		case time.Time:
   167  			err = ds.Serialize.Time(buf, x)
   168  		case *ds.Key:
   169  			err = ds.Serialize.Key(buf, x)
   170  		case *ds.IndexDefinition:
   171  			err = ds.Serialize.IndexDefinition(buf, *x)
   172  		case ds.Property:
   173  			err = ds.Serialize.Property(buf, x)
   174  		default:
   175  			panic(fmt.Errorf("I don't know how to deal with %T: %#v", thing, thing))
   176  		}
   177  		die(err)
   178  	}
   179  	ret := buf.Bytes()
   180  	if ret == nil {
   181  		ret = []byte{}
   182  	}
   183  	return ret
   184  }
   185  
   186  func icat(bytethings ...any) []byte {
   187  	ret := cat(bytethings...)
   188  	for i := range ret {
   189  		ret[i] ^= 0xFF
   190  	}
   191  	return ret
   192  }
   193  
   194  func sat(bytethings ...any) string {
   195  	return string(cat(bytethings...))
   196  }