Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(630)

Side by Side Diff: go/src/infra/appengine/test-results/model/test_file.go

Issue 2250023002: test-results: bug fixes and tests for TestFile Put, PutData (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@cl-ing_upload
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | go/src/infra/appengine/test-results/model/test_file_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 package model 1 package model
2 2
3 import ( 3 import (
4 "bytes" 4 "bytes"
5 "fmt" 5 "fmt"
6 "io" 6 "io"
7 "io/ioutil" 7 "io/ioutil"
8 "math" 8 "math"
9 "time" 9 "time"
10 10
11 "golang.org/x/net/context" 11 "golang.org/x/net/context"
12 12
13 "github.com/luci/gae/service/datastore" 13 "github.com/luci/gae/service/datastore"
14 "github.com/luci/luci-go/common/errors" 14 "github.com/luci/luci-go/common/errors"
15 "github.com/luci/luci-go/common/logging" 15 "github.com/luci/luci-go/common/logging"
16 ) 16 )
17 17
18 // IsAggregateTestFile returns whether filename is that of an aggregate test fil e. 18 // IsAggregateTestFile returns whether filename is that of an aggregate test fil e.
19 func IsAggregateTestFile(filename string) bool { 19 func IsAggregateTestFile(filename string) bool {
20 switch filename { 20 switch filename {
21 case "results.json", "results-small.json": 21 case "results.json", "results-small.json":
22 return true 22 return true
23 default: 23 default:
24 return false 24 return false
25 } 25 }
26 } 26 }
27 27
28 // BuildNum is int64 that is used to handle TestFile datastore records 28 // BuildNum is int64 that is used to handle TestFile datastore records
29 // with null build_number. The value is >= 0 if the datastore value 29 // with null build_number. The value is >= 0 if the datastore value
30 // was not null. 30 // was not null. The value is -1 for null datastore value.
31 type BuildNum int64 31 type BuildNum int64
32 32
33 var _ datastore.PropertyConverter = (*BuildNum)(nil) 33 var _ datastore.PropertyConverter = (*BuildNum)(nil)
34 34
35 // IsNil returns whether b had null value in datastore. 35 // IsNil returns whether b had null value in datastore.
36 func (b *BuildNum) IsNil() bool { return *b == -1 } 36 func (b *BuildNum) IsNil() bool { return *b == -1 }
37 37
38 // ToProperty is for implementing datastore.PropertyConverter. 38 // ToProperty is for implementing datastore.PropertyConverter.
39 func (b *BuildNum) ToProperty() (p datastore.Property, err error) { 39 func (b *BuildNum) ToProperty() (p datastore.Property, err error) {
40 if b.IsNil() { 40 if b.IsNil() {
(...skipping 17 matching lines...) Expand all
58 } 58 }
59 59
60 // DataEntry represents a DataEntry record. 60 // DataEntry represents a DataEntry record.
61 type DataEntry struct { 61 type DataEntry struct {
62 ID int64 `gae:"$id"` 62 ID int64 `gae:"$id"`
63 Data []byte `gae:"data,noindex"` 63 Data []byte `gae:"data,noindex"`
64 } 64 }
65 65
66 // TestFile represents a TestFile record. 66 // TestFile represents a TestFile record.
67 type TestFile struct { 67 type TestFile struct {
68 » ID int64 `gae:"$id"` 68 » ID int64 `gae:"$id"`
69 » BuildNumber BuildNum `gae:"build_number"` 69 » BuildNumber BuildNum `gae:"build_number"`
70 » Builder string `gae:"builder"` 70 » Builder string `gae:"builder"`
71 » DataKeys []*datastore.Key `gae:"data_keys,noindex"` 71 » Master string `gae:"master"`
72 » LastMod time.Time `gae:"date"` 72 » Name string `gae:"name"`
73 » Master string `gae:"master"` 73 » TestType string `gae:"test_type"`
74 » Name string `gae:"name"` 74
75 » TestType string `gae:"test_type"` 75 » // DataKeys is the keys to the DataEntry(s) that contain
76 » // the data for this TestFile.
77 » DataKeys []*datastore.Key `gae:"data_keys,noindex"`
78
79 » // LastMod is the last modified time.
80 » LastMod time.Time `gae:"date"`
76 81
77 // Data is the data in the DataEntry(s) pointed to by DataKeys. 82 // Data is the data in the DataEntry(s) pointed to by DataKeys.
78 // After loading a TestFile from the datastore, this field is 83 // After loading a TestFile from the datastore, this field is
79 // only available after GetData is called. To put updated 84 // only available after GetData is called. To put updated
80 // data in this field to the datastore, call PutData. 85 // data in this field to the datastore, call PutData.
81 // 86 //
82 » // Users should typically perform the following sequence of calls 87 » // Users will typically perform the following sequence of calls
83 // in a transaction: 88 // in a transaction:
84 // 89 //
85 » // - datastore.Get(tf) 90 » // ds = datastore.Get(ctx)
86 » // - tf.GetData() 91 » // err = ds.Get(tf)
87 » // - tf.PutData() 92 » // err = tf.GetData(ctx)
88 » // - datastore.Put(tf) 93 » // // manipulate tf.Data
89 » // - datastore.Delete(tf.OldDataKeys) 94 » // err = tf.PutData(ctx)
95 » // err = ds.Put(tf)
96 » // err = ds.Delete(tf.OldDataKeys)
90 // 97 //
91 Data io.Reader `gae:"-,noindex"` 98 Data io.Reader `gae:"-,noindex"`
92 99
93 // OldDataKeys is the keys of the old DataEntry(s) used 100 // OldDataKeys is the keys of the old DataEntry(s) used
94 // before putting the new data. This field is set after a 101 // before putting the new data. This field is set after a
95 // successful call to PutData. 102 // successful call to PutData.
96 // 103 //
97 // Users are responsible for deleting the DataEntry(s) 104 // Users are responsible for deleting the DataEntry(s)
98 // pointed to by these keys if they are no longer needed. 105 // pointed to by these keys if they are no longer needed.
99 OldDataKeys []*datastore.Key `gae:"-,noindex"` 106 OldDataKeys []*datastore.Key `gae:"-,noindex"`
100 107
101 » // newDataKeys is unused in this implementation. It is 108 » // NewDataKeys is UNUSED in this implementation. It is
102 // a remnant of the old Python implementation. 109 // a remnant of the old Python implementation.
103 » newDataKeys []*datastore.Key `gae:"new_data_keys,noindex"` 110 » NewDataKeys []*datastore.Key `gae:"new_data_keys,noindex"`
104 } 111 }
105 112
106 // GetData fetches data from the DataEntry(s) pointed to by tf.DataKeys 113 // GetData fetches data from the DataEntry(s) pointed to by tf.DataKeys
107 // and sets tf.Data to the fetched data. 114 // and sets tf.Data to the fetched data.
108 func (tf *TestFile) GetData(c context.Context) error { 115 func (tf *TestFile) GetData(c context.Context) error {
109 ids := make([]int64, len(tf.DataKeys)) 116 ids := make([]int64, len(tf.DataKeys))
110 for i, dk := range tf.DataKeys { 117 for i, dk := range tf.DataKeys {
111 ids[i] = dk.IntID() // Intentional: ignores the Kind stored in d k. 118 ids[i] = dk.IntID() // Intentional: ignores the Kind stored in d k.
112 } 119 }
113 ids, err := updatedBlobKeys(c, ids...) 120 ids, err := updatedBlobKeys(c, ids...)
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 // tf.Data and updates tf.LastMod and tf.DataKeys locally. 167 // tf.Data and updates tf.LastMod and tf.DataKeys locally.
161 // If the returned error is non-nil, tf will be unmodified, 168 // If the returned error is non-nil, tf will be unmodified,
162 // except that tf.Data may have been consumed. 169 // except that tf.Data may have been consumed.
163 func (tf *TestFile) putDataEntries(c context.Context) error { 170 func (tf *TestFile) putDataEntries(c context.Context) error {
164 // Maximum data entries in a TestFile. 171 // Maximum data entries in a TestFile.
165 const maxDataEntries = 30 172 const maxDataEntries = 30
166 // 1 megabyte is the maximum allowed blob length. 173 // 1 megabyte is the maximum allowed blob length.
167 // See https://code.googlesource.com/gocloud/+/master/datastore/prop.go# 29. 174 // See https://code.googlesource.com/gocloud/+/master/datastore/prop.go# 29.
168 const maxBlobLen = 1 << 20 175 const maxBlobLen = 1 << 20
169 176
170 » // TODO: Read maxBlobLen bytes at a time. See io.LimitedReader. 177 » // TODO(maybe): Read maxBlobLen bytes at a time. See io.LimitedReader.
171 178
172 data, err := ioutil.ReadAll(tf.Data) 179 data, err := ioutil.ReadAll(tf.Data)
173 if err != nil { 180 if err != nil {
174 return err 181 return err
175 } 182 }
176 183
177 if len(data) > maxDataEntries*maxBlobLen { 184 if len(data) > maxDataEntries*maxBlobLen {
178 return fmt.Errorf( 185 return fmt.Errorf(
179 "model: data too large %d bytes (max allowed %d bytes)", 186 "model: data too large %d bytes (max allowed %d bytes)",
180 len(data), 187 len(data),
(...skipping 12 matching lines...) Expand all
193 if err := datastore.Get(c).Put(dataEntries); err != nil { 200 if err := datastore.Get(c).Put(dataEntries); err != nil {
194 return err 201 return err
195 } 202 }
196 203
197 newKeys := make([]*datastore.Key, 0, len(dataEntries)) 204 newKeys := make([]*datastore.Key, 0, len(dataEntries))
198 for _, de := range dataEntries { 205 for _, de := range dataEntries {
199 newKeys = append(newKeys, datastore.Get(c).KeyForObj(&de)) 206 newKeys = append(newKeys, datastore.Get(c).KeyForObj(&de))
200 } 207 }
201 208
202 tf.DataKeys = newKeys 209 tf.DataKeys = newKeys
203 » tf.LastMod = time.Now() 210 » tf.LastMod = time.Now().UTC()
204 return nil 211 return nil
205 } 212 }
206 213
207 // updatedBlobKeys fetches the updated blob keys for the old keys from their blo bstore migration 214 // updatedBlobKeys fetches the updated blob keys for the old keys from their blo bstore migration
208 // records. len(n) will equal len(old) if there is no error. If len(old)>1 and t here is an error, 215 // records. len(n) will equal len(old) if there is no error. If len(old)>1 and t here is an error,
209 // err will be of type MultiError and the error at index i will correspond to th e 216 // err will be of type MultiError and the error at index i will correspond to th e
210 // error in retrieving the updated blob key for the key at index i in old. A 217 // error in retrieving the updated blob key for the key at index i in old. A
211 // nil error indicates that all values returned in n are valid. 218 // nil error indicates that all values returned in n are valid.
212 func updatedBlobKeys(c context.Context, old ...int64) (n []int64, err error) { 219 func updatedBlobKeys(c context.Context, old ...int64) (n []int64, err error) {
213 ds := datastore.Get(c) 220 ds := datastore.Get(c)
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
317 q = q.Order("-date") 324 q = q.Order("-date")
318 325
319 if p.Limit < 0 || p.Limit > defaultLimit { 326 if p.Limit < 0 || p.Limit > defaultLimit {
320 q = q.Limit(defaultLimit) 327 q = q.Limit(defaultLimit)
321 } else { 328 } else {
322 q = q.Limit(p.Limit) 329 q = q.Limit(p.Limit)
323 } 330 }
324 331
325 return q 332 return q
326 } 333 }
OLDNEW
« no previous file with comments | « no previous file | go/src/infra/appengine/test-results/model/test_file_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698