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  }