gopkg.in/httprequest.v1@v1.2.1/bench_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the LGPLv3, see LICENCE file for details. 3 4 package httprequest_test 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/http/httptest" 10 "net/url" 11 "reflect" 12 "strconv" 13 "testing" 14 "time" 15 16 "github.com/julienschmidt/httprouter" 17 "gopkg.in/errgo.v1" 18 19 "gopkg.in/httprequest.v1" 20 ) 21 22 const dateFormat = "2006-01-02" 23 24 type testResult struct { 25 Key string `json:",omitempty"` 26 Date string `json:",omitempty"` 27 Count int64 28 } 29 30 type testParams2Fields struct { 31 Id string `httprequest:"id,path"` 32 Limit int `httprequest:"limit,form"` 33 } 34 35 type testParams4Fields struct { 36 Id string `httprequest:"id,path"` 37 Limit int `httprequest:"limit,form"` 38 From dateTime `httprequest:"from,form"` 39 To dateTime `httprequest:"to,form"` 40 } 41 42 type dateTime struct { 43 time.Time 44 } 45 46 func (dt *dateTime) UnmarshalText(b []byte) (err error) { 47 dt.Time, err = time.Parse(dateFormat, string(b)) 48 return 49 } 50 51 type testParams2StringFields struct { 52 Field0 string `httprequest:",form"` 53 Field1 string `httprequest:",form"` 54 } 55 56 type testParams4StringFields struct { 57 Field0 string `httprequest:",form"` 58 Field1 string `httprequest:",form"` 59 Field2 string `httprequest:",form"` 60 Field3 string `httprequest:",form"` 61 } 62 63 type testParams8StringFields struct { 64 Field0 string `httprequest:",form"` 65 Field1 string `httprequest:",form"` 66 Field2 string `httprequest:",form"` 67 Field3 string `httprequest:",form"` 68 Field4 string `httprequest:",form"` 69 Field5 string `httprequest:",form"` 70 Field6 string `httprequest:",form"` 71 Field7 string `httprequest:",form"` 72 } 73 74 type testParams16StringFields struct { 75 Field0 string `httprequest:",form"` 76 Field1 string `httprequest:",form"` 77 Field2 string `httprequest:",form"` 78 Field3 string `httprequest:",form"` 79 Field4 string `httprequest:",form"` 80 Field5 string `httprequest:",form"` 81 Field6 string `httprequest:",form"` 82 Field7 string `httprequest:",form"` 83 Field8 string `httprequest:",form"` 84 Field9 string `httprequest:",form"` 85 Field10 string `httprequest:",form"` 86 Field11 string `httprequest:",form"` 87 Field12 string `httprequest:",form"` 88 Field13 string `httprequest:",form"` 89 Field14 string `httprequest:",form"` 90 Field15 string `httprequest:",form"` 91 } 92 93 func BenchmarkUnmarshal2Fields(b *testing.B) { 94 params := httprequest.Params{ 95 Request: &http.Request{ 96 Form: url.Values{ 97 "limit": {"2000"}, 98 }, 99 }, 100 PathVar: httprouter.Params{{ 101 Key: "id", 102 Value: "someid", 103 }}, 104 } 105 var arg testParams2Fields 106 107 b.ResetTimer() 108 for i := 0; i < b.N; i++ { 109 arg = testParams2Fields{} 110 err := httprequest.Unmarshal(params, &arg) 111 if err != nil { 112 b.Fatalf("unmarshal failed: %v", err) 113 } 114 } 115 b.StopTimer() 116 if !reflect.DeepEqual(arg, testParams2Fields{ 117 Id: "someid", 118 Limit: 2000, 119 }) { 120 b.Errorf("unexpected result: got %#v", arg) 121 } 122 } 123 124 func BenchmarkHandle2FieldsTrad(b *testing.B) { 125 results := []testResult{} 126 benchmarkHandle2Fields(b, testServer.HandleJSON(func(p httprequest.Params) (interface{}, error) { 127 limit := -1 128 if limitStr := p.Request.Form.Get("limit"); limitStr != "" { 129 var err error 130 limit, err = strconv.Atoi(limitStr) 131 if err != nil || limit <= 0 { 132 panic("unreachable") 133 } 134 } 135 if id := p.PathVar.ByName("id"); id == "" { 136 panic("unreachable") 137 } 138 return results, nil 139 })) 140 } 141 142 func BenchmarkHandle2Fields(b *testing.B) { 143 results := []testResult{} 144 benchmarkHandle2Fields(b, testServer.Handle(func(p httprequest.Params, arg *testParams2Fields) ([]testResult, error) { 145 if arg.Limit <= 0 { 146 panic("unreachable") 147 } 148 return results, nil 149 }).Handle) 150 } 151 152 func BenchmarkHandle2FieldsUnmarshalOnly(b *testing.B) { 153 results := []testResult{} 154 benchmarkHandle2Fields(b, testServer.HandleJSON(func(p httprequest.Params) (interface{}, error) { 155 var arg testParams2Fields 156 if err := httprequest.Unmarshal(p, &arg); err != nil { 157 return nil, err 158 } 159 if arg.Limit <= 0 { 160 panic("unreachable") 161 } 162 return results, nil 163 })) 164 } 165 166 func benchmarkHandle2Fields(b *testing.B, handle func(w http.ResponseWriter, req *http.Request, pvar httprouter.Params)) { 167 rec := httptest.NewRecorder() 168 params := httprequest.Params{ 169 Request: &http.Request{ 170 Form: url.Values{ 171 "limit": {"2000"}, 172 }, 173 }, 174 PathVar: httprouter.Params{{ 175 Key: "id", 176 Value: "someid", 177 }}, 178 } 179 b.ResetTimer() 180 for i := 0; i < b.N; i++ { 181 rec.Body.Reset() 182 handle(rec, params.Request, params.PathVar) 183 } 184 } 185 186 func BenchmarkUnmarshal4Fields(b *testing.B) { 187 fromDate, err1 := time.Parse(dateFormat, "2010-10-10") 188 toDate, err2 := time.Parse(dateFormat, "2011-11-11") 189 if err1 != nil || err2 != nil { 190 b.Fatalf("bad times") 191 } 192 type P testParams4Fields 193 params := httprequest.Params{ 194 Request: &http.Request{ 195 Form: url.Values{ 196 "limit": {"2000"}, 197 "from": {fromDate.Format(dateFormat)}, 198 "to": {toDate.Format(dateFormat)}, 199 }, 200 }, 201 PathVar: httprouter.Params{{ 202 Key: "id", 203 Value: "someid", 204 }}, 205 } 206 var args P 207 208 b.ResetTimer() 209 for i := 0; i < b.N; i++ { 210 args = P{} 211 err := httprequest.Unmarshal(params, &args) 212 if err != nil { 213 b.Fatalf("unmarshal failed: %v", err) 214 } 215 } 216 b.StopTimer() 217 if !reflect.DeepEqual(args, P{ 218 Id: "someid", 219 Limit: 2000, 220 From: dateTime{fromDate}, 221 To: dateTime{toDate}, 222 }) { 223 b.Errorf("unexpected result: got %#v", args) 224 } 225 } 226 227 func BenchmarkHandle4FieldsTrad(b *testing.B) { 228 results := []testResult{} 229 benchmarkHandle4Fields(b, testServer.HandleJSON(func(p httprequest.Params) (interface{}, error) { 230 start, stop, err := parseDateRange(p.Request.Form) 231 if err != nil { 232 panic("unreachable") 233 } 234 _ = start 235 _ = stop 236 limit := -1 237 if limitStr := p.Request.Form.Get("limit"); limitStr != "" { 238 limit, err = strconv.Atoi(limitStr) 239 if err != nil || limit <= 0 { 240 panic("unreachable") 241 } 242 } 243 if id := p.PathVar.ByName("id"); id == "" { 244 panic("unreachable") 245 } 246 return results, nil 247 })) 248 } 249 250 // parseDateRange parses a date range as specified in an http 251 // request. The returned times will be zero if not specified. 252 func parseDateRange(form url.Values) (start, stop time.Time, err error) { 253 if v := form.Get("start"); v != "" { 254 var err error 255 start, err = time.Parse(dateFormat, v) 256 if err != nil { 257 return time.Time{}, time.Time{}, errgo.Newf("invalid 'start' value %q", v) 258 } 259 } 260 if v := form.Get("stop"); v != "" { 261 var err error 262 stop, err = time.Parse(dateFormat, v) 263 if err != nil { 264 return time.Time{}, time.Time{}, errgo.Newf("invalid 'stop' value %q", v) 265 } 266 // Cover all timestamps within the stop day. 267 stop = stop.Add(24*time.Hour - 1*time.Second) 268 } 269 return 270 } 271 272 func BenchmarkHandle4Fields(b *testing.B) { 273 results := []testResult{} 274 benchmarkHandle4Fields(b, testServer.Handle(func(p httprequest.Params, arg *testParams4Fields) ([]testResult, error) { 275 if arg.To.Before(arg.From.Time) { 276 panic("unreachable") 277 } 278 if arg.Limit <= 0 { 279 panic("unreachable") 280 } 281 return results, nil 282 }).Handle) 283 } 284 285 func BenchmarkHandle4FieldsUnmarshalOnly(b *testing.B) { 286 results := []testResult{} 287 benchmarkHandle4Fields(b, testServer.HandleJSON(func(p httprequest.Params) (interface{}, error) { 288 var arg testParams4Fields 289 if err := httprequest.Unmarshal(p, &arg); err != nil { 290 return nil, err 291 } 292 if arg.To.Before(arg.From.Time) { 293 panic("unreachable") 294 } 295 if arg.Limit <= 0 { 296 panic("unreachable") 297 } 298 return results, nil 299 })) 300 } 301 302 func benchmarkHandle4Fields(b *testing.B, handle func(w http.ResponseWriter, req *http.Request, pvar httprouter.Params)) { 303 // example taken from charmstore changes/published endpoint 304 fromDate, err1 := time.Parse(dateFormat, "2010-10-10") 305 toDate, err2 := time.Parse(dateFormat, "2011-11-11") 306 if err1 != nil || err2 != nil { 307 b.Fatalf("bad times") 308 } 309 rec := httptest.NewRecorder() 310 params := httprequest.Params{ 311 Request: &http.Request{ 312 Form: url.Values{ 313 "limit": {"2000"}, 314 "from": {fromDate.Format(dateFormat)}, 315 "to": {toDate.Format(dateFormat)}, 316 }, 317 }, 318 PathVar: httprouter.Params{{ 319 Key: "id", 320 Value: "someid", 321 }}, 322 } 323 b.ResetTimer() 324 for i := 0; i < b.N; i++ { 325 rec.Body.Reset() 326 handle(rec, params.Request, params.PathVar) 327 } 328 } 329 330 func BenchmarkHandle2StringFields(b *testing.B) { 331 benchmarkHandleNFields(b, 2, testServer.Handle(func(p httprequest.Params, arg *testParams2StringFields) error { 332 return nil 333 }).Handle) 334 } 335 336 func BenchmarkHandle2StringFieldsUnmarshalOnly(b *testing.B) { 337 benchmarkHandleNFields(b, 2, testServer.HandleErrors(func(p httprequest.Params) error { 338 var arg testParams2StringFields 339 return httprequest.Unmarshal(p, &arg) 340 })) 341 } 342 343 func BenchmarkHandle2StringFieldsTrad(b *testing.B) { 344 benchmarkHandleNFields(b, 2, testServer.HandleErrors(func(p httprequest.Params) error { 345 var arg testParams2StringFields 346 arg.Field0 = p.Request.Form.Get("Field0") 347 arg.Field1 = p.Request.Form.Get("Field1") 348 return nil 349 })) 350 } 351 352 func BenchmarkHandle4StringFields(b *testing.B) { 353 benchmarkHandleNFields(b, 4, testServer.Handle(func(p httprequest.Params, arg *testParams4StringFields) error { 354 return nil 355 }).Handle) 356 } 357 358 func BenchmarkHandle4StringFieldsUnmarshalOnly(b *testing.B) { 359 benchmarkHandleNFields(b, 4, testServer.HandleErrors(func(p httprequest.Params) error { 360 var arg testParams4StringFields 361 return httprequest.Unmarshal(p, &arg) 362 })) 363 } 364 365 func BenchmarkHandle4StringFieldsTrad(b *testing.B) { 366 benchmarkHandleNFields(b, 4, testServer.HandleErrors(func(p httprequest.Params) error { 367 var arg testParams4StringFields 368 arg.Field0 = p.Request.Form.Get("Field0") 369 arg.Field1 = p.Request.Form.Get("Field1") 370 arg.Field2 = p.Request.Form.Get("Field2") 371 arg.Field3 = p.Request.Form.Get("Field3") 372 return nil 373 })) 374 } 375 376 func BenchmarkHandle8StringFields(b *testing.B) { 377 benchmarkHandleNFields(b, 8, testServer.Handle(func(p httprequest.Params, arg *testParams8StringFields) error { 378 return nil 379 }).Handle) 380 } 381 382 func BenchmarkHandle8StringFieldsUnmarshalOnly(b *testing.B) { 383 benchmarkHandleNFields(b, 8, testServer.HandleErrors(func(p httprequest.Params) error { 384 var arg testParams8StringFields 385 return httprequest.Unmarshal(p, &arg) 386 })) 387 } 388 389 func BenchmarkHandle8StringFieldsTrad(b *testing.B) { 390 benchmarkHandleNFields(b, 8, testServer.HandleErrors(func(p httprequest.Params) error { 391 var arg testParams8StringFields 392 arg.Field0 = p.Request.Form.Get("Field0") 393 arg.Field1 = p.Request.Form.Get("Field1") 394 arg.Field2 = p.Request.Form.Get("Field2") 395 arg.Field3 = p.Request.Form.Get("Field3") 396 arg.Field4 = p.Request.Form.Get("Field4") 397 arg.Field5 = p.Request.Form.Get("Field5") 398 arg.Field6 = p.Request.Form.Get("Field6") 399 arg.Field7 = p.Request.Form.Get("Field7") 400 return nil 401 })) 402 } 403 404 func BenchmarkHandle16StringFields(b *testing.B) { 405 benchmarkHandleNFields(b, 16, testServer.Handle(func(p httprequest.Params, arg *testParams16StringFields) error { 406 return nil 407 }).Handle) 408 } 409 410 func BenchmarkHandle16StringFieldsUnmarshalOnly(b *testing.B) { 411 benchmarkHandleNFields(b, 16, testServer.HandleErrors(func(p httprequest.Params) error { 412 var arg testParams16StringFields 413 return httprequest.Unmarshal(p, &arg) 414 })) 415 } 416 417 func BenchmarkHandle16StringFieldsTrad(b *testing.B) { 418 benchmarkHandleNFields(b, 16, testServer.HandleErrors(func(p httprequest.Params) error { 419 var arg testParams16StringFields 420 arg.Field0 = p.Request.Form.Get("Field0") 421 arg.Field1 = p.Request.Form.Get("Field1") 422 arg.Field2 = p.Request.Form.Get("Field2") 423 arg.Field3 = p.Request.Form.Get("Field3") 424 arg.Field4 = p.Request.Form.Get("Field4") 425 arg.Field5 = p.Request.Form.Get("Field5") 426 arg.Field6 = p.Request.Form.Get("Field6") 427 arg.Field7 = p.Request.Form.Get("Field7") 428 arg.Field8 = p.Request.Form.Get("Field8") 429 arg.Field9 = p.Request.Form.Get("Field9") 430 arg.Field10 = p.Request.Form.Get("Field10") 431 arg.Field11 = p.Request.Form.Get("Field11") 432 arg.Field12 = p.Request.Form.Get("Field12") 433 arg.Field13 = p.Request.Form.Get("Field13") 434 arg.Field14 = p.Request.Form.Get("Field14") 435 arg.Field15 = p.Request.Form.Get("Field15") 436 return nil 437 })) 438 } 439 440 func benchmarkHandleNFields(b *testing.B, n int, handle func(w http.ResponseWriter, req *http.Request, pvar httprouter.Params)) { 441 form := make(url.Values) 442 for i := 0; i < n; i++ { 443 form[fmt.Sprint("Field", i)] = []string{fmt.Sprintf("field %d", i)} 444 } 445 rec := httptest.NewRecorder() 446 params := httprequest.Params{ 447 Request: &http.Request{ 448 Form: form, 449 }, 450 } 451 b.ResetTimer() 452 for i := 0; i < b.N; i++ { 453 rec.Body.Reset() 454 handle(rec, params.Request, params.PathVar) 455 } 456 }