OLD | NEW |
1 package frontend | 1 package frontend |
2 | 2 |
3 import ( | 3 import ( |
4 "bytes" | 4 "bytes" |
| 5 "encoding/json" |
| 6 "infra/appengine/test-results/model" |
5 "io" | 7 "io" |
6 "io/ioutil" | 8 "io/ioutil" |
7 "mime/multipart" | 9 "mime/multipart" |
8 "net/http" | 10 "net/http" |
9 "net/http/httptest" | 11 "net/http/httptest" |
| 12 "net/url" |
10 "os" | 13 "os" |
11 "path/filepath" | 14 "path/filepath" |
12 "testing" | 15 "testing" |
13 | 16 |
14 "golang.org/x/net/context" | 17 "golang.org/x/net/context" |
15 | 18 |
16 "github.com/luci/gae/impl/memory" | 19 "github.com/luci/gae/impl/memory" |
17 "github.com/luci/gae/service/datastore" | 20 "github.com/luci/gae/service/datastore" |
18 "github.com/luci/luci-go/server/router" | 21 "github.com/luci/luci-go/server/router" |
19 . "github.com/smartystreets/goconvey/convey" | 22 . "github.com/smartystreets/goconvey/convey" |
20 ) | 23 ) |
21 | 24 |
22 func withTestingContext(c *router.Context, next router.Handler) { | 25 func TestUploadAndGetHandlers(t *testing.T) { |
| 26 » t.Parallel() |
| 27 |
23 ctx := memory.Use(context.Background()) | 28 ctx := memory.Use(context.Background()) |
24 » ds := datastore.Get(ctx) | 29 » testFileIdx, err := datastore.FindAndParseIndexYAML(filepath.Join("testd
ata")) |
25 » testFileIdx, err := datastore.FindAndParseIndexYAML(filepath.Join("..",
"model", "testdata")) | |
26 if err != nil { | 30 if err != nil { |
27 panic(err) | 31 panic(err) |
28 } | 32 } |
| 33 ds := datastore.Get(ctx) |
29 ds.Testable().AddIndexes(testFileIdx...) | 34 ds.Testable().AddIndexes(testFileIdx...) |
30 ds.Testable().CatchupIndexes() | |
31 | 35 |
32 » c.Context = ctx | 36 » withTestingContext := func(c *router.Context, next router.Handler) { |
33 » next(c) | 37 » » c.Context = ctx |
34 } | 38 » » ds.Testable().CatchupIndexes() |
35 | 39 » » next(c) |
36 func TestUpload(t *testing.T) { | 40 » } |
37 » t.Parallel() | |
38 | 41 |
39 r := router.New() | 42 r := router.New() |
40 mw := router.NewMiddlewareChain(withTestingContext) | 43 mw := router.NewMiddlewareChain(withTestingContext) |
| 44 r.GET("/testfile", mw.Extend(templatesMiddleware()), getHandler) |
41 r.POST("/testfile/upload", mw.Extend(withParsedUploadForm), uploadHandle
r) | 45 r.POST("/testfile/upload", mw.Extend(withParsedUploadForm), uploadHandle
r) |
42 srv := httptest.NewServer(r) | 46 srv := httptest.NewServer(r) |
43 client := &http.Client{} | 47 client := &http.Client{} |
44 | 48 |
45 » Convey("upload", t, func() { | 49 » Convey("Upload and Get handlers", t, func() { |
46 » » Convey("no matching aggregate file in datastore", func() { | 50 » » Convey("upload full_results.json", func() { |
47 var buf bytes.Buffer | 51 var buf bytes.Buffer |
48 multi := multipart.NewWriter(&buf) | 52 multi := multipart.NewWriter(&buf) |
49 // Form files. | 53 // Form files. |
50 f, err := os.Open(filepath.Join("testdata", "full_result
s_0.json")) | 54 f, err := os.Open(filepath.Join("testdata", "full_result
s_0.json")) |
51 So(err, ShouldBeNil) | 55 So(err, ShouldBeNil) |
52 defer f.Close() | 56 defer f.Close() |
53 multiFile, err := multi.CreateFormFile("file", "full_res
ults.json") | 57 multiFile, err := multi.CreateFormFile("file", "full_res
ults.json") |
54 So(err, ShouldBeNil) | 58 So(err, ShouldBeNil) |
55 _, err = io.Copy(multiFile, f) | 59 _, err = io.Copy(multiFile, f) |
56 So(err, ShouldBeNil) | 60 So(err, ShouldBeNil) |
57 // Form fields. | 61 // Form fields. |
58 fields := []struct { | 62 fields := []struct { |
59 key, val string | 63 key, val string |
60 }{ | 64 }{ |
61 {"master", "chromium.chromiumos"}, | 65 {"master", "chromium.chromiumos"}, |
62 {"builder", "test-builder"}, | 66 {"builder", "test-builder"}, |
63 » » » » {"test_type", "test-type"}, | 67 » » » » {"testtype", "test-type"}, |
64 } | 68 } |
65 for _, field := range fields { | 69 for _, field := range fields { |
66 f, err := multi.CreateFormField(field.key) | 70 f, err := multi.CreateFormField(field.key) |
67 So(err, ShouldBeNil) | 71 So(err, ShouldBeNil) |
68 _, err = f.Write([]byte(field.val)) | 72 _, err = f.Write([]byte(field.val)) |
69 So(err, ShouldBeNil) | 73 So(err, ShouldBeNil) |
70 } | 74 } |
71 multi.Close() | 75 multi.Close() |
72 | 76 |
73 req, err := http.NewRequest("POST", srv.URL+"/testfile/u
pload", &buf) | 77 req, err := http.NewRequest("POST", srv.URL+"/testfile/u
pload", &buf) |
74 So(err, ShouldBeNil) | 78 So(err, ShouldBeNil) |
75 req.Header.Set("Content-Type", multi.FormDataContentType
()) | 79 req.Header.Set("Content-Type", multi.FormDataContentType
()) |
76 resp, err := client.Do(req) | 80 resp, err := client.Do(req) |
77 So(err, ShouldBeNil) | 81 So(err, ShouldBeNil) |
78 defer resp.Body.Close() | 82 defer resp.Body.Close() |
79 So(resp.StatusCode, ShouldEqual, http.StatusOK) | 83 So(resp.StatusCode, ShouldEqual, http.StatusOK) |
80 | 84 |
81 b, err := ioutil.ReadAll(resp.Body) | 85 b, err := ioutil.ReadAll(resp.Body) |
82 So(err, ShouldBeNil) | 86 So(err, ShouldBeNil) |
83 So(string(b), ShouldEqual, "OK") | 87 So(string(b), ShouldEqual, "OK") |
| 88 |
| 89 // Get results.json for uploaded full_results.json |
| 90 req, err = http.NewRequest("GET", srv.URL+"/testfile?"+u
rl.Values{ |
| 91 "master": {"chromium.chromiumos"}, |
| 92 "builder": {"test-builder"}, |
| 93 "testtype": {"test-type"}, |
| 94 "name": {"results.json"}, |
| 95 }.Encode(), nil) |
| 96 So(err, ShouldBeNil) |
| 97 resp, err = client.Do(req) |
| 98 So(err, ShouldBeNil) |
| 99 defer resp.Body.Close() |
| 100 So(resp.StatusCode, ShouldEqual, http.StatusOK) |
| 101 |
| 102 var aggr model.AggregateResult |
| 103 So(json.NewDecoder(resp.Body).Decode(&aggr), ShouldBeNil
) |
| 104 So(aggr, ShouldResemble, model.AggregateResult{ |
| 105 Version: model.ResultsVersion, |
| 106 Builder: "test-builder", |
| 107 BuilderInfo: &model.BuilderInfo{ |
| 108 SecondsEpoch: []int64{1406123456}, |
| 109 BuildNumbers: []model.Number{123}, |
| 110 ChromeRevs: []string{"67890"}, |
| 111 Tests: model.AggregateTest{ |
| 112 "Test1.testproc1": &model.Aggreg
ateTestLeaf{ |
| 113 Results: []model.Result
Summary{{1, "Q"}}, |
| 114 Runtimes: []model.Runtim
eSummary{{1, 1}}, |
| 115 }, |
| 116 }, |
| 117 FailuresByType: map[string][]int{ |
| 118 "FAIL": {0}, |
| 119 "PASS": {1}, |
| 120 "SKIP": {0}, |
| 121 }, |
| 122 FailureMap: model.FailureLongNames, |
| 123 }, |
| 124 }) |
| 125 |
| 126 // Get test list JSON for uploaded full_results.json |
| 127 req, err = http.NewRequest("GET", srv.URL+"/testfile?"+u
rl.Values{ |
| 128 "master": {"chromium.chromiumos"}, |
| 129 "builder": {"test-builder"}, |
| 130 "testtype": {"test-type"}, |
| 131 "name": {"results.json"}, |
| 132 "testlistjson": {"1"}, |
| 133 }.Encode(), nil) |
| 134 So(err, ShouldBeNil) |
| 135 resp, err = client.Do(req) |
| 136 So(err, ShouldBeNil) |
| 137 defer resp.Body.Close() |
| 138 So(resp.StatusCode, ShouldEqual, http.StatusOK) |
| 139 |
| 140 b, err = ioutil.ReadAll(resp.Body) |
| 141 So(err, ShouldBeNil) |
| 142 So(resp.Header.Get("Content-Type"), ShouldContainSubstri
ng, "application/json") |
| 143 So(bytes.TrimSpace(b), ShouldResemble, []byte(`{"test-bu
ilder":{"tests":{"Test1.testproc1":{}}}}`)) |
| 144 |
| 145 // HTML response |
| 146 req, err = http.NewRequest("GET", srv.URL+"/testfile?"+u
rl.Values{ |
| 147 "master": {"chromium.chromiumos"}, |
| 148 "builder": {"test-builder"}, |
| 149 "testtype": {"test-type"}, |
| 150 "name": {"full_results.json"}, |
| 151 }.Encode(), nil) |
| 152 So(err, ShouldBeNil) |
| 153 resp, err = client.Do(req) |
| 154 So(err, ShouldBeNil) |
| 155 defer resp.Body.Close() |
| 156 So(resp.StatusCode, ShouldEqual, http.StatusOK) |
| 157 So(resp.Header.Get("Content-Type"), ShouldContainSubstri
ng, "text/html") |
84 }) | 158 }) |
85 }) | 159 }) |
86 } | 160 } |
| 161 |
| 162 func TestUploadTestFile(t *testing.T) { |
| 163 t.Parallel() |
| 164 |
| 165 Convey("uploadTestFile", t, func() { |
| 166 Convey("data too large to fit in single datastore blob", func()
{ |
| 167 ctx := memory.Use(context.Background()) |
| 168 ctx = SetUploadParams(ctx, &UploadParams{ |
| 169 Master: "foo", |
| 170 Builder: "bar", |
| 171 TestType: "baz", |
| 172 }) |
| 173 data, err := ioutil.ReadFile(filepath.Join("testdata", "
full_results_0.json")) |
| 174 So(err, ShouldBeNil) |
| 175 data = bytes.TrimSpace(data) |
| 176 So(uploadTestFile(ctx, bytes.NewReader(data), "full_resu
lts.json"), ShouldBeNil) |
| 177 |
| 178 Convey("get uploaded data", func() { |
| 179 datastore.Get(ctx).Testable().CatchupIndexes() |
| 180 q := datastore.NewQuery("TestFile") |
| 181 q = q.Eq("master", "foo") |
| 182 q = q.Eq("builder", "bar") |
| 183 q = q.Eq("test_type", "baz") |
| 184 q = q.Eq("name", "full_results.json") |
| 185 tf, err := getFirstTestFile(ctx, q) |
| 186 So(err, ShouldBeNil) |
| 187 |
| 188 So(tf.GetData(ctx), ShouldBeNil) |
| 189 b, err := ioutil.ReadAll(tf.Data) |
| 190 So(err, ShouldBeNil) |
| 191 So(bytes.TrimSpace(b), ShouldResemble, data) |
| 192 }) |
| 193 }) |
| 194 }) |
| 195 } |
| 196 |
| 197 func TestUpdateIncremental(t *testing.T) { |
| 198 t.Parallel() |
| 199 |
| 200 Convey("updateIncremental", t, func() { |
| 201 Convey("simple: updates corresponding aggregate files", func() { |
| 202 ctx := memory.Use(context.Background()) |
| 203 idx, err := datastore.FindAndParseIndexYAML(filepath.Joi
n("testdata")) |
| 204 So(err, ShouldBeNil) |
| 205 ds := datastore.Get(ctx) |
| 206 ds.Testable().AddIndexes(idx...) |
| 207 ds.Testable().CatchupIndexes() |
| 208 |
| 209 data, err := ioutil.ReadFile(filepath.Join("testdata", "
results_0.json")) |
| 210 So(err, ShouldBeNil) |
| 211 var orig model.AggregateResult |
| 212 So(json.Unmarshal(data, &orig), ShouldBeNil) |
| 213 resultsTf := model.TestFile{ |
| 214 Name: "results.json", |
| 215 Master: "chromium.swarm", |
| 216 TestType: "content_unittests", |
| 217 Builder: "Linux Swarm", |
| 218 BuildNumber: -1, |
| 219 Data: bytes.NewReader(data), |
| 220 } |
| 221 So(resultsTf.PutData(ctx), ShouldBeNil) |
| 222 So(ds.Put(&resultsTf), ShouldBeNil) |
| 223 ds.Testable().CatchupIndexes() |
| 224 |
| 225 incr := model.AggregateResult{ |
| 226 Builder: "Linux Swarm", |
| 227 BuilderInfo: &model.BuilderInfo{ |
| 228 BuildNumbers: []model.Number{7399}, |
| 229 }, |
| 230 } |
| 231 So(updateIncremental(SetUploadParams(ctx, &UploadParams{ |
| 232 Master: "chromium.swarm", |
| 233 TestType: "content_unittests", |
| 234 Builder: "Linux Swarm", |
| 235 }), &incr), ShouldBeNil) |
| 236 |
| 237 Convey("updates without error", func() { |
| 238 ds.Testable().CatchupIndexes() |
| 239 q := datastore.NewQuery("TestFile") |
| 240 q = q.Eq("master", "chromium.swarm") |
| 241 q = q.Eq("test_type", "content_unittests") |
| 242 q = q.Eq("builder", "Linux Swarm") |
| 243 q = q.Eq("name", "results.json") |
| 244 tf, err := getFirstTestFile(ctx, q) |
| 245 So(err, ShouldBeNil) |
| 246 |
| 247 So(tf.GetData(ctx), ShouldBeNil) |
| 248 var updated model.AggregateResult |
| 249 So(json.NewDecoder(tf.Data).Decode(&updated), Sh
ouldBeNil) |
| 250 |
| 251 // TODO(nishanths): also check `updated` ShouldR
esemble `expected`. |
| 252 }) |
| 253 }) |
| 254 }) |
| 255 } |
OLD | NEW |