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 }