LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2011 The Go Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style | |
3 // license that can be found in the LICENSE file. | |
4 | |
5 package build | |
6 | |
7 // TODO(adg): test authentication | |
8 | |
9 import ( | |
10 "appengine" | |
11 "appengine/datastore" | |
12 "bytes" | |
13 "encoding/json" | |
14 "errors" | |
15 "fmt" | |
16 "io" | |
17 "net/http" | |
18 "net/http/httptest" | |
19 "net/url" | |
20 "strings" | |
21 "time" | |
22 ) | |
23 | |
24 func init() { | |
25 http.HandleFunc("/buildtest", testHandler) | |
26 } | |
27 | |
28 var testEntityKinds = []string{ | |
29 "Package", | |
30 "Commit", | |
31 "Result", | |
32 "Log", | |
33 } | |
34 | |
35 const testPkg = "code.google.com/p/go.test" | |
36 | |
37 var testPackage = &Package{Name: "Test", Kind: "subrepo", Path: testPkg} | |
38 | |
39 var testPackages = []*Package{ | |
40 {Name: "Go", Path: ""}, | |
41 testPackage, | |
42 } | |
43 | |
44 var tCommitTime = time.Now().Add(-time.Hour * 24 * 7) | |
45 | |
46 func tCommit(hash, parentHash, path string) *Commit { | |
47 tCommitTime.Add(time.Hour) // each commit should have a different time | |
48 return &Commit{ | |
49 PackagePath: path, | |
50 Hash: hash, | |
51 ParentHash: parentHash, | |
52 Time: tCommitTime, | |
53 User: "adg", | |
54 Desc: "change description " + hash, | |
55 } | |
56 } | |
57 | |
58 var testRequests = []struct { | |
59 path string | |
60 vals url.Values | |
61 req interface{} | |
62 res interface{} | |
63 }{ | |
64 // Packages | |
65 {"/packages?kind=subrepo", nil, nil, []*Package{testPackage}}, | |
66 | |
67 // Go repo | |
68 {"/commit", nil, tCommit("0001", "0000", ""), nil}, | |
69 {"/commit", nil, tCommit("0002", "0001", ""), nil}, | |
70 {"/commit", nil, tCommit("0003", "0002", ""), nil}, | |
71 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, | |
72 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd
64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, | |
73 {"/result", nil, &Result{Builder: "linux-386", Hash: "0001", OK: true},
nil}, | |
74 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, | |
75 {"/result", nil, &Result{Builder: "linux-386", Hash: "0002", OK: true},
nil}, | |
76 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, | |
77 | |
78 // multiple builders | |
79 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd
64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, | |
80 {"/result", nil, &Result{Builder: "linux-amd64", Hash: "0003", OK: true}
, nil}, | |
81 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, | |
82 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd
64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0002"}}}, | |
83 | |
84 // branches | |
85 {"/commit", nil, tCommit("0004", "0003", ""), nil}, | |
86 {"/commit", nil, tCommit("0005", "0002", ""), nil}, | |
87 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}}, | |
88 {"/result", nil, &Result{Builder: "linux-386", Hash: "0005", OK: true},
nil}, | |
89 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0004"}}}, | |
90 {"/result", nil, &Result{Builder: "linux-386", Hash: "0004", OK: false},
nil}, | |
91 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}}, | |
92 | |
93 // logs | |
94 {"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false,
Log: "test"}, nil}, | |
95 {"/log/a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", nil, nil, "test"}, | |
96 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, nil}, | |
97 | |
98 // repeat failure (shouldn't re-send mail) | |
99 {"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false,
Log: "test"}, nil}, | |
100 | |
101 // non-Go repos | |
102 {"/commit", nil, tCommit("1001", "1000", testPkg), nil}, | |
103 {"/commit", nil, tCommit("1002", "1001", testPkg), nil}, | |
104 {"/commit", nil, tCommit("1003", "1002", testPkg), nil}, | |
105 {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}
, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package
", Data: &Commit{Hash: "1003"}}}, | |
106 {"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Has
h: "1003", GoHash: "0001", OK: true}, nil}, | |
107 {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}
, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package
", Data: &Commit{Hash: "1002"}}}, | |
108 {"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Has
h: "1002", GoHash: "0001", OK: true}, nil}, | |
109 {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}
, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package
", Data: &Commit{Hash: "1001"}}}, | |
110 {"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Has
h: "1001", GoHash: "0001", OK: true}, nil}, | |
111 {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}
, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, nil}, | |
112 {"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}
, "packagePath": {testPkg}, "goHash": {"0002"}}, nil, &Todo{Kind: "build-package
", Data: &Commit{Hash: "1003"}}}, | |
113 | |
114 // re-build Go revision for stale subrepos | |
115 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}}, | |
116 {"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Has
h: "1001", GoHash: "0005", OK: false, Log: "boo"}, nil}, | |
117 {"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386
"}}, nil, nil}, | |
118 } | |
119 | |
120 func testHandler(w http.ResponseWriter, r *http.Request) { | |
121 if !appengine.IsDevAppServer() { | |
122 fmt.Fprint(w, "These tests must be run under the dev_appserver."
) | |
123 return | |
124 } | |
125 c := appengine.NewContext(r) | |
126 if err := nukeEntities(c, testEntityKinds); err != nil { | |
127 logErr(w, r, err) | |
128 return | |
129 } | |
130 if r.FormValue("nukeonly") != "" { | |
131 fmt.Fprint(w, "OK") | |
132 return | |
133 } | |
134 | |
135 for _, p := range testPackages { | |
136 if _, err := datastore.Put(c, p.Key(c), p); err != nil { | |
137 logErr(w, r, err) | |
138 return | |
139 } | |
140 } | |
141 | |
142 for i, t := range testRequests { | |
143 c.Infof("running test %d %s", i, t.path) | |
144 errorf := func(format string, args ...interface{}) { | |
145 fmt.Fprintf(w, "%d %s: ", i, t.path) | |
146 fmt.Fprintf(w, format, args...) | |
147 fmt.Fprintln(w) | |
148 } | |
149 var body io.ReadWriter | |
150 if t.req != nil { | |
151 body = new(bytes.Buffer) | |
152 json.NewEncoder(body).Encode(t.req) | |
153 } | |
154 url := "http://" + domain + t.path | |
155 if t.vals != nil { | |
156 url += "?" + t.vals.Encode() | |
157 } | |
158 req, err := http.NewRequest("POST", url, body) | |
159 if err != nil { | |
160 logErr(w, r, err) | |
161 return | |
162 } | |
163 if t.req != nil { | |
164 req.Method = "POST" | |
165 } | |
166 req.Header = r.Header | |
167 rec := httptest.NewRecorder() | |
168 | |
169 // Make the request | |
170 http.DefaultServeMux.ServeHTTP(rec, req) | |
171 | |
172 if rec.Code != 0 && rec.Code != 200 { | |
173 errorf(rec.Body.String()) | |
174 return | |
175 } | |
176 resp := new(dashResponse) | |
177 | |
178 // If we're expecting a *Todo value, | |
179 // prime the Response field with a Todo and a Commit inside it. | |
180 if _, ok := t.res.(*Todo); ok { | |
181 resp.Response = &Todo{Data: &Commit{}} | |
182 } | |
183 | |
184 if strings.HasPrefix(t.path, "/log/") { | |
185 resp.Response = rec.Body.String() | |
186 } else { | |
187 err := json.NewDecoder(rec.Body).Decode(resp) | |
188 if err != nil { | |
189 errorf("decoding response: %v", err) | |
190 return | |
191 } | |
192 } | |
193 if e, ok := t.res.(string); ok { | |
194 g, ok := resp.Response.(string) | |
195 if !ok { | |
196 errorf("Response not string: %T", resp.Response) | |
197 return | |
198 } | |
199 if g != e { | |
200 errorf("response mismatch: got %q want %q", g, e
) | |
201 return | |
202 } | |
203 } | |
204 if e, ok := t.res.(*Todo); ok { | |
205 g, ok := resp.Response.(*Todo) | |
206 if !ok { | |
207 errorf("Response not *Todo: %T", resp.Response) | |
208 return | |
209 } | |
210 if e.Data == nil && g.Data != nil { | |
211 errorf("Response.Data should be nil, got: %v", g
.Data) | |
212 return | |
213 } | |
214 if g.Data == nil { | |
215 errorf("Response.Data is nil, want: %v", e.Data) | |
216 return | |
217 } | |
218 gd, ok := g.Data.(*Commit) | |
219 if !ok { | |
220 errorf("Response.Data not *Commit: %T", g.Data) | |
221 return | |
222 } | |
223 if eh := e.Data.(*Commit).Hash; eh != gd.Hash { | |
224 errorf("hashes don't match: got %q, want %q", gd
.Hash, eh) | |
225 return | |
226 } | |
227 } | |
228 if t.res == nil && resp.Response != nil { | |
229 errorf("response mismatch: got %q expected <nil>", | |
230 resp.Response) | |
231 return | |
232 } | |
233 } | |
234 fmt.Fprint(w, "PASS\nYou should see only one mail notification (for 0003
/linux-386) in the dev_appserver logs.") | |
235 } | |
236 | |
237 func nukeEntities(c appengine.Context, kinds []string) error { | |
238 if !appengine.IsDevAppServer() { | |
239 return errors.New("can't nuke production data") | |
240 } | |
241 var keys []*datastore.Key | |
242 for _, kind := range kinds { | |
243 q := datastore.NewQuery(kind).KeysOnly() | |
244 for t := q.Run(c); ; { | |
245 k, err := t.Next(nil) | |
246 if err == datastore.Done { | |
247 break | |
248 } | |
249 if err != nil { | |
250 return err | |
251 } | |
252 keys = append(keys, k) | |
253 } | |
254 } | |
255 return datastore.DeleteMulti(c, keys) | |
256 } | |
LEFT | RIGHT |