diff --git a/.travis.yml b/.travis.yml index 9573daa6..edd60b79 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,10 @@ sudo: false + language: go + go: - - 1.4 - - tip + - 1.4 + - tip + +script: + - go run runner.go --test diff --git a/bench_test.go b/bench_test.go deleted file mode 100644 index 3dd46557..00000000 --- a/bench_test.go +++ /dev/null @@ -1,788 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package main - -import ( - "net/http" - "os" - "regexp" - "runtime" - "strings" - "testing" -) - -var benchRe *regexp.Regexp - -func isTested(name string) bool { - if benchRe == nil { - // Get -test.bench flag value (not accessible via flag package) - bench := "" - for _, arg := range os.Args { - if strings.HasPrefix(arg, "-test.bench=") { - // ignore the benchmark name after an underscore - bench = strings.SplitN(arg[12:], "_", 2)[0] - break - } - } - - // Compile RegExp to match Benchmark names - var err error - benchRe, err = regexp.Compile(bench) - if err != nil { - panic(err.Error()) - } - } - return benchRe.MatchString(name) -} - -func calcMem(name string, load func()) { - if !isTested(name) { - return - } - - m := new(runtime.MemStats) - - // before - runtime.GC() - runtime.ReadMemStats(m) - before := m.HeapAlloc - - load() - - // after - runtime.GC() - runtime.ReadMemStats(m) - after := m.HeapAlloc - println(" "+name+":", after-before, "Bytes") -} - -func benchRequest(b *testing.B, router http.Handler, r *http.Request) { - w := new(mockResponseWriter) - u := r.URL - rq := u.RawQuery - r.RequestURI = u.RequestURI() - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - u.RawQuery = rq - router.ServeHTTP(w, r) - } -} - -func benchRoutes(b *testing.B, router http.Handler, routes []route) { - w := new(mockResponseWriter) - r, _ := http.NewRequest("GET", "/", nil) - u := r.URL - rq := u.RawQuery - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < b.N; i++ { - for _, route := range routes { - r.Method = route.method - r.RequestURI = route.path - u.Path = route.path - u.RawQuery = rq - router.ServeHTTP(w, r) - } - } -} - -// Micro Benchmarks - -// Route with Param (no write) -func BenchmarkAce_Param(b *testing.B) { - router := loadAceSingle("GET", "/user/:name", aceHandle) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkBear_Param(b *testing.B) { - router := loadBearSingle("GET", "/user/{name}", bearHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkBeego_Param(b *testing.B) { - router := loadBeegoSingle("GET", "/user/:name", beegoHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkBone_Param(b *testing.B) { - router := loadBoneSingle("GET", "/user/:name", http.HandlerFunc(httpHandlerFunc)) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkDenco_Param(b *testing.B) { - router := loadDencoSingle("GET", "/user/:name", dencoHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkEcho_Param(b *testing.B) { - router := loadEchoSingle("GET", "/user/:name", echoHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGin_Param(b *testing.B) { - router := loadGinSingle("GET", "/user/:name", ginHandle) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGocraftWeb_Param(b *testing.B) { - router := loadGocraftWebSingle("GET", "/user/:name", gocraftWebHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGoji_Param(b *testing.B) { - router := loadGojiSingle("GET", "/user/:name", httpHandlerFunc) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGoJsonRest_Param(b *testing.B) { - router := loadGoJsonRestSingle("GET", "/user/:name", goJsonRestHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGoRestful_Param(b *testing.B) { - router := loadGoRestfulSingle("GET", "/user/{name}", goRestfulHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGorillaMux_Param(b *testing.B) { - router := loadGorillaMuxSingle("GET", "/user/{name}", httpHandlerFunc) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkHttpRouter_Param(b *testing.B) { - router := loadHttpRouterSingle("GET", "/user/:name", httpRouterHandle) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkHttpTreeMux_Param(b *testing.B) { - router := loadHttpTreeMuxSingle("GET", "/user/:name", httpTreeMuxHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkKocha_Param(b *testing.B) { - handler := new(kochaHandler) - router := loadKochaSingle( - "GET", "/user/:name", - handler, http.HandlerFunc(handler.Get), - ) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkMacaron_Param(b *testing.B) { - router := loadMacaronSingle("GET", "/user/:name", macaronHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkMartini_Param(b *testing.B) { - router := loadMartiniSingle("GET", "/user/:name", martiniHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkPat_Param(b *testing.B) { - router := loadPatSingle("GET", "/user/:name", http.HandlerFunc(httpHandlerFunc)) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} - -func BenchmarkPossum_Param(b *testing.B) { - router := loadPossumSingle("GET", "/user/:name", possumHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkR2router_Param(b *testing.B) { - router := loadR2routerSingle("GET", "/user/:name", r2routerHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} - -func BenchmarkRevel_Param(b *testing.B) { - router := loadRevelSingle("GET", "/user/:name", "RevelController.Handle") - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkRivet_Param(b *testing.B) { - router := loadRivetSingle("GET", "/user/:name", rivetHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkTango_Param(b *testing.B) { - router := loadTangoSingle("GET", "/user/:name", tangoHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkTigerTonic_Param(b *testing.B) { - router := loadTigerTonicSingle("GET", "/user/{name}", httpHandlerFunc) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkTraffic_Param(b *testing.B) { - router := loadTrafficSingle("GET", "/user/:name", trafficHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkVulcan_Param(b *testing.B) { - router := loadVulcanSingle("GET", "/user/:name", vulcanHandler) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} - -// func BenchmarkZeus_Param(b *testing.B) { -// router := loadZeusSingle("GET", "/user/:name", http.HandlerFunc(httpHandlerFunc)) - -// r, _ := http.NewRequest("GET", "/user/gordon", nil) -// benchRequest(b, router, r) -// } - -// Route with 5 Params (no write) -const fiveColon = "/:a/:b/:c/:d/:e" -const fiveBrace = "/{a}/{b}/{c}/{d}/{e}" -const fiveRoute = "/test/test/test/test/test" - -func BenchmarkAce_Param5(b *testing.B) { - router := loadAceSingle("GET", fiveColon, aceHandle) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkBear_Param5(b *testing.B) { - router := loadBearSingle("GET", fiveBrace, bearHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkBeego_Param5(b *testing.B) { - router := loadBeegoSingle("GET", fiveColon, beegoHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkBone_Param5(b *testing.B) { - router := loadBoneSingle("GET", fiveColon, http.HandlerFunc(httpHandlerFunc)) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkDenco_Param5(b *testing.B) { - router := loadDencoSingle("GET", fiveColon, dencoHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkEcho_Param5(b *testing.B) { - router := loadEchoSingle("GET", fiveColon, echoHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkGin_Param5(b *testing.B) { - router := loadGinSingle("GET", fiveColon, ginHandle) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkGocraftWeb_Param5(b *testing.B) { - router := loadGocraftWebSingle("GET", fiveColon, gocraftWebHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkGoji_Param5(b *testing.B) { - router := loadGojiSingle("GET", fiveColon, httpHandlerFunc) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkGoJsonRest_Param5(b *testing.B) { - handler := loadGoJsonRestSingle("GET", fiveColon, goJsonRestHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, handler, r) -} -func BenchmarkGoRestful_Param5(b *testing.B) { - router := loadGoRestfulSingle("GET", fiveBrace, goRestfulHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkGorillaMux_Param5(b *testing.B) { - router := loadGorillaMuxSingle("GET", fiveBrace, httpHandlerFunc) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkHttpRouter_Param5(b *testing.B) { - router := loadHttpRouterSingle("GET", fiveColon, httpRouterHandle) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkHttpTreeMux_Param5(b *testing.B) { - router := loadHttpTreeMuxSingle("GET", fiveColon, httpTreeMuxHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkKocha_Param5(b *testing.B) { - handler := new(kochaHandler) - router := loadKochaSingle( - "GET", fiveColon, - handler, http.HandlerFunc(handler.Get), - ) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkMacaron_Param5(b *testing.B) { - router := loadMacaronSingle("GET", fiveColon, macaronHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkMartini_Param5(b *testing.B) { - router := loadMartiniSingle("GET", fiveColon, martiniHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkPat_Param5(b *testing.B) { - router := loadPatSingle("GET", fiveColon, http.HandlerFunc(httpHandlerFunc)) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkPossum_Param5(b *testing.B) { - router := loadPossumSingle("GET", fiveColon, possumHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkR2router_Param5(b *testing.B) { - router := loadR2routerSingle("GET", fiveColon, r2routerHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} - -func BenchmarkRevel_Param5(b *testing.B) { - router := loadRevelSingle("GET", fiveColon, "RevelController.Handle") - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkRivet_Param5(b *testing.B) { - router := loadRivetSingle("GET", fiveColon, rivetHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkTango_Param5(b *testing.B) { - router := loadTangoSingle("GET", fiveColon, tangoHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkTigerTonic_Param5(b *testing.B) { - router := loadTigerTonicSingle("GET", fiveBrace, httpHandlerFunc) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkTraffic_Param5(b *testing.B) { - router := loadTrafficSingle("GET", fiveColon, trafficHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkVulcan_Param5(b *testing.B) { - router := loadVulcanSingle("GET", fiveColon, vulcanHandler) - - r, _ := http.NewRequest("GET", fiveRoute, nil) - benchRequest(b, router, r) -} - -// func BenchmarkZeus_Param5(b *testing.B) { -// router := loadZeusSingle("GET", fiveColon, http.HandlerFunc(httpHandlerFunc)) - -// r, _ := http.NewRequest("GET", fiveRoute, nil) -// benchRequest(b, router, r) -// } - -// Route with 20 Params (no write) -const twentyColon = "/:a/:b/:c/:d/:e/:f/:g/:h/:i/:j/:k/:l/:m/:n/:o/:p/:q/:r/:s/:t" -const twentyBrace = "/{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}/{i}/{j}/{k}/{l}/{m}/{n}/{o}/{p}/{q}/{r}/{s}/{t}" -const twentyRoute = "/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t" - -func BenchmarkAce_Param20(b *testing.B) { - router := loadAceSingle("GET", twentyColon, aceHandle) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkBear_Param20(b *testing.B) { - router := loadBearSingle("GET", twentyBrace, bearHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkBeego_Param20(b *testing.B) { - router := loadBeegoSingle("GET", twentyColon, beegoHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkBone_Param20(b *testing.B) { - router := loadBoneSingle("GET", twentyColon, http.HandlerFunc(httpHandlerFunc)) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkDenco_Param20(b *testing.B) { - router := loadDencoSingle("GET", twentyColon, dencoHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkEcho_Param20(b *testing.B) { - router := loadEchoSingle("GET", twentyColon, echoHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkGin_Param20(b *testing.B) { - router := loadGinSingle("GET", twentyColon, ginHandle) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkGocraftWeb_Param20(b *testing.B) { - router := loadGocraftWebSingle("GET", twentyColon, gocraftWebHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkGoji_Param20(b *testing.B) { - router := loadGojiSingle("GET", twentyColon, httpHandlerFunc) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkGoJsonRest_Param20(b *testing.B) { - handler := loadGoJsonRestSingle("GET", twentyColon, goJsonRestHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, handler, r) -} -func BenchmarkGoRestful_Param20(b *testing.B) { - handler := loadGoRestfulSingle("GET", twentyBrace, goRestfulHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, handler, r) -} -func BenchmarkGorillaMux_Param20(b *testing.B) { - router := loadGorillaMuxSingle("GET", twentyBrace, httpHandlerFunc) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkHttpRouter_Param20(b *testing.B) { - router := loadHttpRouterSingle("GET", twentyColon, httpRouterHandle) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkHttpTreeMux_Param20(b *testing.B) { - router := loadHttpTreeMuxSingle("GET", twentyColon, httpTreeMuxHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkKocha_Param20(b *testing.B) { - handler := new(kochaHandler) - router := loadKochaSingle( - "GET", twentyColon, - handler, http.HandlerFunc(handler.Get), - ) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkMacaron_Param20(b *testing.B) { - router := loadMacaronSingle("GET", twentyColon, macaronHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkMartini_Param20(b *testing.B) { - router := loadMartiniSingle("GET", twentyColon, martiniHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkPat_Param20(b *testing.B) { - router := loadPatSingle("GET", twentyColon, http.HandlerFunc(httpHandlerFunc)) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkPossum_Param20(b *testing.B) { - router := loadPossumSingle("GET", twentyColon, possumHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkR2router_Param20(b *testing.B) { - router := loadR2routerSingle("GET", twentyColon, r2routerHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} - -func BenchmarkRevel_Param20(b *testing.B) { - router := loadRevelSingle("GET", twentyColon, "RevelController.Handle") - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkRivet_Param20(b *testing.B) { - router := loadRivetSingle("GET", twentyColon, rivetHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkTango_Param20(b *testing.B) { - router := loadTangoSingle("GET", twentyColon, tangoHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkTigerTonic_Param20(b *testing.B) { - router := loadTigerTonicSingle("GET", twentyBrace, httpHandlerFunc) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkTraffic_Param20(b *testing.B) { - router := loadTrafficSingle("GET", twentyColon, trafficHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} -func BenchmarkVulcan_Param20(b *testing.B) { - router := loadVulcanSingle("GET", twentyColon, vulcanHandler) - - r, _ := http.NewRequest("GET", twentyRoute, nil) - benchRequest(b, router, r) -} - -// func BenchmarkZeus_Param20(b *testing.B) { -// router := loadZeusSingle("GET", twentyColon, http.HandlerFunc(httpHandlerFunc)) - -// r, _ := http.NewRequest("GET", twentyRoute, nil) -// benchRequest(b, router, r) -// } - -// Route with Param and write -func BenchmarkAce_ParamWrite(b *testing.B) { - router := loadAceSingle("GET", "/user/:name", aceHandleWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkBear_ParamWrite(b *testing.B) { - router := loadBearSingle("GET", "/user/{name}", bearHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkBeego_ParamWrite(b *testing.B) { - router := loadBeegoSingle("GET", "/user/:name", beegoHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkBone_ParamWrite(b *testing.B) { - router := loadBoneSingle("GET", "/user/:name", http.HandlerFunc(boneHandlerWrite)) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkDenco_ParamWrite(b *testing.B) { - router := loadDencoSingle("GET", "/user/:name", dencoHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkEcho_ParamWrite(b *testing.B) { - router := loadEchoSingle("GET", "/user/:name", echoHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGin_ParamWrite(b *testing.B) { - router := loadGinSingle("GET", "/user/:name", ginHandleWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGocraftWeb_ParamWrite(b *testing.B) { - router := loadGocraftWebSingle("GET", "/user/:name", gocraftWebHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGoji_ParamWrite(b *testing.B) { - router := loadGojiSingle("GET", "/user/:name", gojiFuncWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkGoJsonRest_ParamWrite(b *testing.B) { - handler := loadGoJsonRestSingle("GET", "/user/:name", goJsonRestHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, handler, r) -} -func BenchmarkGoRestful_ParamWrite(b *testing.B) { - handler := loadGoRestfulSingle("GET", "/user/{name}", goRestfulHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, handler, r) -} -func BenchmarkGorillaMux_ParamWrite(b *testing.B) { - router := loadGorillaMuxSingle("GET", "/user/{name}", gorillaHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkHttpRouter_ParamWrite(b *testing.B) { - router := loadHttpRouterSingle("GET", "/user/:name", httpRouterHandleWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkHttpTreeMux_ParamWrite(b *testing.B) { - router := loadHttpTreeMuxSingle("GET", "/user/:name", httpTreeMuxHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkKocha_ParamWrite(b *testing.B) { - handler := new(kochaHandler) - router := loadKochaSingle( - "GET", "/user/:name", - handler, http.HandlerFunc(handler.kochaHandlerWrite), - ) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkMacaron_ParamWrite(b *testing.B) { - router := loadMacaronSingle("GET", "/user/:name", macaronHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkMartini_ParamWrite(b *testing.B) { - router := loadMartiniSingle("GET", "/user/:name", martiniHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkPat_ParamWrite(b *testing.B) { - router := loadPatSingle("GET", "/user/:name", http.HandlerFunc(patHandlerWrite)) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkPossum_ParamWrite(b *testing.B) { - router := loadPossumSingle("GET", "/user/:name", possumHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkR2router_ParamWrite(b *testing.B) { - router := loadR2routerSingle("GET", "/user/:name", r2routerHandleWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} - -func BenchmarkRevel_ParamWrite(b *testing.B) { - router := loadRevelSingle("GET", "/user/:name", "RevelController.HandleWrite") - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkRivet_ParamWrite(b *testing.B) { - router := loadRivetSingle("GET", "/user/:name", rivetHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkTango_ParamWrite(b *testing.B) { - router := loadTangoSingle("GET", "/user/:name", tangoHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkTigerTonic_ParamWrite(b *testing.B) { - router := loadTigerTonicSingle( - "GET", "/user/{name}", - http.HandlerFunc(tigerTonicHandlerWrite), - ) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkTraffic_ParamWrite(b *testing.B) { - router := loadTrafficSingle("GET", "/user/:name", trafficHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} -func BenchmarkVulcan_ParamWrite(b *testing.B) { - router := loadVulcanSingle("GET", "/user/:name", vulcanHandlerWrite) - - r, _ := http.NewRequest("GET", "/user/gordon", nil) - benchRequest(b, router, r) -} - -// func BenchmarkZeus_ParamWrite(b *testing.B) { -// router := loadZeusSingle("GET", "/user/:name", zeusHandlerWrite) - -// r, _ := http.NewRequest("GET", "/user/gordon", nil) -// benchRequest(b, router, r) -// } diff --git a/driver/driver.go b/driver/driver.go new file mode 100644 index 00000000..ffe6c56e --- /dev/null +++ b/driver/driver.go @@ -0,0 +1,294 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package driver + +import ( + "fmt" + "log" + "net/http" + "os" + "runtime" + "strings" + "testing" + + "github.com/julienschmidt/go-http-routing-benchmark/driver/ui" +) + +// RunTests runs all registerd tests against all registerd packages. +func RunTests() []*Assertion { + pn, bn := len(packages), len(benchmarks) + + if pn == 0 { + fmt.Fprint(os.Stderr, "error: test: no packages registered\n") + os.Exit(1) + } + if bn == 0 { + fmt.Fprint(os.Stderr, "error: test: no benchmarks registered\n") + os.Exit(1) + } + + var runs int + var errs []*Assertion + + // Calculate the number of tests we have to run + for _, b := range benchmarks { + var rs, qs, ps int + + for _, t := range b.tests { + if t.Route != nil { + rs += len(b.routes) + } + if t.Request != nil { + qs += len(b.requests) + } + } + for _, p := range packages { + if p.Supports.isset(b.Type) { + ps++ + } + } + + runs += ((rs + qs) * ps) + (len(packages) - ps) + } + + rw := &Response{} + ui := ui.NewText(runs) + + defer func() { + if rerr := recover(); rerr != nil { + ui.Error() + stack := make([]byte, 10000) + runtime.Stack(stack, false) + fmt.Fprintf(os.Stderr, "\n\n%s\n\n%s", rerr, stack) + os.Exit(2) + } + }() + + for _, b := range benchmarks { + for _, p := range packages { + if !p.Supports.isset(b.Type) { + ui.Skip() + continue + } + + h := p.Router(b.Type, normalize(b.routes, p.Normalizer)) + + for _, t := range b.tests { + rt := t.Route + if rt != nil { + for _, r := range b.routes { + x := verify(rt(r)) + + if x != nil { + ui.Error() + x.pkg = p + x.test = t + x.bench = b + errs = append(errs, x) + } else { + ui.Success() + } + } + } + + qt := t.Request + if qt != nil { + for _, r := range b.requests { + rw.Reset() + h.ServeHTTP(rw, r) + x := verify(qt(r, rw)) + + if x != nil { + if x.Error == "" && x.Expect == x.Actual { + ui.Success() + continue + } + + ui.Error() + x.pkg = p + x.test = t + x.bench = b + errs = append(errs, x) + } else { + ui.Success() + } + } + } + } + } + } + + return errs +} + +// RunBenchmarks runs all registerd benchmarks against all registerd packages. +func RunBenchmarks() []*Result { + pn, bn := len(packages), len(benchmarks) + + if pn == 0 { + fmt.Fprint(os.Stderr, "error: benchmark: no packages registered\n") + os.Exit(1) + } + if bn == 0 { + fmt.Fprint(os.Stderr, "error: benchmark: no benchmarks registered\n") + os.Exit(1) + } + + var ( + results []*Result + memory runtime.MemStats + ) + + ui := ui.NewText(pn * bn) + + for _, b := range benchmarks { + r := &Result{ + benchmark: b, + } + + for _, p := range packages { + if !p.Supports.isset(b.Type) { + ui.Skip() + continue + } + + f := normalize(b.routes, p.Normalizer) + + runtime.GC() + runtime.ReadMemStats(&memory) + m := memory.HeapAlloc + + h := p.Router(b.Type, f) + + runtime.GC() + runtime.ReadMemStats(&memory) + m = memory.HeapAlloc - m + + x := testing.Benchmark(benchmarkify(h, b.requests)) + + r.pkgs = append(r.pkgs, p) + r.alloc = append(r.alloc, m) + r.results = append(r.results, &x) + + ui.Success() + } + + if r.pkgs != nil && r.alloc != nil && r.results != nil { + results = append(results, r) + } + + } + + return results +} + +// benchmarkify wraps a http.Handler in a benchmarkable closure. +func benchmarkify(h http.Handler, rs []*http.Request) func(*testing.B) { + rw := &ResponseStub{} + + if len(rs) > 1 { + r := rs[0] + + return func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + h.ServeHTTP(rw, r) + } + } + } + + return func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for _, r := range rs { + h.ServeHTTP(rw, r) + } + } + } +} + +var ncache = make(map[nkey]Fixtures) + +type nkey struct { + f *Fixtures + n *Normalizer +} + +// normalize's and caches normalized Fixtures. +func normalize(f Fixtures, n Normalizer) Fixtures { + if n == nil { + return f + } + + k := nkey{&f, &n} + x, ok := ncache[k] + + if ok { + return x + } + + o := n(f) + ncache[k] = o + + return o +} + +// verify checks that the Assertion is valid or nil. A Assertion is only valid +// if Error is set and Expect and Actual are empty or vice versa. See Assertion. +func verify(a *Assertion) *Assertion { + if a == nil { + return a + } + + var errors []string + + if a.Error != "" { + if a.Expect != "" { + panic("Expect must be empty if Error is set") + } + if a.Actual != "" { + errors = append(errors, "Actual must be empty if Error is set") + } + } else { + if a.Expect == "" { + errors = append(errors, "Expect must be set") + } + if a.Actual == "" { + errors = append(errors, "Actual must be set") + } + } + + if errors != nil { + panic(strings.Join(errors, ", ")) + } + + return a +} + +type ResponseStub struct{} + +func (r *ResponseStub) Header() http.Header { + return http.Header{} +} + +func (r *ResponseStub) Write(p []byte) (int, error) { + return len(p), nil +} + +func (r *ResponseStub) WriteString(p string) (int, error) { + return len(p), nil +} + +func (r *ResponseStub) WriteHeader(int) {} + +var LoggerStub *log.Logger + +func init() { + LoggerStub = log.New(&ResponseStub{}, "", 0) +} diff --git a/driver/extern.go b/driver/extern.go new file mode 100644 index 00000000..40cc94e6 --- /dev/null +++ b/driver/extern.go @@ -0,0 +1,481 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package driver + +import ( + "bytes" + "fmt" + "net/http" + "net/url" + "regexp" + "strings" + "testing" +) + +// Type defines a benchmark type +type Type uint + +// isset determines wether b is set. +func (t Type) isset(b Type) bool { + return t&b != 0 +} + +const ( + // Benchmark route with a static path. This benchmark must only contain + // static routes. + // + // Route: GET /test/example + // Request: GET /test/example + Static Type = 1 << 0 + + // Benchmark routes with one or more dynamic path segments. This benchmark + // may also contain static routes. + // + // Route: GET /test/:example + // Request: GET /test/benchmark + // Parameter: example=benchmark + Parameterized Type = 1 << 1 + + // Benchmark a route with multiple dynamic path segments where one parameter + // (dynamicparam, see ParamNameReadWrite) is read and written to /dev/null. + // This benchmark must not contain other types of benchmarks. + // + // Route: GET /test/:one/:two/:three/:dynamicparam + // Request: GET /test/one/two/three/benchmark + // Parameter: one=one, two=two, three=three, dynamicparam=benchmark + // Response: benchmark + ParameterReadWrite Type = 1 << 2 + + // ParameterReadWrite benchmark expects a parameter named dynamicparam. + ParamNameReadWrite = "dynamicparam" +) + +// Fixture is used to benchmark and test Router implementations. +type Fixture struct { + // Method used for routing and requesting the endpoint. + Method string + + // Path used for routing and requesting the endpoint. + Path string +} + +// Fixtures is a list of Fixture objects. +type Fixtures []*Fixture + +// Router initializes and returns a http.Handler used to route requests. +type Router func(Type, Fixtures) http.Handler + +// Normalizer normalizes parameterized routes before they get passed to the Router. +// Route normalization is importatnt for Routers that differ in the way they +// indicate a parameter (e.g. not prefixing it with a colon). +type Normalizer func(Fixtures) Fixtures + +var nre = regexp.MustCompile(":([^/]*)") + +// CurlyBracesNormalizer replaces colon (:param) parameters with curly braces +// (i.e. {param}). +func CurlyBracesNormalizer(f Fixtures) Fixtures { + s := make(Fixtures, len(f)) + + for i, r := range f { + s[i] = &Fixture{r.Method, nre.ReplaceAllString(r.Path, "{$1}")} + } + + return s +} + +// XThanSignNormalizer replaces colon (:param) parameters with greater- and +// less-than sign (i.e. ). +func XThanSignNormalizer(f Fixtures) Fixtures { + s := make(Fixtures, len(f)) + + for i, r := range f { + s[i] = &Fixture{r.Method, nre.ReplaceAllString(r.Path, "<$1>")} + } + + return s +} + +// RegExAllNormalizer replaces colon (:param) parameters with a regular expression +// which matches everything.except a slash +func RegExAllNormalizer(f Fixtures) Fixtures { + s := make(Fixtures, len(f)) + + for i, r := range f { + s[i] = &Fixture{r.Method, nre.ReplaceAllString(r.Path, "(?P<$1>[^/]*)")} + } + + return s +} + +// Benchmark describes a benchmark. A benchmark may only cover one benchmark type. +type Benchmark struct { + Type Type + Name string + Description string + tests []*Test + routes Fixtures + requests []*http.Request +} + +var benchmarks []*Benchmark + +// NewBenchmark registers and returns a new benchmark object. This function +// panics on error. +func NewBenchmark(t Type, name, desc string) *Benchmark { + var errors []string + + if t == Type(0) { + errors = append(errors, "Benchmark type cannot be zero") + } + if t&(t-Type(1)) != Type(0) { + errors = append(errors, "Benchmark cannot cover multiple types") + } + if name == "" { + errors = append(errors, "Benchmark name cannot be empty") + } + if desc == "" { + errors = append(errors, "Benchmark description cannot be empty") + } + + if errors != nil { + panic(strings.Join(errors, ", ")) + } + + b := &Benchmark{ + Type: t, + Name: name, + Description: desc, + } + benchmarks = append(benchmarks, b) + + return b +} + +// AddTest registers a new test case which will be run against a Router +// implementation. This function panics on error. +func (b *Benchmark) AddTest(t *Test) { + var errors []string + + if t.Description == "" { + errors = append(errors, "Test.Description cannot be empty") + } + if t.Route == nil && t.Request == nil { + errors = append(errors, "Test.Route or Test.Request must be set") + } + + if errors != nil { + panic(strings.Join(errors, ", ")) + } + + b.tests = append(b.tests, t) +} + +// AddRoute registers a new route which will be registered with a Router. +// This function panics on error. +func (b *Benchmark) AddRoute(method, path string) { + var errors []string + + if method == "" { + errors = append(errors, "Method cannot be empty") + } + if path == "" { + errors = append(errors, "Path cannot be empty") + } + + if errors != nil { + panic(strings.Join(errors, ", ")) + } + + b.routes = append(b.routes, &Fixture{method, path}) +} + +// AddRoutes registers routes based on Fixtures which will be registered with a Router. +// This function panics on error. +func (b *Benchmark) AddRoutes(fs Fixtures) { + var errors []string + + for i, f := range fs { + if f.Method == "" { + errors = append(errors, fmt.Sprintf("Fixture #%d: Method cannot be empty", i)) + } + if f.Path == "" { + errors = append(errors, fmt.Sprintf("Fixture #%d: Path cannot be empty", i)) + } + + b.routes = append(b.routes, f) + } + + if errors != nil { + panic(strings.Join(errors, ", ")) + } +} + +var request = strings.NewReplacer("/:", "/") + +// AddRoute registers a new route which will be registered with a Router. +// This function panics on error. +func (b *Benchmark) AddRequest(method, path string) { + var errors []string + r, err := http.NewRequest(method, request.Replace(path), nil) + + if err != nil { + errors = append(errors, err.Error()) + } + if method == "" { + errors = append(errors, "Method cannot be empty") + } + if path == "" { + errors = append(errors, "Path cannot be empty") + } + + if errors != nil { + panic(strings.Join(errors, ", ")) + } + + b.requests = append(b.requests, r) +} + +// AddRequests generates requests based on Fixtures. The paths will be striped +// of parameters (/:). // This function panics on error. +func (b *Benchmark) AddRequests(fs Fixtures) { + var errors []string + + for i, f := range fs { + r, err := http.NewRequest(f.Method, request.Replace(f.Path), nil) + + if err != nil { + errors = append(errors, err.Error()) + } + if f.Method == "" { + errors = append(errors, fmt.Sprintf("Fixture #%d: Method cannot be empty", i)) + } + if f.Path == "" { + errors = append(errors, fmt.Sprintf("Fixture #%d: Path cannot be empty", i)) + } + + b.requests = append(b.requests, r) + } + + if errors != nil { + panic(strings.Join(errors, ", ")) + } +} + +// Package describes a router or framework. Every field except for Normalizer +// is required. +type Package struct { + Name string // Name of the Router + Homepage string // Homepage or repository URL for the router/framework + Router Router // Router to initialize a http.Handler + Supports Type // Bitmask of supported benchmark Type's + Normalizer Normalizer // Normalizer to run before routes get passed to the Router +} + +var packages []*Package + +// Returns the number of registered packages. +func NumPackages() int { + return len(packages) +} + +// Returns the names of registered packages. +func PackageNames() []string { + r := make([]string, len(packages)) + for i, pkg := range packages { + r[i] = pkg.Name + } + + return r +} + +// RegisterPackage registers router/framework package. +func RegisterPackage(p *Package) { + var errors []string + + if p == nil { + errors = append(errors, "Package cannot be nil") + } else { + if p.Name == "" { + errors = append(errors, "Package.Name cannot be empty") + } + if p.Homepage == "" { + errors = append(errors, "Package.Homepage cannot be empty") + } else if _, err := url.Parse(p.Homepage); p != nil && err != nil { + errors = append(errors, err.Error()) + } + if p.Router == nil { + errors = append(errors, "Package.Router cannot be nil") + } + if p.Supports == Type(0) { + errors = append(errors, "Package.Supports cannot be zero") + } + } + + if errors != nil { + panic(strings.Join(errors, ", ")) + } + + packages = append(packages, p) +} + +// Test describes a test case used to test that benchmarks are configured correctly. +// A Test function may return a pointer to a Assertion object or nil, depending whether +// the test was successful or not. +type Test struct { + // Description for the test case. + Description string + + // Tests the raw, not normalized routes. + Route func(*Fixture) *Assertion + + // Tests the http.Request or Response object. + Request func(*http.Request, *Response) *Assertion +} + +// Assertion is used in test cases to indicate that an error occured or to compare +// the expected output is equal to the actual output. If the Error property is +// set, Actual and Expect must be empty and vice versa. The comparison of the expected +// and actual output is not part of the test and will be handled externaly. +type Assertion struct { + test *Test + pkg *Package + bench *Benchmark + Error string + Actual string + Expect string +} + +// Returns the description of the test that failed. +func (a *Assertion) Description() string { + if a.test == nil { + return "" + } + + return a.test.Description +} + +// Returns the router name of the test that failed. +func (a *Assertion) Router() string { + if a.pkg == nil { + return "" + } + + return a.pkg.Name +} + +// Returns the benchmark description of the test that failed. +func (a *Assertion) Benchmark() string { + if a.bench == nil { + return "" + } + + return a.bench.Name + ": " + a.bench.Description +} + +// Result is a benchmark result. +type Result struct { + benchmark *Benchmark + alloc []uint64 + pkgs []*Package + results []*testing.BenchmarkResult +} + +// Name returns the name of the benchmark. +func (r *Result) Name() string { + if r.benchmark == nil { + panic("benchmark is nil") + } + + return r.benchmark.Name +} + +// Description returns the description of the benchmark. +func (r *Result) Description() string { + if r.benchmark == nil { + panic("benchmark is nil") + } + + return r.benchmark.Description +} + +// Package returns all packages which were benchmarked. +func (r *Result) Packages() []*Package { + if len(r.pkgs) != len(r.results) && len(r.pkgs) != len(r.alloc) { + panic("Package, BenchmarkResult and HeapMemory mismatch") + } + + return r.pkgs +} + +// RouterMemory returns the heap memory used for the router object. +func (r *Result) RouterMemory() []uint64 { + if len(r.pkgs) != len(r.results) && len(r.pkgs) != len(r.alloc) { + panic("Package, BenchmarkResult and HeapMemory mismatch") + } + + return r.alloc +} + +// Result returns the benchmarks result of all benchmarked packages. +func (r *Result) Results() []*testing.BenchmarkResult { + if len(r.pkgs) != len(r.results) && len(r.pkgs) != len(r.alloc) { + panic("Package, BenchmarkResult and HeapMemory mismatch") + } + + return r.results +} + +// Response is used in during tests and captures the written response. +type Response struct { + flushed bool + status int + output bytes.Buffer + header http.Header +} + +// Reset's the captured status code and output. +func (r *Response) Reset() { + r.status = 0 + r.header = nil + r.flushed = false + r.output.Reset() +} + +// StatusCode returns the written status code. +func (r *Response) Status() int { + return r.status +} + +// Output returns the written output. +func (r *Response) Output() []byte { + return r.output.Bytes() +} + +// Header returns the header map that will be sent by WriteHeader. +func (r *Response) Header() http.Header { + if r.header == nil { + r.header = http.Header{} + } + + return r.header +} + +// Write's the data to the connection as part of an HTTP reply. +func (r *Response) Write(b []byte) (int, error) { + if !r.flushed { + r.WriteHeader(http.StatusOK) + } + + return r.output.Write(b) +} + +// WriteHeader sends an HTTP response header with status code. +func (r *Response) WriteHeader(c int) { + if !r.flushed { + r.status = c + r.flushed = true + } +} diff --git a/driver/ui/console.go b/driver/ui/console.go new file mode 100644 index 00000000..391ff3c3 --- /dev/null +++ b/driver/ui/console.go @@ -0,0 +1,114 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package ui + +import ( + "fmt" + "runtime" + "strings" +) + +var defaultConsole ConsoleAdapter + +// Console sets the default console and returns it. +func Console(style string) ConsoleAdapter { + var stylizer Colorizer + + switch style { + case "on": + stylizer = color{} + case "ansi": + stylizer = ansiColor{} + case "off": + stylizer = nullColor{} + default: + panic("Unknown color option") + } + + switch runtime.GOOS { + case "windows": + defaultConsole = windowsConsole{baseConsole{stylizer}} + default: + defaultConsole = posixConsole{baseConsole{stylizer}} + } + + return defaultConsole +} + +type ConsoleAdapter interface { + Reset() + Width() int + Height() int + Red(string) + Green(string) + Yellow(string) + Print(string) + Printf(string, ...interface{}) + Color() Colorizer +} + +type baseConsole struct { + style Colorizer +} + +func (c baseConsole) Width() int { return 80 } +func (c baseConsole) Height() int { return 25 } +func (c baseConsole) Color() Colorizer { return c.style } +func (c baseConsole) Reset() { fmt.Print("\f") } +func (c baseConsole) Red(b string) { fmt.Print(c.style.Red(b)) } +func (c baseConsole) Green(b string) { fmt.Print(c.style.Green(b)) } +func (c baseConsole) Yellow(b string) { fmt.Print(c.style.Yellow(b)) } +func (c baseConsole) Print(p string) { fmt.Print(p) } +func (c baseConsole) Printf(f string, args ...interface{}) { fmt.Printf(f, args...) } + +type posixConsole struct { + baseConsole +} + +type windowsConsole struct { + baseConsole +} + +func (c windowsConsole) Reset() { fmt.Print(strings.Repeat("\r\n", c.Height())) } + +type Colorizer interface { + Red(string) string + Green(string) string + Yellow(string) string +} + +type nullColor struct{} + +func (c nullColor) Red(b string) string { return b } +func (c nullColor) Green(b string) string { return b } +func (c nullColor) Yellow(b string) string { return b } + +type color struct{} + +func (c color) Red(b string) string { + return "\033[1;31m" + b + "\033[0m" +} + +func (c color) Green(b string) string { + return "\033[1;32m" + b + "\033[0m" +} + +func (c color) Yellow(b string) string { + return "\033[1;33m" + b + "\033[0m" +} + +type ansiColor struct{} + +func (c ansiColor) Red(b string) string { + return "\x1b[1;31m" + b + "\x1b[0m" +} + +func (c ansiColor) Green(b string) string { + return "\x1b[1;32m" + b + "\x1b[0m" +} + +func (c ansiColor) Yellow(b string) string { + return "\x1b[1;33m" + b + "\x1b[0m" +} diff --git a/driver/ui/text.go b/driver/ui/text.go new file mode 100644 index 00000000..f002bcaa --- /dev/null +++ b/driver/ui/text.go @@ -0,0 +1,106 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package ui + +import ( + "bytes" + "strconv" + "strings" + "time" +) + +// Text describes a text runner user interface. +type Text struct { + errors int + skipped int + + max int // Maximum steps to take + step int // Steps already taken + colum int // Right colum width + width int // Current width without the colum + digits int // Digits of max steps + + start time.Time + line bytes.Buffer + console ConsoleAdapter +} + +// NewText returns a new text user interface which can be called steps times. +// After steps calls it prints stats. This function captures the default colorizer +// at instantiation. +func NewText(steps int) *Text { + d := len(strconv.Itoa(steps)) + + return &Text{ + digits: d, + max: steps, + start: time.Now(), + console: defaultConsole, + colum: 6 + d + 1 + d, // " 000% " d "/" d, + } +} + +// Success marks the current step as successful. +func (t *Text) Success() { + t.write(".") +} + +// Error marks the current step as failed. +func (t *Text) Error() { + t.errors++ + t.write(t.console.Color().Red("E")) +} + +// Skip marks the current step as skipped. +func (t *Text) Skip() { + t.skipped++ + t.write(t.console.Color().Yellow("S")) +} + +func (t *Text) write(b string) { + t.step++ + t.width++ + t.line.WriteString(b) + + pd := strings.Repeat(" ", t.console.Width()-(t.width+t.colum)) + + if t.max < t.step { + panic("Maximum steps already reached") + } + + pc := int(100 * float64(t.step) / float64(t.max)) + t.console.Printf("\r%s%s %3d%% %*d/%*d", t.line.String(), pd, pc, t.digits, t.step, t.digits, t.max) + + if (t.width + t.colum) >= t.console.Width() { + t.width = 0 + t.line.Reset() + t.console.Print("\n") + } + + if t.max == t.step { + t.console.Print("\n\n") + + t.console.Printf("Total: %d", t.max) + if t.errors > 0 { + t.console.Printf(", %d failed", t.errors) + } + if t.skipped > 0 { + t.console.Printf(", %d skipped", t.skipped) + } + + d := time.Since(t.start) + h := int(d.Hours()) + m := int(d.Minutes()) % 60 + s := int(d.Seconds()) % 60 + + t.console.Print("; Time: ") + if h > 0 { + t.console.Printf("%d:", h) + } + t.console.Printf("%02d:%02d", m, s) + + t.console.Print("\n\n\n") + } +} diff --git a/github_test.go b/github_test.go deleted file mode 100644 index c744447d..00000000 --- a/github_test.go +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package main - -import ( - "net/http" - "testing" -) - -// http://developer.github.com/v3/ -var githubAPI = []route{ - // OAuth Authorizations - {"GET", "/authorizations"}, - {"GET", "/authorizations/:id"}, - {"POST", "/authorizations"}, - //{"PUT", "/authorizations/clients/:client_id"}, - //{"PATCH", "/authorizations/:id"}, - {"DELETE", "/authorizations/:id"}, - {"GET", "/applications/:client_id/tokens/:access_token"}, - {"DELETE", "/applications/:client_id/tokens"}, - {"DELETE", "/applications/:client_id/tokens/:access_token"}, - - // Activity - {"GET", "/events"}, - {"GET", "/repos/:owner/:repo/events"}, - {"GET", "/networks/:owner/:repo/events"}, - {"GET", "/orgs/:org/events"}, - {"GET", "/users/:user/received_events"}, - {"GET", "/users/:user/received_events/public"}, - {"GET", "/users/:user/events"}, - {"GET", "/users/:user/events/public"}, - {"GET", "/users/:user/events/orgs/:org"}, - {"GET", "/feeds"}, - {"GET", "/notifications"}, - {"GET", "/repos/:owner/:repo/notifications"}, - {"PUT", "/notifications"}, - {"PUT", "/repos/:owner/:repo/notifications"}, - {"GET", "/notifications/threads/:id"}, - //{"PATCH", "/notifications/threads/:id"}, - {"GET", "/notifications/threads/:id/subscription"}, - {"PUT", "/notifications/threads/:id/subscription"}, - {"DELETE", "/notifications/threads/:id/subscription"}, - {"GET", "/repos/:owner/:repo/stargazers"}, - {"GET", "/users/:user/starred"}, - {"GET", "/user/starred"}, - {"GET", "/user/starred/:owner/:repo"}, - {"PUT", "/user/starred/:owner/:repo"}, - {"DELETE", "/user/starred/:owner/:repo"}, - {"GET", "/repos/:owner/:repo/subscribers"}, - {"GET", "/users/:user/subscriptions"}, - {"GET", "/user/subscriptions"}, - {"GET", "/repos/:owner/:repo/subscription"}, - {"PUT", "/repos/:owner/:repo/subscription"}, - {"DELETE", "/repos/:owner/:repo/subscription"}, - {"GET", "/user/subscriptions/:owner/:repo"}, - {"PUT", "/user/subscriptions/:owner/:repo"}, - {"DELETE", "/user/subscriptions/:owner/:repo"}, - - // Gists - {"GET", "/users/:user/gists"}, - {"GET", "/gists"}, - //{"GET", "/gists/public"}, - //{"GET", "/gists/starred"}, - {"GET", "/gists/:id"}, - {"POST", "/gists"}, - //{"PATCH", "/gists/:id"}, - {"PUT", "/gists/:id/star"}, - {"DELETE", "/gists/:id/star"}, - {"GET", "/gists/:id/star"}, - {"POST", "/gists/:id/forks"}, - {"DELETE", "/gists/:id"}, - - // Git Data - {"GET", "/repos/:owner/:repo/git/blobs/:sha"}, - {"POST", "/repos/:owner/:repo/git/blobs"}, - {"GET", "/repos/:owner/:repo/git/commits/:sha"}, - {"POST", "/repos/:owner/:repo/git/commits"}, - //{"GET", "/repos/:owner/:repo/git/refs/*ref"}, - {"GET", "/repos/:owner/:repo/git/refs"}, - {"POST", "/repos/:owner/:repo/git/refs"}, - //{"PATCH", "/repos/:owner/:repo/git/refs/*ref"}, - //{"DELETE", "/repos/:owner/:repo/git/refs/*ref"}, - {"GET", "/repos/:owner/:repo/git/tags/:sha"}, - {"POST", "/repos/:owner/:repo/git/tags"}, - {"GET", "/repos/:owner/:repo/git/trees/:sha"}, - {"POST", "/repos/:owner/:repo/git/trees"}, - - // Issues - {"GET", "/issues"}, - {"GET", "/user/issues"}, - {"GET", "/orgs/:org/issues"}, - {"GET", "/repos/:owner/:repo/issues"}, - {"GET", "/repos/:owner/:repo/issues/:number"}, - {"POST", "/repos/:owner/:repo/issues"}, - //{"PATCH", "/repos/:owner/:repo/issues/:number"}, - {"GET", "/repos/:owner/:repo/assignees"}, - {"GET", "/repos/:owner/:repo/assignees/:assignee"}, - {"GET", "/repos/:owner/:repo/issues/:number/comments"}, - //{"GET", "/repos/:owner/:repo/issues/comments"}, - //{"GET", "/repos/:owner/:repo/issues/comments/:id"}, - {"POST", "/repos/:owner/:repo/issues/:number/comments"}, - //{"PATCH", "/repos/:owner/:repo/issues/comments/:id"}, - //{"DELETE", "/repos/:owner/:repo/issues/comments/:id"}, - {"GET", "/repos/:owner/:repo/issues/:number/events"}, - //{"GET", "/repos/:owner/:repo/issues/events"}, - //{"GET", "/repos/:owner/:repo/issues/events/:id"}, - {"GET", "/repos/:owner/:repo/labels"}, - {"GET", "/repos/:owner/:repo/labels/:name"}, - {"POST", "/repos/:owner/:repo/labels"}, - //{"PATCH", "/repos/:owner/:repo/labels/:name"}, - {"DELETE", "/repos/:owner/:repo/labels/:name"}, - {"GET", "/repos/:owner/:repo/issues/:number/labels"}, - {"POST", "/repos/:owner/:repo/issues/:number/labels"}, - {"DELETE", "/repos/:owner/:repo/issues/:number/labels/:name"}, - {"PUT", "/repos/:owner/:repo/issues/:number/labels"}, - {"DELETE", "/repos/:owner/:repo/issues/:number/labels"}, - {"GET", "/repos/:owner/:repo/milestones/:number/labels"}, - {"GET", "/repos/:owner/:repo/milestones"}, - {"GET", "/repos/:owner/:repo/milestones/:number"}, - {"POST", "/repos/:owner/:repo/milestones"}, - //{"PATCH", "/repos/:owner/:repo/milestones/:number"}, - {"DELETE", "/repos/:owner/:repo/milestones/:number"}, - - // Miscellaneous - {"GET", "/emojis"}, - {"GET", "/gitignore/templates"}, - {"GET", "/gitignore/templates/:name"}, - {"POST", "/markdown"}, - {"POST", "/markdown/raw"}, - {"GET", "/meta"}, - {"GET", "/rate_limit"}, - - // Organizations - {"GET", "/users/:user/orgs"}, - {"GET", "/user/orgs"}, - {"GET", "/orgs/:org"}, - //{"PATCH", "/orgs/:org"}, - {"GET", "/orgs/:org/members"}, - {"GET", "/orgs/:org/members/:user"}, - {"DELETE", "/orgs/:org/members/:user"}, - {"GET", "/orgs/:org/public_members"}, - {"GET", "/orgs/:org/public_members/:user"}, - {"PUT", "/orgs/:org/public_members/:user"}, - {"DELETE", "/orgs/:org/public_members/:user"}, - {"GET", "/orgs/:org/teams"}, - {"GET", "/teams/:id"}, - {"POST", "/orgs/:org/teams"}, - //{"PATCH", "/teams/:id"}, - {"DELETE", "/teams/:id"}, - {"GET", "/teams/:id/members"}, - {"GET", "/teams/:id/members/:user"}, - {"PUT", "/teams/:id/members/:user"}, - {"DELETE", "/teams/:id/members/:user"}, - {"GET", "/teams/:id/repos"}, - {"GET", "/teams/:id/repos/:owner/:repo"}, - {"PUT", "/teams/:id/repos/:owner/:repo"}, - {"DELETE", "/teams/:id/repos/:owner/:repo"}, - {"GET", "/user/teams"}, - - // Pull Requests - {"GET", "/repos/:owner/:repo/pulls"}, - {"GET", "/repos/:owner/:repo/pulls/:number"}, - {"POST", "/repos/:owner/:repo/pulls"}, - //{"PATCH", "/repos/:owner/:repo/pulls/:number"}, - {"GET", "/repos/:owner/:repo/pulls/:number/commits"}, - {"GET", "/repos/:owner/:repo/pulls/:number/files"}, - {"GET", "/repos/:owner/:repo/pulls/:number/merge"}, - {"PUT", "/repos/:owner/:repo/pulls/:number/merge"}, - {"GET", "/repos/:owner/:repo/pulls/:number/comments"}, - //{"GET", "/repos/:owner/:repo/pulls/comments"}, - //{"GET", "/repos/:owner/:repo/pulls/comments/:number"}, - {"PUT", "/repos/:owner/:repo/pulls/:number/comments"}, - //{"PATCH", "/repos/:owner/:repo/pulls/comments/:number"}, - //{"DELETE", "/repos/:owner/:repo/pulls/comments/:number"}, - - // Repositories - {"GET", "/user/repos"}, - {"GET", "/users/:user/repos"}, - {"GET", "/orgs/:org/repos"}, - {"GET", "/repositories"}, - {"POST", "/user/repos"}, - {"POST", "/orgs/:org/repos"}, - {"GET", "/repos/:owner/:repo"}, - //{"PATCH", "/repos/:owner/:repo"}, - {"GET", "/repos/:owner/:repo/contributors"}, - {"GET", "/repos/:owner/:repo/languages"}, - {"GET", "/repos/:owner/:repo/teams"}, - {"GET", "/repos/:owner/:repo/tags"}, - {"GET", "/repos/:owner/:repo/branches"}, - {"GET", "/repos/:owner/:repo/branches/:branch"}, - {"DELETE", "/repos/:owner/:repo"}, - {"GET", "/repos/:owner/:repo/collaborators"}, - {"GET", "/repos/:owner/:repo/collaborators/:user"}, - {"PUT", "/repos/:owner/:repo/collaborators/:user"}, - {"DELETE", "/repos/:owner/:repo/collaborators/:user"}, - {"GET", "/repos/:owner/:repo/comments"}, - {"GET", "/repos/:owner/:repo/commits/:sha/comments"}, - {"POST", "/repos/:owner/:repo/commits/:sha/comments"}, - {"GET", "/repos/:owner/:repo/comments/:id"}, - //{"PATCH", "/repos/:owner/:repo/comments/:id"}, - {"DELETE", "/repos/:owner/:repo/comments/:id"}, - {"GET", "/repos/:owner/:repo/commits"}, - {"GET", "/repos/:owner/:repo/commits/:sha"}, - {"GET", "/repos/:owner/:repo/readme"}, - //{"GET", "/repos/:owner/:repo/contents/*path"}, - //{"PUT", "/repos/:owner/:repo/contents/*path"}, - //{"DELETE", "/repos/:owner/:repo/contents/*path"}, - //{"GET", "/repos/:owner/:repo/:archive_format/:ref"}, - {"GET", "/repos/:owner/:repo/keys"}, - {"GET", "/repos/:owner/:repo/keys/:id"}, - {"POST", "/repos/:owner/:repo/keys"}, - //{"PATCH", "/repos/:owner/:repo/keys/:id"}, - {"DELETE", "/repos/:owner/:repo/keys/:id"}, - {"GET", "/repos/:owner/:repo/downloads"}, - {"GET", "/repos/:owner/:repo/downloads/:id"}, - {"DELETE", "/repos/:owner/:repo/downloads/:id"}, - {"GET", "/repos/:owner/:repo/forks"}, - {"POST", "/repos/:owner/:repo/forks"}, - {"GET", "/repos/:owner/:repo/hooks"}, - {"GET", "/repos/:owner/:repo/hooks/:id"}, - {"POST", "/repos/:owner/:repo/hooks"}, - //{"PATCH", "/repos/:owner/:repo/hooks/:id"}, - {"POST", "/repos/:owner/:repo/hooks/:id/tests"}, - {"DELETE", "/repos/:owner/:repo/hooks/:id"}, - {"POST", "/repos/:owner/:repo/merges"}, - {"GET", "/repos/:owner/:repo/releases"}, - {"GET", "/repos/:owner/:repo/releases/:id"}, - {"POST", "/repos/:owner/:repo/releases"}, - //{"PATCH", "/repos/:owner/:repo/releases/:id"}, - {"DELETE", "/repos/:owner/:repo/releases/:id"}, - {"GET", "/repos/:owner/:repo/releases/:id/assets"}, - {"GET", "/repos/:owner/:repo/stats/contributors"}, - {"GET", "/repos/:owner/:repo/stats/commit_activity"}, - {"GET", "/repos/:owner/:repo/stats/code_frequency"}, - {"GET", "/repos/:owner/:repo/stats/participation"}, - {"GET", "/repos/:owner/:repo/stats/punch_card"}, - {"GET", "/repos/:owner/:repo/statuses/:ref"}, - {"POST", "/repos/:owner/:repo/statuses/:ref"}, - - // Search - {"GET", "/search/repositories"}, - {"GET", "/search/code"}, - {"GET", "/search/issues"}, - {"GET", "/search/users"}, - {"GET", "/legacy/issues/search/:owner/:repository/:state/:keyword"}, - {"GET", "/legacy/repos/search/:keyword"}, - {"GET", "/legacy/user/search/:keyword"}, - {"GET", "/legacy/user/email/:email"}, - - // Users - {"GET", "/users/:user"}, - {"GET", "/user"}, - //{"PATCH", "/user"}, - {"GET", "/users"}, - {"GET", "/user/emails"}, - {"POST", "/user/emails"}, - {"DELETE", "/user/emails"}, - {"GET", "/users/:user/followers"}, - {"GET", "/user/followers"}, - {"GET", "/users/:user/following"}, - {"GET", "/user/following"}, - {"GET", "/user/following/:user"}, - {"GET", "/users/:user/following/:target_user"}, - {"PUT", "/user/following/:user"}, - {"DELETE", "/user/following/:user"}, - {"GET", "/users/:user/keys"}, - {"GET", "/user/keys"}, - {"GET", "/user/keys/:id"}, - {"POST", "/user/keys"}, - //{"PATCH", "/user/keys/:id"}, - {"DELETE", "/user/keys/:id"}, -} - -var ( - githubAce http.Handler - githubBear http.Handler - githubBeego http.Handler - githubBone http.Handler - githubDenco http.Handler - githubEcho http.Handler - githubGin http.Handler - githubGocraftWeb http.Handler - githubGoji http.Handler - githubGoJsonRest http.Handler - githubGoRestful http.Handler - githubGorillaMux http.Handler - githubHttpRouter http.Handler - githubHttpTreeMux http.Handler - githubKocha http.Handler - githubMacaron http.Handler - githubMartini http.Handler - githubPat http.Handler - githubPossum http.Handler - githubR2router http.Handler - githubRevel http.Handler - githubRivet http.Handler - githubTango http.Handler - githubTigerTonic http.Handler - githubTraffic http.Handler - githubVulcan http.Handler - // githubZeus http.Handler -) - -func init() { - println("#GithubAPI Routes:", len(githubAPI)) - - calcMem("Ace", func() { - githubAce = loadAce(githubAPI) - }) - calcMem("Bear", func() { - githubBear = loadBear(githubAPI) - }) - calcMem("Beego", func() { - githubBeego = loadBeego(githubAPI) - }) - calcMem("Bone", func() { - githubBone = loadBone(githubAPI) - }) - calcMem("Denco", func() { - githubDenco = loadDenco(githubAPI) - }) - calcMem("Echo", func() { - githubEcho = loadEcho(githubAPI) - }) - calcMem("Gin", func() { - githubGin = loadGin(githubAPI) - }) - calcMem("GocraftWeb", func() { - githubGocraftWeb = loadGocraftWeb(githubAPI) - }) - calcMem("Goji", func() { - githubGoji = loadGoji(githubAPI) - }) - calcMem("GoJsonRest", func() { - githubGoJsonRest = loadGoJsonRest(githubAPI) - }) - calcMem("GoRestful", func() { - githubGoRestful = loadGoRestful(githubAPI) - }) - calcMem("GorillaMux", func() { - githubGorillaMux = loadGorillaMux(githubAPI) - }) - calcMem("HttpRouter", func() { - githubHttpRouter = loadHttpRouter(githubAPI) - }) - calcMem("HttpTreeMux", func() { - githubHttpTreeMux = loadHttpTreeMux(githubAPI) - }) - calcMem("Kocha", func() { - githubKocha = loadKocha(githubAPI) - }) - calcMem("Macaron", func() { - githubMacaron = loadMacaron(githubAPI) - }) - calcMem("Martini", func() { - githubMartini = loadMartini(githubAPI) - }) - calcMem("Pat", func() { - githubPat = loadPat(githubAPI) - }) - calcMem("Possum", func() { - githubPossum = loadPossum(githubAPI) - }) - calcMem("R2router", func() { - githubR2router = loadR2router(githubAPI) - }) - calcMem("Revel", func() { - githubRevel = loadRevel(githubAPI) - }) - calcMem("Rivet", func() { - githubRivet = loadRivet(githubAPI) - }) - calcMem("Tango", func() { - githubTango = loadTango(githubAPI) - }) - calcMem("TigerTonic", func() { - githubTigerTonic = loadTigerTonic(githubAPI) - }) - calcMem("Traffic", func() { - githubTraffic = loadTraffic(githubAPI) - }) - calcMem("Vulcan", func() { - githubVulcan = loadVulcan(githubAPI) - }) - // calcMem("Zeus", func() { - // githubZeus = loadZeus(githubAPI) - // }) - - println() -} - -// Static -func BenchmarkAce_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubAce, req) -} -func BenchmarkBear_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubBear, req) -} -func BenchmarkBeego_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubBeego, req) -} -func BenchmarkBone_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubBone, req) -} -func BenchmarkDenco_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubDenco, req) -} -func BenchmarkEcho_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubEcho, req) -} -func BenchmarkGin_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubGin, req) -} -func BenchmarkGocraftWeb_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubGocraftWeb, req) -} -func BenchmarkGoji_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubGoji, req) -} -func BenchmarkGoRestful_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubGoRestful, req) -} -func BenchmarkGoJsonRest_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubGoJsonRest, req) -} -func BenchmarkGorillaMux_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubGorillaMux, req) -} -func BenchmarkHttpRouter_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubHttpRouter, req) -} -func BenchmarkHttpTreeMux_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubHttpTreeMux, req) -} -func BenchmarkKocha_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubKocha, req) -} -func BenchmarkMacaron_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubMacaron, req) -} -func BenchmarkMartini_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubMartini, req) -} -func BenchmarkPat_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubPat, req) -} -func BenchmarkPossum_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubPossum, req) -} -func BenchmarkR2router_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubR2router, req) -} -func BenchmarkRevel_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubRevel, req) -} -func BenchmarkRivet_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubRivet, req) -} -func BenchmarkTango_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubTango, req) -} -func BenchmarkTigerTonic_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubTigerTonic, req) -} -func BenchmarkTraffic_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubTraffic, req) -} -func BenchmarkVulcan_GithubStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/user/repos", nil) - benchRequest(b, githubVulcan, req) -} - -// func BenchmarkZeus_GithubStatic(b *testing.B) { -// req, _ := http.NewRequest("GET", "/user/repos", nil) -// benchRequest(b, githubZeus, req) -// } - -// Param -func BenchmarkAce_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubAce, req) -} -func BenchmarkBear_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubBear, req) -} -func BenchmarkBeego_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubBeego, req) -} -func BenchmarkBone_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubBone, req) -} -func BenchmarkDenco_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubDenco, req) -} -func BenchmarkEcho_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubEcho, req) -} -func BenchmarkGin_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubGin, req) -} -func BenchmarkGocraftWeb_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubGocraftWeb, req) -} -func BenchmarkGoji_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubGoji, req) -} -func BenchmarkGoJsonRest_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubGoJsonRest, req) -} -func BenchmarkGoRestful_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubGoRestful, req) -} -func BenchmarkGorillaMux_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubGorillaMux, req) -} -func BenchmarkHttpRouter_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubHttpRouter, req) -} -func BenchmarkHttpTreeMux_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubHttpTreeMux, req) -} -func BenchmarkKocha_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubKocha, req) -} -func BenchmarkMacaron_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubMacaron, req) -} -func BenchmarkMartini_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubMartini, req) -} -func BenchmarkPat_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubPat, req) -} -func BenchmarkPossum_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubPossum, req) -} -func BenchmarkR2router_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubR2router, req) -} -func BenchmarkRevel_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubRevel, req) -} -func BenchmarkRivet_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubRivet, req) -} -func BenchmarkTango_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubTango, req) -} -func BenchmarkTigerTonic_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubTigerTonic, req) -} -func BenchmarkTraffic_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubTraffic, req) -} -func BenchmarkVulcan_GithubParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) - benchRequest(b, githubVulcan, req) -} - -// func BenchmarkZeus_GithubParam(b *testing.B) { -// req, _ := http.NewRequest("GET", "/repos/julienschmidt/httprouter/stargazers", nil) -// benchRequest(b, githubZeus, req) -// } - -// All routes -func BenchmarkAce_GithubAll(b *testing.B) { - benchRoutes(b, githubAce, githubAPI) -} -func BenchmarkBear_GithubAll(b *testing.B) { - benchRoutes(b, githubBear, githubAPI) -} -func BenchmarkBeego_GithubAll(b *testing.B) { - benchRoutes(b, githubBeego, githubAPI) -} -func BenchmarkBone_GithubAll(b *testing.B) { - benchRoutes(b, githubBone, githubAPI) -} -func BenchmarkDenco_GithubAll(b *testing.B) { - benchRoutes(b, githubDenco, githubAPI) -} -func BenchmarkEcho_GithubAll(b *testing.B) { - benchRoutes(b, githubEcho, githubAPI) -} -func BenchmarkGin_GithubAll(b *testing.B) { - benchRoutes(b, githubGin, githubAPI) -} -func BenchmarkGocraftWeb_GithubAll(b *testing.B) { - benchRoutes(b, githubGocraftWeb, githubAPI) -} -func BenchmarkGoji_GithubAll(b *testing.B) { - benchRoutes(b, githubGoji, githubAPI) -} -func BenchmarkGoJsonRest_GithubAll(b *testing.B) { - benchRoutes(b, githubGoJsonRest, githubAPI) -} -func BenchmarkGoRestful_GithubAll(b *testing.B) { - benchRoutes(b, githubGoRestful, githubAPI) -} -func BenchmarkGorillaMux_GithubAll(b *testing.B) { - benchRoutes(b, githubGorillaMux, githubAPI) -} -func BenchmarkHttpRouter_GithubAll(b *testing.B) { - benchRoutes(b, githubHttpRouter, githubAPI) -} -func BenchmarkHttpTreeMux_GithubAll(b *testing.B) { - benchRoutes(b, githubHttpTreeMux, githubAPI) -} -func BenchmarkKocha_GithubAll(b *testing.B) { - benchRoutes(b, githubKocha, githubAPI) -} -func BenchmarkMacaron_GithubAll(b *testing.B) { - benchRoutes(b, githubMacaron, githubAPI) -} -func BenchmarkMartini_GithubAll(b *testing.B) { - benchRoutes(b, githubMartini, githubAPI) -} -func BenchmarkPat_GithubAll(b *testing.B) { - benchRoutes(b, githubPat, githubAPI) -} -func BenchmarkPossum_GithubAll(b *testing.B) { - benchRoutes(b, githubPossum, githubAPI) -} -func BenchmarkR2router_GithubAll(b *testing.B) { - benchRoutes(b, githubR2router, githubAPI) -} -func BenchmarkRevel_GithubAll(b *testing.B) { - benchRoutes(b, githubRevel, githubAPI) -} -func BenchmarkRivet_GithubAll(b *testing.B) { - benchRoutes(b, githubRivet, githubAPI) -} -func BenchmarkTango_GithubAll(b *testing.B) { - benchRoutes(b, githubTango, githubAPI) -} -func BenchmarkTigerTonic_GithubAll(b *testing.B) { - benchRoutes(b, githubTigerTonic, githubAPI) -} -func BenchmarkTraffic_GithubAll(b *testing.B) { - benchRoutes(b, githubTraffic, githubAPI) -} -func BenchmarkVulcan_GithubAll(b *testing.B) { - benchRoutes(b, githubVulcan, githubAPI) -} - -// func BenchmarkZeus_GithubAll(b *testing.B) { -// benchRoutes(b, githubZeus, githubAPI) -// } diff --git a/gplus_test.go b/gplus_test.go deleted file mode 100644 index af5da63d..00000000 --- a/gplus_test.go +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package main - -import ( - "net/http" - "testing" -) - -// Google+ -// https://developers.google.com/+/api/latest/ -// (in reality this is just a subset of a much larger API) -var gplusAPI = []route{ - // People - {"GET", "/people/:userId"}, - {"GET", "/people"}, - {"GET", "/activities/:activityId/people/:collection"}, - {"GET", "/people/:userId/people/:collection"}, - {"GET", "/people/:userId/openIdConnect"}, - - // Activities - {"GET", "/people/:userId/activities/:collection"}, - {"GET", "/activities/:activityId"}, - {"GET", "/activities"}, - - // Comments - {"GET", "/activities/:activityId/comments"}, - {"GET", "/comments/:commentId"}, - - // Moments - {"POST", "/people/:userId/moments/:collection"}, - {"GET", "/people/:userId/moments/:collection"}, - {"DELETE", "/moments/:id"}, -} - -var ( - gplusAce http.Handler - gplusBear http.Handler - gplusBeego http.Handler - gplusBone http.Handler - gplusDenco http.Handler - gplusEcho http.Handler - gplusGin http.Handler - gplusGocraftWeb http.Handler - gplusGoji http.Handler - gplusGoJsonRest http.Handler - gplusGoRestful http.Handler - gplusGorillaMux http.Handler - gplusHttpRouter http.Handler - gplusHttpTreeMux http.Handler - gplusKocha http.Handler - gplusMacaron http.Handler - gplusMartini http.Handler - gplusPat http.Handler - gplusPossum http.Handler - gplusR2router http.Handler - gplusRevel http.Handler - gplusRivet http.Handler - gplusTango http.Handler - gplusTigerTonic http.Handler - gplusTraffic http.Handler - gplusVulcan http.Handler - // gplusZeus http.Handler -) - -func init() { - println("#GPlusAPI Routes:", len(gplusAPI)) - - calcMem("Ace", func() { - gplusAce = loadAce(gplusAPI) - }) - calcMem("Bear", func() { - gplusBear = loadBear(gplusAPI) - }) - calcMem("Beego", func() { - gplusBeego = loadBeego(gplusAPI) - }) - calcMem("Bone", func() { - gplusBone = loadBone(gplusAPI) - }) - calcMem("Denco", func() { - gplusDenco = loadDenco(gplusAPI) - }) - calcMem("Echo", func() { - gplusEcho = loadEcho(gplusAPI) - }) - calcMem("Gin", func() { - gplusGin = loadGin(gplusAPI) - }) - calcMem("GocraftWeb", func() { - gplusGocraftWeb = loadGocraftWeb(gplusAPI) - }) - calcMem("Goji", func() { - gplusGoji = loadGoji(gplusAPI) - }) - calcMem("GoJsonRest", func() { - gplusGoJsonRest = loadGoJsonRest(gplusAPI) - }) - calcMem("GoRestful", func() { - gplusGoRestful = loadGoRestful(gplusAPI) - }) - calcMem("GorillaMux", func() { - gplusGorillaMux = loadGorillaMux(gplusAPI) - }) - calcMem("HttpRouter", func() { - gplusHttpRouter = loadHttpRouter(gplusAPI) - }) - calcMem("HttpTreeMux", func() { - gplusHttpTreeMux = loadHttpTreeMux(gplusAPI) - }) - calcMem("Kocha", func() { - gplusKocha = loadKocha(gplusAPI) - }) - calcMem("Macaron", func() { - gplusMacaron = loadMacaron(gplusAPI) - }) - calcMem("Martini", func() { - gplusMartini = loadMartini(gplusAPI) - }) - calcMem("Pat", func() { - gplusPat = loadPat(gplusAPI) - }) - calcMem("Possum", func() { - gplusPossum = loadPossum(gplusAPI) - }) - calcMem("R2router", func() { - gplusR2router = loadR2router(gplusAPI) - }) - calcMem("Revel", func() { - gplusRevel = loadRevel(gplusAPI) - }) - calcMem("Rivet", func() { - gplusRivet = loadRivet(gplusAPI) - }) - calcMem("Tango", func() { - gplusTango = loadTango(gplusAPI) - }) - calcMem("TigerTonic", func() { - gplusTigerTonic = loadTigerTonic(gplusAPI) - }) - calcMem("Traffic", func() { - gplusTraffic = loadTraffic(gplusAPI) - }) - calcMem("Vulcan", func() { - gplusVulcan = loadVulcan(gplusAPI) - }) - // calcMem("Zeus", func() { - // gplusZeus = loadZeus(gplusAPI) - // }) - - println() -} - -// Static -func BenchmarkAce_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusAce, req) -} -func BenchmarkBear_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusBear, req) -} -func BenchmarkBeego_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusBeego, req) -} -func BenchmarkBone_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusBone, req) -} -func BenchmarkDenco_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusDenco, req) -} -func BenchmarkEcho_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusEcho, req) -} -func BenchmarkGin_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusGin, req) -} -func BenchmarkGocraftWeb_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusGocraftWeb, req) -} -func BenchmarkGoji_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusGoji, req) -} -func BenchmarkGoJsonRest_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusGoJsonRest, req) -} -func BenchmarkGoRestful_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusGoRestful, req) -} -func BenchmarkGorillaMux_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusGorillaMux, req) -} -func BenchmarkHttpRouter_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusHttpRouter, req) -} -func BenchmarkHttpTreeMux_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusHttpTreeMux, req) -} -func BenchmarkKocha_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusKocha, req) -} -func BenchmarkMacaron_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusMacaron, req) -} -func BenchmarkMartini_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusMartini, req) -} -func BenchmarkPat_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusPat, req) -} -func BenchmarkPossum_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusPossum, req) -} -func BenchmarkR2router_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusR2router, req) -} -func BenchmarkRevel_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusRevel, req) -} -func BenchmarkRivet_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusRivet, req) -} -func BenchmarkTango_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusTango, req) -} -func BenchmarkTigerTonic_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusTigerTonic, req) -} -func BenchmarkTraffic_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusTraffic, req) -} -func BenchmarkVulcan_GPlusStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/people", nil) - benchRequest(b, gplusVulcan, req) -} - -// func BenchmarkZeus_GPlusStatic(b *testing.B) { -// req, _ := http.NewRequest("GET", "/people", nil) -// benchRequest(b, gplusZeus, req) -// } - -// One Param -func BenchmarkAce_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusAce, req) -} -func BenchmarkBear_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusBear, req) -} -func BenchmarkBeego_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusBeego, req) -} -func BenchmarkBone_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusBone, req) -} -func BenchmarkDenco_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusDenco, req) -} -func BenchmarkEcho_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusEcho, req) -} -func BenchmarkGin_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusGin, req) -} -func BenchmarkGocraftWeb_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusGocraftWeb, req) -} -func BenchmarkGoji_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusGoji, req) -} -func BenchmarkGoJsonRest_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusGoJsonRest, req) -} -func BenchmarkGoRestful_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusGoRestful, req) -} -func BenchmarkGorillaMux_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusGorillaMux, req) -} -func BenchmarkHttpRouter_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusHttpRouter, req) -} -func BenchmarkHttpTreeMux_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusHttpTreeMux, req) -} -func BenchmarkKocha_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusKocha, req) -} -func BenchmarkMacaron_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusMacaron, req) -} -func BenchmarkMartini_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusMartini, req) -} -func BenchmarkPat_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusPat, req) -} -func BenchmarkPossum_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusPossum, req) -} -func BenchmarkR2router_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusR2router, req) -} -func BenchmarkRevel_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusRevel, req) -} -func BenchmarkRivet_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusRivet, req) -} -func BenchmarkTango_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusTango, req) -} -func BenchmarkTigerTonic_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusTigerTonic, req) -} -func BenchmarkTraffic_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusTraffic, req) -} -func BenchmarkVulcan_GPlusParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) - benchRequest(b, gplusVulcan, req) -} - -// func BenchmarkZeus_GPlusParam(b *testing.B) { -// req, _ := http.NewRequest("GET", "/people/118051310819094153327", nil) -// benchRequest(b, gplusZeus, req) -// } - -// Two Params -func BenchmarkAce_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusAce, req) -} -func BenchmarkBear_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusBear, req) -} -func BenchmarkBeego_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusBeego, req) -} -func BenchmarkBone_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusBone, req) -} -func BenchmarkDenco_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusDenco, req) -} -func BenchmarkEcho_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusEcho, req) -} -func BenchmarkGin_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusGin, req) -} -func BenchmarkGocraftWeb_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusGocraftWeb, req) -} -func BenchmarkGoji_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusGoji, req) -} -func BenchmarkGoJsonRest_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusGoJsonRest, req) -} -func BenchmarkGoRestful_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusGoRestful, req) -} -func BenchmarkGorillaMux_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusGorillaMux, req) -} -func BenchmarkHttpRouter_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusHttpRouter, req) -} -func BenchmarkHttpTreeMux_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusHttpTreeMux, req) -} -func BenchmarkKocha_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusKocha, req) -} -func BenchmarkMacaron_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusMacaron, req) -} -func BenchmarkMartini_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusMartini, req) -} -func BenchmarkPat_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusPat, req) -} -func BenchmarkPossum_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusPossum, req) -} -func BenchmarkR2router_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusR2router, req) -} -func BenchmarkRevel_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusRevel, req) -} -func BenchmarkRivet_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusRivet, req) -} -func BenchmarkTango_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusTango, req) -} -func BenchmarkTigerTonic_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusTigerTonic, req) -} -func BenchmarkTraffic_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusTraffic, req) -} -func BenchmarkVulcan_GPlus2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) - benchRequest(b, gplusVulcan, req) -} - -// func BenchmarkZeus_GPlus2Params(b *testing.B) { -// req, _ := http.NewRequest("GET", "/people/118051310819094153327/activities/123456789", nil) -// benchRequest(b, gplusZeus, req) -// } - -// All Routes -func BenchmarkAce_GPlusAll(b *testing.B) { - benchRoutes(b, gplusAce, gplusAPI) -} -func BenchmarkBear_GPlusAll(b *testing.B) { - benchRoutes(b, gplusBear, gplusAPI) -} -func BenchmarkBeego_GPlusAll(b *testing.B) { - benchRoutes(b, gplusBeego, gplusAPI) -} -func BenchmarkBone_GPlusAll(b *testing.B) { - benchRoutes(b, gplusBone, gplusAPI) -} -func BenchmarkDenco_GPlusAll(b *testing.B) { - benchRoutes(b, gplusDenco, gplusAPI) -} -func BenchmarkEcho_GPlusAll(b *testing.B) { - benchRoutes(b, gplusEcho, gplusAPI) -} -func BenchmarkGin_GPlusAll(b *testing.B) { - benchRoutes(b, gplusGin, gplusAPI) -} -func BenchmarkGocraftWeb_GPlusAll(b *testing.B) { - benchRoutes(b, gplusGocraftWeb, gplusAPI) -} -func BenchmarkGoji_GPlusAll(b *testing.B) { - benchRoutes(b, gplusGoji, gplusAPI) -} -func BenchmarkGoJsonRest_GPlusAll(b *testing.B) { - benchRoutes(b, gplusGoJsonRest, gplusAPI) -} -func BenchmarkGoRestful_GPlusAll(b *testing.B) { - benchRoutes(b, gplusGoRestful, gplusAPI) -} -func BenchmarkGorillaMux_GPlusAll(b *testing.B) { - benchRoutes(b, gplusGorillaMux, gplusAPI) -} -func BenchmarkHttpRouter_GPlusAll(b *testing.B) { - benchRoutes(b, gplusHttpRouter, gplusAPI) -} -func BenchmarkHttpTreeMux_GPlusAll(b *testing.B) { - benchRoutes(b, gplusHttpTreeMux, gplusAPI) -} -func BenchmarkKocha_GPlusAll(b *testing.B) { - benchRoutes(b, gplusKocha, gplusAPI) -} -func BenchmarkMacaron_GPlusAll(b *testing.B) { - benchRoutes(b, gplusMacaron, gplusAPI) -} -func BenchmarkMartini_GPlusAll(b *testing.B) { - benchRoutes(b, gplusMartini, gplusAPI) -} -func BenchmarkPat_GPlusAll(b *testing.B) { - benchRoutes(b, gplusPat, gplusAPI) -} -func BenchmarkPossum_GPlusAll(b *testing.B) { - benchRoutes(b, gplusPossum, gplusAPI) -} -func BenchmarkR2router_GPlusAll(b *testing.B) { - benchRoutes(b, gplusR2router, gplusAPI) -} -func BenchmarkRevel_GPlusAll(b *testing.B) { - benchRoutes(b, gplusRevel, gplusAPI) -} -func BenchmarkRivet_GPlusAll(b *testing.B) { - benchRoutes(b, gplusRivet, gplusAPI) -} -func BenchmarkTango_GPlusAll(b *testing.B) { - benchRoutes(b, gplusTango, gplusAPI) -} -func BenchmarkTigerTonic_GPlusAll(b *testing.B) { - benchRoutes(b, gplusTigerTonic, gplusAPI) -} -func BenchmarkTraffic_GPlusAll(b *testing.B) { - benchRoutes(b, gplusTraffic, gplusAPI) -} -func BenchmarkVulcan_GPlusAll(b *testing.B) { - benchRoutes(b, gplusVulcan, gplusAPI) -} - -// func BenchmarkZeus_GPlusAll(b *testing.B) { -// benchRoutes(b, gplusZeus, gplusAPI) -// } diff --git a/parse_test.go b/parse_test.go deleted file mode 100644 index c54046c6..00000000 --- a/parse_test.go +++ /dev/null @@ -1,591 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package main - -import ( - "net/http" - "testing" -) - -// Parse -// https://parse.com/docs/rest#summary -var parseAPI = []route{ - // Objects - {"POST", "/1/classes/:className"}, - {"GET", "/1/classes/:className/:objectId"}, - {"PUT", "/1/classes/:className/:objectId"}, - {"GET", "/1/classes/:className"}, - {"DELETE", "/1/classes/:className/:objectId"}, - - // Users - {"POST", "/1/users"}, - {"GET", "/1/login"}, - {"GET", "/1/users/:objectId"}, - {"PUT", "/1/users/:objectId"}, - {"GET", "/1/users"}, - {"DELETE", "/1/users/:objectId"}, - {"POST", "/1/requestPasswordReset"}, - - // Roles - {"POST", "/1/roles"}, - {"GET", "/1/roles/:objectId"}, - {"PUT", "/1/roles/:objectId"}, - {"GET", "/1/roles"}, - {"DELETE", "/1/roles/:objectId"}, - - // Files - {"POST", "/1/files/:fileName"}, - - // Analytics - {"POST", "/1/events/:eventName"}, - - // Push Notifications - {"POST", "/1/push"}, - - // Installations - {"POST", "/1/installations"}, - {"GET", "/1/installations/:objectId"}, - {"PUT", "/1/installations/:objectId"}, - {"GET", "/1/installations"}, - {"DELETE", "/1/installations/:objectId"}, - - // Cloud Functions - {"POST", "/1/functions"}, -} - -var ( - parseAce http.Handler - parseBear http.Handler - parseBeego http.Handler - parseBone http.Handler - parseDenco http.Handler - parseEcho http.Handler - parseGin http.Handler - parseGocraftWeb http.Handler - parseGoji http.Handler - parseGoJsonRest http.Handler - parseGoRestful http.Handler - parseGorillaMux http.Handler - parseHttpRouter http.Handler - parseHttpTreeMux http.Handler - parseKocha http.Handler - parseMacaron http.Handler - parseMartini http.Handler - parsePat http.Handler - parsePossum http.Handler - parseR2router http.Handler - parseRevel http.Handler - parseRivet http.Handler - parseTango http.Handler - parseTigerTonic http.Handler - parseTraffic http.Handler - parseVulcan http.Handler - // parseZeus http.Handler -) - -func init() { - println("#ParseAPI Routes:", len(parseAPI)) - - calcMem("Ace", func() { - parseAce = loadAce(parseAPI) - }) - calcMem("Bear", func() { - parseBear = loadBear(parseAPI) - }) - calcMem("Beego", func() { - parseBeego = loadBeego(parseAPI) - }) - calcMem("Bone", func() { - parseBone = loadBone(parseAPI) - }) - calcMem("Denco", func() { - parseDenco = loadDenco(parseAPI) - }) - calcMem("Echo", func() { - parseEcho = loadEcho(parseAPI) - }) - calcMem("Gin", func() { - parseGin = loadGin(parseAPI) - }) - calcMem("GocraftWeb", func() { - parseGocraftWeb = loadGocraftWeb(parseAPI) - }) - calcMem("Goji", func() { - parseGoji = loadGoji(parseAPI) - }) - calcMem("GoJsonRest", func() { - parseGoJsonRest = loadGoJsonRest(parseAPI) - }) - calcMem("GoRestful", func() { - parseGoRestful = loadGoRestful(parseAPI) - }) - calcMem("GorillaMux", func() { - parseGorillaMux = loadGorillaMux(parseAPI) - }) - calcMem("HttpRouter", func() { - parseHttpRouter = loadHttpRouter(parseAPI) - }) - calcMem("HttpTreeMux", func() { - parseHttpTreeMux = loadHttpTreeMux(parseAPI) - }) - calcMem("Kocha", func() { - parseKocha = loadKocha(parseAPI) - }) - calcMem("Macaron", func() { - parseMacaron = loadMacaron(parseAPI) - }) - calcMem("Martini", func() { - parseMartini = loadMartini(parseAPI) - }) - calcMem("Pat", func() { - parsePat = loadPat(parseAPI) - }) - calcMem("Possum", func() { - parsePossum = loadPossum(parseAPI) - }) - calcMem("R2router", func() { - parseR2router = loadR2router(parseAPI) - }) - calcMem("Revel", func() { - parseRevel = loadRevel(parseAPI) - }) - calcMem("Rivet", func() { - parseRivet = loadRivet(parseAPI) - }) - calcMem("Tango", func() { - parseTango = loadTango(parseAPI) - }) - calcMem("TigerTonic", func() { - parseTigerTonic = loadTigerTonic(parseAPI) - }) - calcMem("Traffic", func() { - parseTraffic = loadTraffic(parseAPI) - }) - calcMem("Vulcan", func() { - parseVulcan = loadVulcan(parseAPI) - }) - // calcMem("Zeus", func() { - // parseZeus = loadZeus(parseAPI) - // }) - - println() -} - -// Static -func BenchmarkAce_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseAce, req) -} -func BenchmarkBear_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseBear, req) -} -func BenchmarkBeego_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseBeego, req) -} -func BenchmarkBone_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseBone, req) -} -func BenchmarkDenco_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseDenco, req) -} -func BenchmarkEcho_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseEcho, req) -} -func BenchmarkGin_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseGin, req) -} -func BenchmarkGocraftWeb_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseGocraftWeb, req) -} -func BenchmarkGoji_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseGoji, req) -} -func BenchmarkGoJsonRest_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseGoJsonRest, req) -} -func BenchmarkGoRestful_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseGoRestful, req) -} -func BenchmarkGorillaMux_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseGorillaMux, req) -} -func BenchmarkHttpRouter_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseHttpRouter, req) -} -func BenchmarkHttpTreeMux_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseHttpTreeMux, req) -} -func BenchmarkKocha_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseKocha, req) -} -func BenchmarkMacaron_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseMacaron, req) -} -func BenchmarkMartini_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseMartini, req) -} -func BenchmarkPat_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parsePat, req) -} -func BenchmarkPossum_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parsePossum, req) -} -func BenchmarkR2router_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseR2router, req) -} -func BenchmarkRevel_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseRevel, req) -} -func BenchmarkRivet_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseRivet, req) -} -func BenchmarkTango_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseTango, req) -} -func BenchmarkTigerTonic_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseTigerTonic, req) -} -func BenchmarkTraffic_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseTraffic, req) -} -func BenchmarkVulcan_ParseStatic(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/users", nil) - benchRequest(b, parseVulcan, req) -} - -// func BenchmarkZeus_ParseStatic(b *testing.B) { -// req, _ := http.NewRequest("GET", "/1/users", nil) -// benchRequest(b, parseZeus, req) -// } - -// One Param -func BenchmarkAce_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseAce, req) -} -func BenchmarkBear_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseBear, req) -} -func BenchmarkBeego_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseBeego, req) -} -func BenchmarkBone_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseBone, req) -} -func BenchmarkDenco_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseDenco, req) -} -func BenchmarkEcho_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseEcho, req) -} -func BenchmarkGin_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseGin, req) -} -func BenchmarkGocraftWeb_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseGocraftWeb, req) -} -func BenchmarkGoji_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseGoji, req) -} -func BenchmarkGoJsonRest_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseGoJsonRest, req) -} -func BenchmarkGoRestful_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseGoRestful, req) -} -func BenchmarkGorillaMux_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseGorillaMux, req) -} -func BenchmarkHttpRouter_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseHttpRouter, req) -} -func BenchmarkHttpTreeMux_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseHttpTreeMux, req) -} -func BenchmarkKocha_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseKocha, req) -} -func BenchmarkMacaron_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseMacaron, req) -} -func BenchmarkMartini_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseMartini, req) -} -func BenchmarkPat_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parsePat, req) -} -func BenchmarkPossum_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parsePossum, req) -} -func BenchmarkR2router_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseR2router, req) -} -func BenchmarkRevel_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseRevel, req) -} -func BenchmarkRivet_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseRivet, req) -} -func BenchmarkTango_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseTango, req) -} -func BenchmarkTigerTonic_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseTigerTonic, req) -} -func BenchmarkTraffic_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseTraffic, req) -} -func BenchmarkVulcan_ParseParam(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go", nil) - benchRequest(b, parseVulcan, req) -} - -// func BenchmarkZeus_ParseParam(b *testing.B) { -// req, _ := http.NewRequest("GET", "/1/classes/go", nil) -// benchRequest(b, parseZeus, req) -// } - -// Two Params -func BenchmarkAce_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseAce, req) -} -func BenchmarkBear_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseBear, req) -} -func BenchmarkBeego_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseBeego, req) -} -func BenchmarkBone_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseBone, req) -} -func BenchmarkDenco_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseDenco, req) -} -func BenchmarkEcho_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseEcho, req) -} -func BenchmarkGin_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseGin, req) -} -func BenchmarkGocraftWeb_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseGocraftWeb, req) -} -func BenchmarkGoji_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseGoji, req) -} -func BenchmarkGoJsonRest_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseGoJsonRest, req) -} -func BenchmarkGoRestful_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseGoRestful, req) -} -func BenchmarkGorillaMux_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseGorillaMux, req) -} -func BenchmarkHttpRouter_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseHttpRouter, req) -} -func BenchmarkHttpTreeMux_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseHttpTreeMux, req) -} -func BenchmarkKocha_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseKocha, req) -} -func BenchmarkMacaron_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseMacaron, req) -} -func BenchmarkMartini_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseMartini, req) -} -func BenchmarkPat_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parsePat, req) -} -func BenchmarkPossum_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parsePossum, req) -} -func BenchmarkR2router_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseR2router, req) -} -func BenchmarkRevel_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseRevel, req) -} -func BenchmarkRivet_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseRivet, req) -} -func BenchmarkTango_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseTango, req) -} -func BenchmarkTigerTonic_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseTigerTonic, req) -} -func BenchmarkTraffic_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseTraffic, req) -} -func BenchmarkVulcan_Parse2Params(b *testing.B) { - req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) - benchRequest(b, parseVulcan, req) -} - -// func BenchmarkZeus_Parse2Params(b *testing.B) { -// req, _ := http.NewRequest("GET", "/1/classes/go/123456789", nil) -// benchRequest(b, parseZeus, req) -// } - -// All Routes -func BenchmarkAce_ParseAll(b *testing.B) { - benchRoutes(b, parseAce, parseAPI) -} -func BenchmarkBear_ParseAll(b *testing.B) { - benchRoutes(b, parseBeego, parseAPI) -} -func BenchmarkBeego_ParseAll(b *testing.B) { - benchRoutes(b, parseBeego, parseAPI) -} -func BenchmarkBone_ParseAll(b *testing.B) { - benchRoutes(b, parseBone, parseAPI) -} -func BenchmarkDenco_ParseAll(b *testing.B) { - benchRoutes(b, parseDenco, parseAPI) -} -func BenchmarkEcho_ParseAll(b *testing.B) { - benchRoutes(b, parseEcho, parseAPI) -} -func BenchmarkGin_ParseAll(b *testing.B) { - benchRoutes(b, parseGin, parseAPI) -} -func BenchmarkGocraftWeb_ParseAll(b *testing.B) { - benchRoutes(b, parseGocraftWeb, parseAPI) -} -func BenchmarkGoji_ParseAll(b *testing.B) { - benchRoutes(b, parseGoji, parseAPI) -} -func BenchmarkGoJsonRest_ParseAll(b *testing.B) { - benchRoutes(b, parseGoJsonRest, parseAPI) -} -func BenchmarkGoRestful_ParseAll(b *testing.B) { - benchRoutes(b, parseGoRestful, parseAPI) -} -func BenchmarkGorillaMux_ParseAll(b *testing.B) { - benchRoutes(b, parseGorillaMux, parseAPI) -} -func BenchmarkHttpRouter_ParseAll(b *testing.B) { - benchRoutes(b, parseHttpRouter, parseAPI) -} -func BenchmarkHttpTreeMux_ParseAll(b *testing.B) { - benchRoutes(b, parseHttpTreeMux, parseAPI) -} -func BenchmarkKocha_ParseAll(b *testing.B) { - benchRoutes(b, parseKocha, parseAPI) -} -func BenchmarkMacaron_ParseAll(b *testing.B) { - benchRoutes(b, parseMacaron, parseAPI) -} -func BenchmarkMartini_ParseAll(b *testing.B) { - benchRoutes(b, parseMartini, parseAPI) -} -func BenchmarkPat_ParseAll(b *testing.B) { - benchRoutes(b, parsePat, parseAPI) -} -func BenchmarkPossum_ParseAll(b *testing.B) { - benchRoutes(b, parsePossum, parseAPI) -} -func BenchmarkR2router_ParseAll(b *testing.B) { - benchRoutes(b, parseR2router, parseAPI) -} -func BenchmarkRevel_ParseAll(b *testing.B) { - benchRoutes(b, parseRevel, parseAPI) -} -func BenchmarkRivet_ParseAll(b *testing.B) { - benchRoutes(b, parseRivet, parseAPI) -} -func BenchmarkTango_ParseAll(b *testing.B) { - benchRoutes(b, parseTango, parseAPI) -} -func BenchmarkTigerTonic_ParseAll(b *testing.B) { - benchRoutes(b, parseTigerTonic, parseAPI) -} -func BenchmarkTraffic_ParseAll(b *testing.B) { - benchRoutes(b, parseTraffic, parseAPI) -} -func BenchmarkVulcan_ParseAll(b *testing.B) { - benchRoutes(b, parseVulcan, parseAPI) -} - -// func BenchmarkZeus_ParseAll(b *testing.B) { -// benchRoutes(b, parseZeus, parseAPI) -// } diff --git a/router/ace.go b/router/ace.go new file mode 100644 index 00000000..b9fb4014 --- /dev/null +++ b/router/ace.go @@ -0,0 +1,56 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !ace + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/plimble/ace" +) + +func aceHandler(c *ace.C) { + c.Writer.WriteHeader(http.StatusOK) +} + +func aceHandlerReadWrite(c *ace.C) { + io.WriteString(c.Writer, c.Param(driver.ParamNameReadWrite)) +} + +func aceFactory(t driver.Type) []ace.HandlerFunc { + switch t { + case driver.Static: + return []ace.HandlerFunc{aceHandler} + case driver.Parameterized: + return []ace.HandlerFunc{aceHandler} + case driver.ParameterReadWrite: + return []ace.HandlerFunc{aceHandlerReadWrite} + default: + panic("Unknown benchmark type passed") + } +} + +func aceRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := ace.New() + h := aceFactory(t) + + for _, r := range f { + m.Handle(r.Method, r.Path, h) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Ace", + Router: aceRouter, + Homepage: "http://github.com/plimble/ace", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/bear.go b/router/bear.go new file mode 100644 index 00000000..896b3bc1 --- /dev/null +++ b/router/bear.go @@ -0,0 +1,62 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !bear + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/ursiform/bear" +) + +func bearHandler(w http.ResponseWriter, _ *http.Request, _ *bear.Context) { + w.WriteHeader(http.StatusOK) +} + +func bearHandlerReadWrite(w http.ResponseWriter, _ *http.Request, ctx *bear.Context) { + io.WriteString(w, ctx.Params[driver.ParamNameReadWrite]) +} + +func bearFactory(t driver.Type) func(http.ResponseWriter, *http.Request, *bear.Context) { + switch t { + case driver.Static: + return bearHandler + case driver.Parameterized: + return bearHandler + case driver.ParameterReadWrite: + return bearHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func bearRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := bear.New() + h := bearFactory(t) + + for _, r := range f { + switch r.Method { + case "GET", "POST", "PUT", "PATCH", "DELETE": + m.On(r.Method, r.Path, h) + default: + panic("Unknown HTTP method: " + r.Method) + } + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Bear", + Router: bearRouter, + Normalizer: driver.CurlyBracesNormalizer, + Homepage: "http://github.com/ursiform/bear", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/beego.go b/router/beego.go new file mode 100644 index 00000000..14068701 --- /dev/null +++ b/router/beego.go @@ -0,0 +1,74 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !beego + +package router + +import ( + "net/http" + + "github.com/astaxie/beego" + "github.com/astaxie/beego/context" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +const beegoRWP = ":" + driver.ParamNameReadWrite + +func beegoHandler(ctx *context.Context) { + ctx.ResponseWriter.WriteHeader(http.StatusOK) +} + +func beegoHandlerReadWrite(ctx *context.Context) { + ctx.WriteString(ctx.Input.Param(beegoRWP)) +} + +func beegoFactory(t driver.Type) func(ctx *context.Context) { + switch t { + case driver.Static: + return beegoHandler + case driver.Parameterized: + return beegoHandler + case driver.ParameterReadWrite: + return beegoHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func beegoRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := beegoFactory(t) + m := beego.NewControllerRegister() + + for _, r := range f { + switch r.Method { + case "GET": + m.Get(r.Path, h) + case "POST": + m.Post(r.Path, h) + case "PUT": + m.Put(r.Path, h) + case "PATCH": + m.Patch(r.Path, h) + case "DELETE": + m.Delete(r.Path, h) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + return m +} + +func init() { + beego.RunMode = "prod" + beego.BeeLogger.Close() + + driver.RegisterPackage(&driver.Package{ + Name: "Beego", + Router: beegoRouter, + Homepage: "http://beego.me/", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/bone.go b/router/bone.go new file mode 100644 index 00000000..abed18f6 --- /dev/null +++ b/router/bone.go @@ -0,0 +1,69 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !bone + +package router + +import ( + "io" + "net/http" + + "github.com/go-zoo/bone" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +func boneHandler(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func boneHandlerReadWrite(w http.ResponseWriter, req *http.Request) { + io.WriteString(w, bone.GetValue(req, driver.ParamNameReadWrite)) +} + +func boneFactory(t driver.Type) http.Handler { + switch t { + case driver.Static: + return http.HandlerFunc(boneHandler) + case driver.Parameterized: + return http.HandlerFunc(boneHandler) + case driver.ParameterReadWrite: + return http.HandlerFunc(boneHandlerReadWrite) + default: + panic("Unknown benchmark type passed") + } +} + +func boneRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := bone.New() + h := boneFactory(t) + + for _, r := range f { + switch r.Method { + case "GET": + m.Get(r.Path, h) + case "POST": + m.Post(r.Path, h) + case "PUT": + m.Put(r.Path, h) + case "PATCH": + m.Patch(r.Path, h) + case "DELETE": + m.Delete(r.Path, h) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Bone", + Router: boneRouter, + Homepage: "http://github.com/go-zoo/bone", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/denco.go b/router/denco.go new file mode 100644 index 00000000..21a8f2d8 --- /dev/null +++ b/router/denco.go @@ -0,0 +1,65 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !denco + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/naoina/denco" +) + +func dencoHandler(w http.ResponseWriter, _ *http.Request, params denco.Params) { + w.WriteHeader(http.StatusOK) +} + +func dencoHandlerReadWrite(w http.ResponseWriter, req *http.Request, params denco.Params) { + io.WriteString(w, params.Get(driver.ParamNameReadWrite)) +} + +func dencoFactory(t driver.Type) func(http.ResponseWriter, *http.Request, denco.Params) { + switch t { + case driver.Static: + return dencoHandler + case driver.Parameterized: + return dencoHandler + case driver.ParameterReadWrite: + return dencoHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func dencoRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := denco.NewMux() + h := dencoFactory(t) + + handlers := make([]denco.Handler, 0, len(f)) + + for _, r := range f { + handler := m.Handler(r.Method, r.Path, h) + handlers = append(handlers, handler) + } + + handler, err := m.Build(handlers) + + if err != nil { + panic(err) + } + + return handler +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Denco", + Router: dencoRouter, + Homepage: "http://github.com/naoina/denco", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/echo.go b/router/echo.go new file mode 100644 index 00000000..4a290004 --- /dev/null +++ b/router/echo.go @@ -0,0 +1,71 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !echo + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/labstack/echo" +) + +func echoHandler(c *echo.Context) error { + c.Response().WriteHeader(http.StatusOK) + return nil +} + +func echoHandlerReadWrite(c *echo.Context) error { + io.WriteString(c.Response(), c.Param(driver.ParamNameReadWrite)) + return nil +} + +func echoFactory(t driver.Type) func(*echo.Context) error { + switch t { + case driver.Static: + return echoHandler + case driver.Parameterized: + return echoHandler + case driver.ParameterReadWrite: + return echoHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func echoRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := echo.New() + h := echoFactory(t) + + for _, r := range f { + switch r.Method { + case "GET": + m.Get(r.Path, h) + case "POST": + m.Post(r.Path, h) + case "PUT": + m.Put(r.Path, h) + case "PATCH": + m.Patch(r.Path, h) + case "DELETE": + m.Delete(r.Path, h) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Echo", + Router: echoRouter, + Homepage: "http://github.com/labstack/echo", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/gin.go b/router/gin.go new file mode 100644 index 00000000..bdc0bfbd --- /dev/null +++ b/router/gin.go @@ -0,0 +1,58 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !gin + +package router + +import ( + "io" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +func ginHandler(c *gin.Context) { + c.Writer.WriteHeader(http.StatusOK) +} + +func ginHandlerReadWrite(c *gin.Context) { + io.WriteString(c.Writer, c.Params.ByName(driver.ParamNameReadWrite)) +} + +func ginFactory(t driver.Type) func(*gin.Context) { + switch t { + case driver.Static: + return ginHandler + case driver.Parameterized: + return ginHandler + case driver.ParameterReadWrite: + return ginHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func ginRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := gin.New() + h := ginFactory(t) + + for _, r := range f { + m.Handle(r.Method, r.Path, h) + } + + return m +} + +func init() { + gin.SetMode(gin.ReleaseMode) + + driver.RegisterPackage(&driver.Package{ + Name: "Gin", + Router: ginRouter, + Homepage: "http://github.com/gin-gonic/gin", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/gocraftweb.go b/router/gocraftweb.go new file mode 100644 index 00000000..7da5f0de --- /dev/null +++ b/router/gocraftweb.go @@ -0,0 +1,71 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !gocraftweb + +package router + +import ( + "io" + "net/http" + + "github.com/gocraft/web" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +type gocraftWebContext struct{} + +func gocraftWebHandler(w web.ResponseWriter, _ *web.Request) { + w.WriteHeader(http.StatusOK) +} + +func gocraftWebHandlerReadWrite(w web.ResponseWriter, r *web.Request) { + io.WriteString(w, r.PathParams[driver.ParamNameReadWrite]) +} + +func gocraftWebFactory(t driver.Type) func(web.ResponseWriter, *web.Request) { + switch t { + case driver.Static: + return gocraftWebHandler + case driver.Parameterized: + return gocraftWebHandler + case driver.ParameterReadWrite: + return gocraftWebHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func gocraftWebRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := gocraftWebFactory(t) + m := web.New(gocraftWebContext{}) + + for _, r := range f { + switch r.Method { + case "GET": + m.Get(r.Path, h) + case "POST": + m.Post(r.Path, h) + case "PUT": + m.Put(r.Path, h) + case "PATCH": + m.Patch(r.Path, h) + case "DELETE": + m.Delete(r.Path, h) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "GoCraft Web", + Router: gocraftWebRouter, + Homepage: "http://github.com/gocraft/web", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/goji.go b/router/goji.go new file mode 100644 index 00000000..3efa7558 --- /dev/null +++ b/router/goji.go @@ -0,0 +1,69 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !goji + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/zenazn/goji/web" +) + +func gojiHandler(_ web.C, w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func gojiHandlerReadWrite(c web.C, w http.ResponseWriter, _ *http.Request) { + io.WriteString(w, c.URLParams[driver.ParamNameReadWrite]) +} + +func gojiFactory(t driver.Type) func(web.C, http.ResponseWriter, *http.Request) { + switch t { + case driver.Static: + return gojiHandler + case driver.Parameterized: + return gojiHandler + case driver.ParameterReadWrite: + return gojiHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func gojiRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := web.New() + h := gojiFactory(t) + + for _, r := range f { + switch r.Method { + case "GET": + m.Get(r.Path, h) + case "POST": + m.Post(r.Path, h) + case "PUT": + m.Put(r.Path, h) + case "PATCH": + m.Patch(r.Path, h) + case "DELETE": + m.Delete(r.Path, h) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Goji", + Router: gojiRouter, + Homepage: "http://github.com/zenazn/goji/web", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/gojsonrest.go b/router/gojsonrest.go new file mode 100644 index 00000000..18c1f674 --- /dev/null +++ b/router/gojsonrest.go @@ -0,0 +1,67 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !gojsonrest + +package router + +import ( + "io" + "log" + "net/http" + + "github.com/ant0ine/go-json-rest/rest" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +func goJsonRestHandler(w rest.ResponseWriter, _ *rest.Request) { + w.WriteHeader(http.StatusOK) +} + +func goJsonRestHandlerReadWrite(w rest.ResponseWriter, r *rest.Request) { + io.WriteString(w.(io.Writer), r.PathParam(driver.ParamNameReadWrite)) +} + +func goJsonRestFactory(t driver.Type) func(rest.ResponseWriter, *rest.Request) { + switch t { + case driver.Static: + return goJsonRestHandler + case driver.Parameterized: + return goJsonRestHandler + case driver.ParameterReadWrite: + return goJsonRestHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func goJsonRestRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := rest.NewApi() + h := goJsonRestFactory(t) + + restRoutes := make([]*rest.Route, 0, len(f)) + + for _, r := range f { + restRoutes = append(restRoutes, + &rest.Route{r.Method, r.Path, h}, + ) + } + + router, err := rest.MakeRouter(restRoutes...) + if err != nil { + log.Fatal(err) + } + + m.SetApp(router) + return m.MakeHandler() +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Go Json Rest", + Router: goJsonRestRouter, + Homepage: "http://github.com/ant0ine/go-json-rest/rest", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/gorestful.go b/router/gorestful.go new file mode 100644 index 00000000..f2ad5c5a --- /dev/null +++ b/router/gorestful.go @@ -0,0 +1,74 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !gorestful + +package router + +import ( + "io" + "net/http" + + "github.com/emicklei/go-restful" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +func goRestfulHandler(_ *restful.Request, w *restful.Response) { + // Cannot use w.WriteHeader for some reason + w.ResponseWriter.WriteHeader(http.StatusOK) +} + +func goRestfulHandlerReadWrite(r *restful.Request, w *restful.Response) { + io.WriteString(w, r.PathParameter(driver.ParamNameReadWrite)) +} + +func goRestfulFactory(t driver.Type) func(*restful.Request, *restful.Response) { + switch t { + case driver.Static: + return goRestfulHandler + case driver.Parameterized: + return goRestfulHandler + case driver.ParameterReadWrite: + return goRestfulHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func goRestfulRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := goRestfulFactory(t) + s := &restful.WebService{} + m := restful.NewContainer() + + for _, r := range f { + switch r.Method { + case "GET": + s.Route(s.GET(r.Path).To(h)) + case "POST": + s.Route(s.POST(r.Path).To(h)) + case "PUT": + s.Route(s.PUT(r.Path).To(h)) + case "PATCH": + s.Route(s.PATCH(r.Path).To(h)) + case "DELETE": + s.Route(s.DELETE(r.Path).To(h)) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + m.Add(s) + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Go Restful", + Router: goRestfulRouter, + Normalizer: driver.CurlyBracesNormalizer, + Homepage: "http://github.com/emicklei/go-restful", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/gorilla_mux.go b/router/gorilla_mux.go new file mode 100644 index 00000000..f9341a03 --- /dev/null +++ b/router/gorilla_mux.go @@ -0,0 +1,58 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !gorilla_mux + +package router + +import ( + "io" + "net/http" + + "github.com/gorilla/mux" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +func gorillaHandler(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func gorillaHandlerReadWrite(w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + io.WriteString(w, params[driver.ParamNameReadWrite]) +} + +func gorillaFactory(t driver.Type) func(http.ResponseWriter, *http.Request) { + switch t { + case driver.Static: + return gorillaHandler + case driver.Parameterized: + return gorillaHandler + case driver.ParameterReadWrite: + return gorillaHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func gorillaRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := mux.NewRouter() + h := gorillaFactory(t) + + for _, r := range f { + m.HandleFunc(r.Path, h).Methods(r.Method) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Gorilla Mux", + Router: gorillaRouter, + Normalizer: driver.CurlyBracesNormalizer, + Homepage: "http://github.com/gorilla/mux", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/http_servemux.go b/router/http_servemux.go new file mode 100644 index 00000000..c44619b1 --- /dev/null +++ b/router/http_servemux.go @@ -0,0 +1,48 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !http_servemux + +package router + +import ( + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +type httpServeMuxHandler struct{} + +func (h *httpServeMuxHandler) ServeHTTP(r http.ResponseWriter, _ *http.Request) { + r.WriteHeader(http.StatusOK) +} + +func httpServeMuxFactory(t driver.Type) http.Handler { + switch t { + case driver.Static: + return &httpServeMuxHandler{} + default: + panic("Unknown benchmark type passed") + } +} + +func httpServeMuxRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := httpServeMuxFactory(t) + m := http.NewServeMux() + + for _, r := range f { + m.Handle(r.Path, h) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "http.ServeMux", + Router: httpServeMuxRouter, + Homepage: "http://golang.org/pkg/net/http/#ServeMux", + Supports: driver.Static, + }) +} diff --git a/router/httprouter.go b/router/httprouter.go new file mode 100644 index 00000000..05b18468 --- /dev/null +++ b/router/httprouter.go @@ -0,0 +1,56 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !httprouter + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/julienschmidt/httprouter" +) + +func httpRouterHandler(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) { + w.WriteHeader(http.StatusOK) +} + +func httpRouterHandlerReadWrite(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + io.WriteString(w, p.ByName(driver.ParamNameReadWrite)) +} + +func httpRouterFactory(t driver.Type) func(http.ResponseWriter, *http.Request, httprouter.Params) { + switch t { + case driver.Static: + return httpRouterHandler + case driver.Parameterized: + return httpRouterHandler + case driver.ParameterReadWrite: + return httpRouterHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func httpRouterRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := httprouter.New() + h := httpRouterFactory(t) + + for _, r := range f { + m.Handle(r.Method, r.Path, h) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "HTTP Router", + Router: httpRouterRouter, + Homepage: "http://github.com/julienschmidt/httprouter", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/httptreemux.go b/router/httptreemux.go new file mode 100644 index 00000000..9197913d --- /dev/null +++ b/router/httptreemux.go @@ -0,0 +1,56 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !httptreemux + +package router + +import ( + "io" + "net/http" + + "github.com/dimfeld/httptreemux" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +func httpTreeMuxHandler(w http.ResponseWriter, _ *http.Request, _ map[string]string) { + w.WriteHeader(http.StatusOK) +} + +func httpTreeMuxHandlerReadWrite(w http.ResponseWriter, r *http.Request, p map[string]string) { + io.WriteString(w, p[driver.ParamNameReadWrite]) +} + +func httpTreeMuxFactory(t driver.Type) func(http.ResponseWriter, *http.Request, map[string]string) { + switch t { + case driver.Static: + return httpTreeMuxHandler + case driver.Parameterized: + return httpTreeMuxHandler + case driver.ParameterReadWrite: + return httpTreeMuxHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func httpTreeMuxRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := httptreemux.New() + h := httpTreeMuxFactory(t) + + for _, r := range f { + m.Handle(r.Method, r.Path, h) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "HTTP Tree Mux", + Router: httpTreeMuxRouter, + Homepage: "http://github.com/dimfeld/httptreemux", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/kocha.go b/router/kocha.go new file mode 100644 index 00000000..da3e0de6 --- /dev/null +++ b/router/kocha.go @@ -0,0 +1,177 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !kocha + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/naoina/kocha-urlrouter" + _ "github.com/naoina/kocha-urlrouter/doublearray" +) + +type kochaController interface { + http.Handler + Get(http.ResponseWriter, *http.Request) + Post(http.ResponseWriter, *http.Request) + Put(http.ResponseWriter, *http.Request) + Patch(http.ResponseWriter, *http.Request) + Delete(http.ResponseWriter, *http.Request) + RouterMap() map[string]urlrouter.URLRouter +} + +type kochaHandler struct { + routerMap map[string]urlrouter.URLRouter + params []urlrouter.Param +} + +func (h *kochaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + method, params := h.routerMap[r.Method].Lookup(r.URL.Path) + h.params = params + + if method == nil { + w.WriteHeader(http.StatusNotFound) + } else { + method.(http.HandlerFunc).ServeHTTP(w, r) + } +} + +func (h *kochaHandler) Get(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (h *kochaHandler) Post(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (h *kochaHandler) Put(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (h *kochaHandler) Patch(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (h *kochaHandler) Delete(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (h *kochaHandler) RouterMap() map[string]urlrouter.URLRouter { + return h.routerMap +} + +type kochaHandlerReadWrite struct { + routerMap map[string]urlrouter.URLRouter + params []urlrouter.Param +} + +func (h *kochaHandlerReadWrite) ServeHTTP(w http.ResponseWriter, r *http.Request) { + method, params := h.routerMap[r.Method].Lookup(r.URL.Path) + h.params = params + + if method == nil { + w.WriteHeader(http.StatusNotFound) + } else { + method.(http.HandlerFunc).ServeHTTP(w, r) + } +} + +func (h *kochaHandlerReadWrite) Post(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (h *kochaHandlerReadWrite) Put(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (h *kochaHandlerReadWrite) Patch(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (h *kochaHandlerReadWrite) Delete(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (h *kochaHandlerReadWrite) RouterMap() map[string]urlrouter.URLRouter { + return h.routerMap +} + +func (h *kochaHandlerReadWrite) Get(w http.ResponseWriter, r *http.Request) { + var name string + for _, param := range h.params { + if param.Name == driver.ParamNameReadWrite { + name = param.Value + break + } + } + io.WriteString(w, name) +} + +func kochaFactory(t driver.Type) kochaController { + switch t { + case driver.Static, driver.Parameterized: + return &kochaHandler{routerMap: map[string]urlrouter.URLRouter{ + "GET": urlrouter.NewURLRouter("doublearray"), + "POST": urlrouter.NewURLRouter("doublearray"), + "PUT": urlrouter.NewURLRouter("doublearray"), + "PATCH": urlrouter.NewURLRouter("doublearray"), + "DELETE": urlrouter.NewURLRouter("doublearray"), + }} + case driver.ParameterReadWrite: + return &kochaHandlerReadWrite{routerMap: map[string]urlrouter.URLRouter{ + "GET": urlrouter.NewURLRouter("doublearray"), + "POST": urlrouter.NewURLRouter("doublearray"), + "PUT": urlrouter.NewURLRouter("doublearray"), + "PATCH": urlrouter.NewURLRouter("doublearray"), + "DELETE": urlrouter.NewURLRouter("doublearray"), + }} + default: + panic("Unknown benchmark type passed") + } +} + +func kochaRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := kochaFactory(t) + h := make(map[string][]urlrouter.Record) + + for _, r := range f { + var ch http.HandlerFunc + + switch r.Method { + case "GET": + ch = m.Get + case "POST": + ch = m.Post + case "PUT": + ch = m.Put + case "PATCH": + ch = m.Patch + case "DELETE": + ch = m.Delete + } + + h[r.Method] = append(h[r.Method], urlrouter.NewRecord(r.Path, ch)) + } + + for method, records := range h { + if err := m.RouterMap()[method].Build(records); err != nil { + panic(err) + } + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Kocha", + Router: kochaRouter, + Homepage: "http://github.com/naoina/kocha-urlrouter", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/macaron.go b/router/macaron.go new file mode 100644 index 00000000..0ee53319 --- /dev/null +++ b/router/macaron.go @@ -0,0 +1,55 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !macaron + +package router + +import ( + "net/http" + + "github.com/Unknwon/macaron" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +func macaronHandler(c *macaron.Context) { + c.Resp.WriteHeader(http.StatusOK) +} + +func macaronHandlerReadWrite(c *macaron.Context) string { + return c.Params(driver.ParamNameReadWrite) +} + +func macaronFactory(t driver.Type) interface{} { + switch t { + case driver.Static: + return macaronHandler + case driver.Parameterized: + return macaronHandler + case driver.ParameterReadWrite: + return macaronHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func macaronRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := macaron.New() + h := macaronFactory(t) + + for _, r := range f { + m.Handle(r.Method, r.Path, []macaron.Handler{h}) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Macaron", + Router: macaronRouter, + Homepage: "http://github.com/Unknwon/macaron", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/martini.go b/router/martini.go new file mode 100644 index 00000000..700260f3 --- /dev/null +++ b/router/martini.go @@ -0,0 +1,71 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !martini + +package router + +import ( + "net/http" + + "github.com/go-martini/martini" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +func martiniHandler(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func martiniHandlerReadWrite(p martini.Params) string { + return p[driver.ParamNameReadWrite] +} + +func martiniFactory(t driver.Type) interface{} { + switch t { + case driver.Static: + return martiniHandler + case driver.Parameterized: + return martiniHandler + case driver.ParameterReadWrite: + return martiniHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func martiniRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := martiniFactory(t) + m := martini.NewRouter() + + for _, r := range f { + switch r.Method { + case "GET": + m.Get(r.Path, h) + case "POST": + m.Post(r.Path, h) + case "PUT": + m.Put(r.Path, h) + case "PATCH": + m.Patch(r.Path, h) + case "DELETE": + m.Delete(r.Path, h) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + martini := martini.New() + martini.Action(m.Handle) + + return martini +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Martini", + Router: martiniRouter, + Homepage: "http://github.com/go-martini/martini", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/pat.go b/router/pat.go new file mode 100644 index 00000000..2d792fca --- /dev/null +++ b/router/pat.go @@ -0,0 +1,69 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !martini + +package router + +import ( + "io" + "net/http" + + "github.com/bmizerany/pat" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +const patRWP = ":" + driver.ParamNameReadWrite + +func patHandler(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func patHandlerReadWrite(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, r.URL.Query().Get(patRWP)) +} + +func patFactory(t driver.Type) http.Handler { + switch t { + case driver.Static: + return http.HandlerFunc(patHandler) + case driver.Parameterized: + return http.HandlerFunc(patHandler) + case driver.ParameterReadWrite: + return http.HandlerFunc(patHandlerReadWrite) + default: + panic("Unknown benchmark type passed") + } +} + +func patRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := pat.New() + h := patFactory(t) + + for _, r := range f { + switch r.Method { + case "GET": + m.Get(r.Path, h) + case "POST": + m.Post(r.Path, h) + case "PUT": + m.Put(r.Path, h) + case "DELETE": + m.Del(r.Path, h) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Pat", + Router: patRouter, + Homepage: "http://github.com/bmizerany/pat", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/possum.go b/router/possum.go new file mode 100644 index 00000000..26a5c4fe --- /dev/null +++ b/router/possum.go @@ -0,0 +1,68 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !possum + +package router + +import ( + "io" + "net/http" + "strings" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/mikespook/possum" + "github.com/mikespook/possum/router" + "github.com/mikespook/possum/view" +) + +func possumHandler(c *possum.Context) error { + c.Response.WriteHeader(http.StatusOK) + return nil +} + +func possumHandlerReadWrite(c *possum.Context) error { + io.WriteString(c.Response, c.Request.URL.Query().Get(driver.ParamNameReadWrite)) + return nil +} + +func possumFactory(t driver.Type) func(*possum.Context) error { + switch t { + case driver.Static: + return possumHandler + case driver.Parameterized: + return possumHandler + case driver.ParameterReadWrite: + return possumHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func possumRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := possumFactory(t) + m := possum.NewServerMux() + + for _, r := range f { + var routable router.Router = router.Simple(r.Path) + + if strings.Contains(r.Path, "[^/]*)") { + routable = router.RegEx(r.Path) + } + + m.HandleFunc(routable, h, view.Simple("text/html", "utf-8")) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Possum", + Router: possumRouter, + Normalizer: driver.RegExAllNormalizer, + Homepage: "http://github.com/mikespook/possum", + Supports: driver.Static | driver.Parameterized, /* | driver.ParameterReadWrite */ + }) +} diff --git a/router/r2router.go b/router/r2router.go new file mode 100644 index 00000000..fc06c510 --- /dev/null +++ b/router/r2router.go @@ -0,0 +1,56 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !r2router + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/vanng822/r2router" +) + +func r2RouterHandler(w http.ResponseWriter, _ *http.Request, _ r2router.Params) { + w.WriteHeader(http.StatusOK) +} + +func r2RouterHandlerReadWrite(w http.ResponseWriter, _ *http.Request, p r2router.Params) { + io.WriteString(w, p.Get(driver.ParamNameReadWrite)) +} + +func r2RouterFactory(t driver.Type) func(http.ResponseWriter, *http.Request, r2router.Params) { + switch t { + case driver.Static: + return r2RouterHandler + case driver.Parameterized: + return r2RouterHandler + case driver.ParameterReadWrite: + return r2RouterHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func r2RouterRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := r2RouterFactory(t) + m := r2router.NewRouter() + + for _, r := range f { + m.AddHandler(r.Method, r.Path, h) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "R2 Router", + Router: r2RouterRouter, + Homepage: "http://github.com/vanng822/r2router", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/revel.go b/router/revel.go new file mode 100644 index 00000000..7892f7bd --- /dev/null +++ b/router/revel.go @@ -0,0 +1,216 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !revel + +package router + +import ( + "fmt" + "io" + "net/http" + "path" + "path/filepath" + + "go/build" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/revel/revel" + "github.com/robfig/pathtree" +) + +// In the following code some Revel internals are modelled. +// The original revel code is copyrighted by Rob Figueiredo. +// See https://github.com/revel/revel/blob/master/LICENSE +type RevelController struct { + *revel.Controller + router *revel.Router +} + +func (rc *RevelController) Handle() revel.Result { + return revelResult{} +} + +func (rc *RevelController) HandleReadWrite() revel.Result { + return rc.RenderText(rc.Params.Get(driver.ParamNameReadWrite)) +} + +func (rc *RevelController) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Dirty hacks, do NOT copy! + revel.MainRouter = rc.router + + upgrade := r.Header.Get("Upgrade") + if upgrade == "websocket" || upgrade == "Websocket" { + panic("Not implemented") + } + + var ( + req = revel.NewRequest(r) + resp = revel.NewResponse(w) + c = revel.NewController(req, resp) + ) + req.Websocket = nil + + revel.Filters[0](c, revel.Filters[1:]) + + if c.Result != nil { + c.Result.Apply(req, resp) + } else if c.Response.Status != 0 { + c.Response.Out.WriteHeader(c.Response.Status) + } else { + panic("Result is empty") + } + // Close the Writer if we can + if w, ok := resp.Out.(io.Closer); ok { + w.Close() + } +} + +type revelResult struct{} + +func (rr revelResult) Apply(req *revel.Request, resp *revel.Response) { + resp.Out.WriteHeader(http.StatusOK) +} + +func revelFactory(t driver.Type) string { + switch t { + case driver.Static: + return "RevelController.Handle" + case driver.Parameterized: + return "RevelController.Handle" + case driver.ParameterReadWrite: + return "RevelController.HandleReadWrite" + default: + panic("Unknown benchmark type passed") + } +} + +func revelRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := revelFactory(t) + m := revel.NewRouter("") + + rs := make([]*revel.Route, 0, len(f)) + for _, r := range f { + rs = append(rs, revel.NewRoute(r.Method, r.Path, h, "", "", 0)) + } + m.Routes = rs + + m.Tree = pathtree.New() + for _, r := range m.Routes { + err := m.Tree.Add(r.TreePath, r) + + // Allow GETs to respond to HEAD requests. + if err == nil && r.Method == "GET" { + err = m.Tree.Add("/HEAD"+r.Path, r) + } + + // Error adding a route to the pathtree. + if err != nil { + panic(err) + } + } + + rc := &RevelController{} + rc.router = m + + return rc +} + +// findSrcPaths uses the "go/build" package to find the source root for Revel +// and the app. +// +// Directly copied from revel/revel.go. +// The original revel code is copyrighted by Rob Figueiredo. +// See https://github.com/revel/revel/blob/master/LICENSE +func revelFindSrcPaths(importPath string) (revelSourcePath, appSourcePath string) { + var ( + gopaths = filepath.SplitList(build.Default.GOPATH) + goroot = build.Default.GOROOT + ) + + if len(gopaths) == 0 { + panic("GOPATH environment variable is not set. " + + "Please refer to http://golang.org/doc/code.html to configure your Go environment.") + } + + if revelContainsString(gopaths, goroot) { + panic(fmt.Sprintf("GOPATH (%s) must not include your GOROOT (%s). "+ + "Please refer to http://golang.org/doc/code.html to configure your Go environment.", + gopaths, goroot)) + } + + appPkg, err := build.Import(importPath, "", build.FindOnly) + if err != nil { + panic("Failed to import" + importPath + "with error:" + err.Error()) + } + + revelPkg, err := build.Import(revel.REVEL_IMPORT_PATH, "", build.FindOnly) + if err != nil { + panic("Failed to find Revel with error:" + err.Error()) + } + + return revelPkg.SrcRoot, appPkg.SrcRoot +} + +// Directly copied from revel/utils.go. +// The original revel code is copyrighted by Rob Figueiredo. +// See https://github.com/revel/revel/blob/master/LICENSE +func revelContainsString(list []string, target string) bool { + for _, el := range list { + if el == target { + return true + } + } + return false +} + +func revelInit() { + // Only use the Revel filters required for this benchmark + revel.Filters = []revel.Filter{ + revel.RouterFilter, + revel.ParamsFilter, + revel.ActionInvoker, + } + + revel.RegisterController( + (*RevelController)(nil), + []*revel.MethodType{ + &revel.MethodType{ + Name: "Handle", + }, + &revel.MethodType{ + Name: "HandleReadWrite", + }, + }, + ) + + // Load mime config from revel, otherwise revel panics (nil pointer dereference) + srcPath, _ := revelFindSrcPaths(revel.REVEL_IMPORT_PATH) + revel.ConfPaths = []string{path.Join( + srcPath, + filepath.FromSlash(revel.REVEL_IMPORT_PATH), + "conf"), + } + revel.LoadMimeConfig() + + // Must be set before instantiationg the TemplateLoader object to avoid panics. + revel.RevelPath = path.Join(srcPath, filepath.FromSlash(revel.REVEL_IMPORT_PATH)) + revel.TemplatePaths = []string{path.Join(revel.RevelPath, "templates")} + + // Otherwise revel panics (nil pointer dereference) in revel/results.go:47 + // ErrorResult.Apply: tmpl, err := MainTemplateLoader.Template(templatePath) + revel.MainTemplateLoader = revel.NewTemplateLoader(revel.TemplatePaths) + revel.MainTemplateLoader.Refresh() +} + +func init() { + revelInit() + + driver.RegisterPackage(&driver.Package{ + Name: "Revel", + Router: revelRouter, + Homepage: "http://github.com/revel/revel", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/rivet.go b/router/rivet.go new file mode 100644 index 00000000..088f9956 --- /dev/null +++ b/router/rivet.go @@ -0,0 +1,55 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !rivet + +package router + +import ( + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/typepress/rivet" +) + +func rivetHandler(c rivet.Context) { + c.Response().WriteHeader(http.StatusOK) +} + +func rivetHandlerReadWrite(c rivet.Context) { + c.WriteString(c.GetParams().Get(driver.ParamNameReadWrite)) +} + +func rivetFactory(t driver.Type) func(rivet.Context) { + switch t { + case driver.Static: + return rivetHandler + case driver.Parameterized: + return rivetHandler + case driver.ParameterReadWrite: + return rivetHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func rivetRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := rivetFactory(t) + m := rivet.NewRouter(nil) + + for _, r := range f { + m.Handle(r.Method, r.Path, h) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Rivet", + Router: rivetRouter, + Homepage: "http://github.com/typepress/rivet", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/tango.go b/router/tango.go new file mode 100644 index 00000000..fbe64fe3 --- /dev/null +++ b/router/tango.go @@ -0,0 +1,61 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !tango + +package router + +import ( + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + llog "github.com/lunny/log" + "github.com/lunny/tango" +) + +const tangoRWP = ":" + driver.ParamNameReadWrite + +func tangoHandler(c *tango.Context) { + c.ResponseWriter.WriteHeader(http.StatusOK) +} + +func tangoHandlerReadWrite(c *tango.Context) { + c.Write([]byte(c.Params().Get(tangoRWP))) +} + +func tangoFactory(t driver.Type) func(*tango.Context) { + switch t { + case driver.Static: + return tangoHandler + case driver.Parameterized: + return tangoHandler + case driver.ParameterReadWrite: + return tangoHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func tangoRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := tangoFactory(t) + m := tango.NewWithLog(llog.Std) + + for _, r := range f { + m.Route(r.Method, r.Path, h) + } + + return m +} + +func init() { + llog.SetOutputLevel(llog.Lnone) + llog.SetOutput(&driver.ResponseStub{}) + + driver.RegisterPackage(&driver.Package{ + Name: "Tango", + Router: tangoRouter, + Homepage: "http://github.com/lunny/tango", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/tigertonic.go b/router/tigertonic.go new file mode 100644 index 00000000..4795160e --- /dev/null +++ b/router/tigertonic.go @@ -0,0 +1,59 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !tigertonic + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/rcrowley/go-tigertonic" +) + +const tigerTonicRWP = ":" + driver.ParamNameReadWrite + +func tigerTonicHandler(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func tigerTonicHandlerReadWrite(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, r.URL.Query().Get(tigerTonicRWP)) +} + +func tigerTonicFactory(t driver.Type) func(http.ResponseWriter, *http.Request) { + switch t { + case driver.Static: + return tigerTonicHandler + case driver.Parameterized: + return tigerTonicHandler + case driver.ParameterReadWrite: + return tigerTonicHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func tigerTonicRouter(t driver.Type, f driver.Fixtures) http.Handler { + h := tigerTonicFactory(t) + m := tigertonic.NewTrieServeMux() + + for _, r := range f { + m.HandleFunc(r.Method, r.Path, h) + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Tiger Tonic", + Router: tigerTonicRouter, + Normalizer: driver.CurlyBracesNormalizer, + Homepage: "http://github.com/rcrowley/go-tigertonic", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/traffic.go b/router/traffic.go new file mode 100644 index 00000000..dc220afa --- /dev/null +++ b/router/traffic.go @@ -0,0 +1,71 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !traffic + +package router + +import ( + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/pilu/traffic" +) + +func trafficHandler(w traffic.ResponseWriter, _ *traffic.Request) { + w.WriteHeader(http.StatusOK) +} + +func trafficHandlerReadWrite(w traffic.ResponseWriter, r *traffic.Request) { + io.WriteString(w, r.URL.Query().Get(driver.ParamNameReadWrite)) +} + +func trafficFactory(t driver.Type) func(traffic.ResponseWriter, *traffic.Request) { + switch t { + case driver.Static: + return trafficHandler + case driver.Parameterized: + return trafficHandler + case driver.ParameterReadWrite: + return trafficHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func trafficRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := traffic.New() + h := trafficFactory(t) + + for _, r := range f { + switch r.Method { + case "GET": + m.Get(r.Path, h) + case "POST": + m.Post(r.Path, h) + case "PUT": + m.Put(r.Path, h) + case "PATCH": + m.Patch(r.Path, h) + case "DELETE": + m.Delete(r.Path, h) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + return m +} + +func init() { + traffic.SetVar("env", "bench") + + driver.RegisterPackage(&driver.Package{ + Name: "Traffic", + Router: trafficRouter, + Homepage: "http://github.com/pilu/traffic", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/vulcan.go b/router/vulcan.go new file mode 100644 index 00000000..afd149a9 --- /dev/null +++ b/router/vulcan.go @@ -0,0 +1,62 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build !vulcan + +package router + +import ( + "fmt" + "io" + "net/http" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/mailgun/route" +) + +func vulcanHandler(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func vulcanHandlerReadWrite(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, r.URL.Query().Get(driver.ParamNameReadWrite)) +} + +func vulcanFactory(t driver.Type) func(http.ResponseWriter, *http.Request) { + switch t { + case driver.Static: + return vulcanHandler + case driver.Parameterized: + return vulcanHandler + case driver.ParameterReadWrite: + return vulcanHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func vulcanRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := route.NewMux() + h := vulcanFactory(t) + + for _, r := range f { + expr := fmt.Sprintf(`Method("%s") && Path("%s")`, r.Method, r.Path) + + if err := m.HandleFunc(expr, h); err != nil { + panic(err) + } + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Mailgun Vulcan", + Router: vulcanRouter, + Normalizer: driver.XThanSignNormalizer, + Homepage: "http://github.com/mailgun/route", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/router/zeus.go b/router/zeus.go new file mode 100644 index 00000000..f0bf9810 --- /dev/null +++ b/router/zeus.go @@ -0,0 +1,67 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// +build ignore,!zeus + +package router + +import ( + "io" + "net/http" + + "github.com/daryl/zeus" + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +func zeusHandler(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func zeusHandlerReadWrite(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, zeus.Var(r, driver.ParamNameReadWrite)) +} + +func zeusFactory(t driver.Type) func(http.ResponseWriter, *http.Request) { + switch t { + case driver.Static: + return zeusHandler + case driver.Parameterized: + return zeusHandler + case driver.ParameterReadWrite: + return zeusHandlerReadWrite + default: + panic("Unknown benchmark type passed") + } +} + +func zeusRouter(t driver.Type, f driver.Fixtures) http.Handler { + m := zeus.New() + h := zeusFactory(t) + + for _, r := range f { + switch r.Method { + case "GET": + m.GET(r.Path, h) + case "POST": + m.POST(r.Path, h) + case "PUT": + m.PUT(r.Path, h) + case "DELETE": + m.DELETE(r.Path, h) + default: + panic("Unknow HTTP method: " + r.Method) + } + } + + return m +} + +func init() { + driver.RegisterPackage(&driver.Package{ + Name: "Zeus", + Router: zeusRouter, + Homepage: "http://github.com/daryl/zeus", + Supports: driver.Static | driver.Parameterized | driver.ParameterReadWrite, + }) +} diff --git a/routers.go b/routers.go deleted file mode 100644 index ecb7a97c..00000000 --- a/routers.go +++ /dev/null @@ -1,1370 +0,0 @@ -// Copyright 2014 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package main - -import ( - "fmt" - "io" - "log" - "net/http" - "os" - "regexp" - "runtime" - - // If you add new routers please: - // - Keep the benchmark functions etc. alphabetically sorted - // - Make a pull request (without benchmark results) at - // https://github.com/julienschmidt/go-http-routing-benchmark - "github.com/Unknwon/macaron" - "github.com/ant0ine/go-json-rest/rest" - "github.com/astaxie/beego" - "github.com/astaxie/beego/context" - "github.com/bmizerany/pat" - // "github.com/daryl/zeus" - "github.com/dimfeld/httptreemux" - "github.com/emicklei/go-restful" - "github.com/gin-gonic/gin" - "github.com/go-martini/martini" - "github.com/go-zoo/bone" - "github.com/gocraft/web" - "github.com/gorilla/mux" - "github.com/julienschmidt/httprouter" - "github.com/labstack/echo" - llog "github.com/lunny/log" - "github.com/lunny/tango" - vulcan "github.com/mailgun/route" - "github.com/mikespook/possum" - possumrouter "github.com/mikespook/possum/router" - possumview "github.com/mikespook/possum/view" - "github.com/naoina/denco" - "github.com/naoina/kocha-urlrouter" - _ "github.com/naoina/kocha-urlrouter/doublearray" - "github.com/pilu/traffic" - "github.com/plimble/ace" - "github.com/rcrowley/go-tigertonic" - "github.com/revel/revel" - "github.com/robfig/pathtree" - "github.com/typepress/rivet" - "github.com/ursiform/bear" - "github.com/vanng822/r2router" - goji "github.com/zenazn/goji/web" -) - -type route struct { - method string - path string -} - -type mockResponseWriter struct{} - -func (m *mockResponseWriter) Header() (h http.Header) { - return http.Header{} -} - -func (m *mockResponseWriter) Write(p []byte) (n int, err error) { - return len(p), nil -} - -func (m *mockResponseWriter) WriteString(s string) (n int, err error) { - return len(s), nil -} - -func (m *mockResponseWriter) WriteHeader(int) {} - -var nullLogger *log.Logger - -// flag indicating if the normal or the test handler should be loaded -var loadTestHandler = false - -func init() { - // beego sets it to runtime.NumCPU() - // Currently none of the contesters does concurrent routing - runtime.GOMAXPROCS(1) - - // makes logging 'webscale' (ignores them) - log.SetOutput(new(mockResponseWriter)) - nullLogger = log.New(new(mockResponseWriter), "", 0) - - initBeego() - initGin() - initMartini() - initRevel() - initTango() - initTraffic() -} - -// Common -func httpHandlerFunc(w http.ResponseWriter, r *http.Request) {} - -func httpHandlerFuncTest(w http.ResponseWriter, r *http.Request) { - io.WriteString(w, r.RequestURI) -} - -// Ace -func aceHandle(_ *ace.C) {} - -func aceHandleWrite(c *ace.C) { - io.WriteString(c.Writer, c.Param("name")) -} - -func aceHandleTest(c *ace.C) { - io.WriteString(c.Writer, c.Request.RequestURI) -} - -func loadAce(routes []route) http.Handler { - h := []ace.HandlerFunc{aceHandle} - if loadTestHandler { - h = []ace.HandlerFunc{aceHandleTest} - } - - router := ace.New() - for _, route := range routes { - router.Handle(route.method, route.path, h) - } - return router -} - -func loadAceSingle(method, path string, handle ace.HandlerFunc) http.Handler { - router := ace.New() - router.Handle(method, path, []ace.HandlerFunc{handle}) - return router -} - -// bear -func bearHandler(_ http.ResponseWriter, _ *http.Request, _ *bear.Context) {} - -func bearHandlerWrite(w http.ResponseWriter, _ *http.Request, ctx *bear.Context) { - io.WriteString(w, ctx.Params["name"]) -} - -func bearHandlerTest(w http.ResponseWriter, r *http.Request, _ *bear.Context) { - io.WriteString(w, r.RequestURI) -} - -func loadBear(routes []route) http.Handler { - h := bearHandler - if loadTestHandler { - h = bearHandlerTest - } - - router := bear.New() - re := regexp.MustCompile(":([^/]*)") - for _, route := range routes { - switch route.method { - case "GET", "POST", "PUT", "PATCH", "DELETE": - router.On(route.method, re.ReplaceAllString(route.path, "{$1}"), h) - default: - panic("Unknown HTTP method: " + route.method) - } - } - return router -} - -func loadBearSingle(method string, path string, handler bear.HandlerFunc) http.Handler { - router := bear.New() - switch method { - case "GET", "POST", "PUT", "PATCH", "DELETE": - router.On(method, path, handler) - default: - panic("Unknown HTTP method: " + method) - } - return router -} - -// beego -func beegoHandler(ctx *context.Context) {} - -func beegoHandlerWrite(ctx *context.Context) { - ctx.WriteString(ctx.Input.Param(":name")) -} - -func beegoHandlerTest(ctx *context.Context) { - ctx.WriteString(ctx.Request.RequestURI) -} - -func initBeego() { - beego.RunMode = "prod" - beego.BeeLogger.Close() -} - -func loadBeego(routes []route) http.Handler { - h := beegoHandler - if loadTestHandler { - h = beegoHandlerTest - } - - re := regexp.MustCompile(":([^/]*)") - app := beego.NewControllerRegister() - for _, route := range routes { - route.path = re.ReplaceAllString(route.path, ":$1") - switch route.method { - case "GET": - app.Get(route.path, h) - case "POST": - app.Post(route.path, h) - case "PUT": - app.Put(route.path, h) - case "PATCH": - app.Patch(route.path, h) - case "DELETE": - app.Delete(route.path, h) - default: - panic("Unknow HTTP method: " + route.method) - } - } - return app -} - -func loadBeegoSingle(method, path string, handler beego.FilterFunc) http.Handler { - app := beego.NewControllerRegister() - switch method { - case "GET": - app.Get(path, handler) - case "POST": - app.Post(path, handler) - case "PUT": - app.Put(path, handler) - case "PATCH": - app.Patch(path, handler) - case "DELETE": - app.Delete(path, handler) - default: - panic("Unknow HTTP method: " + method) - } - return app -} - -// bone -func boneHandlerWrite(rw http.ResponseWriter, req *http.Request) { - io.WriteString(rw, bone.GetValue(req, "name")) -} - -func loadBone(routes []route) http.Handler { - h := http.HandlerFunc(httpHandlerFunc) - if loadTestHandler { - h = http.HandlerFunc(httpHandlerFuncTest) - } - - router := bone.New() - for _, route := range routes { - switch route.method { - case "GET": - router.Get(route.path, h) - case "POST": - router.Post(route.path, h) - case "PUT": - router.Put(route.path, h) - case "PATCH": - router.Patch(route.path, h) - case "DELETE": - router.Delete(route.path, h) - default: - panic("Unknow HTTP method: " + route.method) - } - } - return router -} - -func loadBoneSingle(method, path string, handler http.Handler) http.Handler { - router := bone.New() - switch method { - case "GET": - router.Get(path, handler) - case "POST": - router.Post(path, handler) - case "PUT": - router.Put(path, handler) - case "PATCH": - router.Patch(path, handler) - case "DELETE": - router.Delete(path, handler) - default: - panic("Unknow HTTP method: " + method) - } - return router -} - -// Denco -func dencoHandler(w http.ResponseWriter, r *http.Request, params denco.Params) {} - -func dencoHandlerWrite(w http.ResponseWriter, r *http.Request, params denco.Params) { - io.WriteString(w, params.Get("name")) -} - -func dencoHandlerTest(w http.ResponseWriter, r *http.Request, params denco.Params) { - io.WriteString(w, r.RequestURI) -} - -func loadDenco(routes []route) http.Handler { - h := dencoHandler - if loadTestHandler { - h = dencoHandlerTest - } - - mux := denco.NewMux() - handlers := make([]denco.Handler, 0, len(routes)) - for _, route := range routes { - handler := mux.Handler(route.method, route.path, h) - handlers = append(handlers, handler) - } - handler, err := mux.Build(handlers) - if err != nil { - panic(err) - } - return handler -} - -func loadDencoSingle(method, path string, h denco.HandlerFunc) http.Handler { - mux := denco.NewMux() - handler, err := mux.Build([]denco.Handler{mux.Handler(method, path, h)}) - if err != nil { - panic(err) - } - return handler -} - -// Echo -func echoHandler(c *echo.Context) error { - return nil -} - -func echoHandlerWrite(c *echo.Context) error { - io.WriteString(c.Response(), c.Param("name")) - return nil -} - -func echoHandlerTest(c *echo.Context) error { - io.WriteString(c.Response(), c.Request().RequestURI) - return nil -} - -func loadEcho(routes []route) http.Handler { - var h interface{} = echoHandler - if loadTestHandler { - h = echoHandlerTest - } - - e := echo.New() - for _, r := range routes { - switch r.method { - case "GET": - e.Get(r.path, h) - case "POST": - e.Post(r.path, h) - case "PUT": - e.Put(r.path, h) - case "PATCH": - e.Patch(r.path, h) - case "DELETE": - e.Delete(r.path, h) - default: - panic("Unknow HTTP method: " + r.method) - } - } - return e -} - -func loadEchoSingle(method, path string, h interface{}) http.Handler { - e := echo.New() - switch method { - case "GET": - e.Get(path, h) - case "POST": - e.Post(path, h) - case "PUT": - e.Put(path, h) - case "PATCH": - e.Patch(path, h) - case "DELETE": - e.Delete(path, h) - default: - panic("Unknow HTTP method: " + method) - } - return e -} - -// Gin -func ginHandle(_ *gin.Context) {} - -func ginHandleWrite(c *gin.Context) { - io.WriteString(c.Writer, c.Params.ByName("name")) -} - -func ginHandleTest(c *gin.Context) { - io.WriteString(c.Writer, c.Request.RequestURI) -} - -func initGin() { - gin.SetMode(gin.ReleaseMode) -} - -func loadGin(routes []route) http.Handler { - h := ginHandle - if loadTestHandler { - h = ginHandleTest - } - - router := gin.New() - for _, route := range routes { - router.Handle(route.method, route.path, h) - } - return router -} - -func loadGinSingle(method, path string, handle gin.HandlerFunc) http.Handler { - router := gin.New() - router.Handle(method, path, handle) - return router -} - -// gocraft/web -type gocraftWebContext struct{} - -func gocraftWebHandler(w web.ResponseWriter, r *web.Request) {} - -func gocraftWebHandlerWrite(w web.ResponseWriter, r *web.Request) { - io.WriteString(w, r.PathParams["name"]) -} - -func gocraftWebHandlerTest(w web.ResponseWriter, r *web.Request) { - io.WriteString(w, r.RequestURI) -} - -func loadGocraftWeb(routes []route) http.Handler { - h := gocraftWebHandler - if loadTestHandler { - h = gocraftWebHandlerTest - } - - router := web.New(gocraftWebContext{}) - for _, route := range routes { - switch route.method { - case "GET": - router.Get(route.path, h) - case "POST": - router.Post(route.path, h) - case "PUT": - router.Put(route.path, h) - case "PATCH": - router.Patch(route.path, h) - case "DELETE": - router.Delete(route.path, h) - default: - panic("Unknow HTTP method: " + route.method) - } - } - return router -} - -func loadGocraftWebSingle(method, path string, handler interface{}) http.Handler { - router := web.New(gocraftWebContext{}) - switch method { - case "GET": - router.Get(path, handler) - case "POST": - router.Post(path, handler) - case "PUT": - router.Put(path, handler) - case "PATCH": - router.Patch(path, handler) - case "DELETE": - router.Delete(path, handler) - default: - panic("Unknow HTTP method: " + method) - } - return router -} - -// goji -func gojiFuncWrite(c goji.C, w http.ResponseWriter, r *http.Request) { - io.WriteString(w, c.URLParams["name"]) -} - -func loadGoji(routes []route) http.Handler { - h := httpHandlerFunc - if loadTestHandler { - h = httpHandlerFuncTest - } - - mux := goji.New() - for _, route := range routes { - switch route.method { - case "GET": - mux.Get(route.path, h) - case "POST": - mux.Post(route.path, h) - case "PUT": - mux.Put(route.path, h) - case "PATCH": - mux.Patch(route.path, h) - case "DELETE": - mux.Delete(route.path, h) - default: - panic("Unknown HTTP method: " + route.method) - } - } - return mux -} - -func loadGojiSingle(method, path string, handler interface{}) http.Handler { - mux := goji.New() - switch method { - case "GET": - mux.Get(path, handler) - case "POST": - mux.Post(path, handler) - case "PUT": - mux.Put(path, handler) - case "PATCH": - mux.Patch(path, handler) - case "DELETE": - mux.Delete(path, handler) - default: - panic("Unknow HTTP method: " + method) - } - return mux -} - -// go-json-rest/rest -func goJsonRestHandler(w rest.ResponseWriter, req *rest.Request) {} - -func goJsonRestHandlerWrite(w rest.ResponseWriter, req *rest.Request) { - io.WriteString(w.(io.Writer), req.PathParam("name")) -} - -func goJsonRestHandlerTest(w rest.ResponseWriter, req *rest.Request) { - io.WriteString(w.(io.Writer), req.RequestURI) -} - -func loadGoJsonRest(routes []route) http.Handler { - h := goJsonRestHandler - if loadTestHandler { - h = goJsonRestHandlerTest - } - - api := rest.NewApi() - restRoutes := make([]*rest.Route, 0, len(routes)) - for _, route := range routes { - restRoutes = append(restRoutes, - &rest.Route{route.method, route.path, h}, - ) - } - router, err := rest.MakeRouter(restRoutes...) - if err != nil { - log.Fatal(err) - } - api.SetApp(router) - return api.MakeHandler() -} - -func loadGoJsonRestSingle(method, path string, hfunc rest.HandlerFunc) http.Handler { - api := rest.NewApi() - router, err := rest.MakeRouter( - &rest.Route{method, path, hfunc}, - ) - if err != nil { - log.Fatal(err) - } - api.SetApp(router) - return api.MakeHandler() -} - -// go-restful -func goRestfulHandler(r *restful.Request, w *restful.Response) {} - -func goRestfulHandlerWrite(r *restful.Request, w *restful.Response) { - io.WriteString(w, r.PathParameter("name")) -} - -func goRestfulHandlerTest(r *restful.Request, w *restful.Response) { - io.WriteString(w, r.Request.RequestURI) -} - -func loadGoRestful(routes []route) http.Handler { - h := goRestfulHandler - if loadTestHandler { - h = goRestfulHandlerTest - } - - re := regexp.MustCompile(":([^/]*)") - - wsContainer := restful.NewContainer() - ws := new(restful.WebService) - - for _, route := range routes { - path := re.ReplaceAllString(route.path, "{$1}") - - switch route.method { - case "GET": - ws.Route(ws.GET(path).To(h)) - case "POST": - ws.Route(ws.POST(path).To(h)) - case "PUT": - ws.Route(ws.PUT(path).To(h)) - case "PATCH": - ws.Route(ws.PATCH(path).To(h)) - case "DELETE": - ws.Route(ws.DELETE(path).To(h)) - default: - panic("Unknow HTTP method: " + route.method) - } - } - wsContainer.Add(ws) - return wsContainer -} - -func loadGoRestfulSingle(method, path string, handler restful.RouteFunction) http.Handler { - wsContainer := restful.NewContainer() - ws := new(restful.WebService) - switch method { - case "GET": - ws.Route(ws.GET(path).To(handler)) - case "POST": - ws.Route(ws.POST(path).To(handler)) - case "PUT": - ws.Route(ws.PUT(path).To(handler)) - case "PATCH": - ws.Route(ws.PATCH(path).To(handler)) - case "DELETE": - ws.Route(ws.DELETE(path).To(handler)) - default: - panic("Unknow HTTP method: " + method) - } - wsContainer.Add(ws) - return wsContainer -} - -// gorilla/mux -func gorillaHandlerWrite(w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - io.WriteString(w, params["name"]) -} - -func loadGorillaMux(routes []route) http.Handler { - h := httpHandlerFunc - if loadTestHandler { - h = httpHandlerFuncTest - } - - re := regexp.MustCompile(":([^/]*)") - m := mux.NewRouter() - for _, route := range routes { - m.HandleFunc( - re.ReplaceAllString(route.path, "{$1}"), - h, - ).Methods(route.method) - } - return m -} - -func loadGorillaMuxSingle(method, path string, handler http.HandlerFunc) http.Handler { - m := mux.NewRouter() - m.HandleFunc(path, handler).Methods(method) - return m -} - -// HttpRouter -func httpRouterHandle(_ http.ResponseWriter, _ *http.Request, _ httprouter.Params) {} - -func httpRouterHandleWrite(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) { - io.WriteString(w, ps.ByName("name")) -} - -func httpRouterHandleTest(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - io.WriteString(w, r.RequestURI) -} - -func loadHttpRouter(routes []route) http.Handler { - h := httpRouterHandle - if loadTestHandler { - h = httpRouterHandleTest - } - - router := httprouter.New() - for _, route := range routes { - router.Handle(route.method, route.path, h) - } - return router -} - -func loadHttpRouterSingle(method, path string, handle httprouter.Handle) http.Handler { - router := httprouter.New() - router.Handle(method, path, handle) - return router -} - -// httpTreeMux -func httpTreeMuxHandler(_ http.ResponseWriter, _ *http.Request, _ map[string]string) {} - -func httpTreeMuxHandlerWrite(w http.ResponseWriter, _ *http.Request, vars map[string]string) { - io.WriteString(w, vars["name"]) -} - -func httpTreeMuxHandlerTest(w http.ResponseWriter, r *http.Request, _ map[string]string) { - io.WriteString(w, r.RequestURI) -} - -func loadHttpTreeMux(routes []route) http.Handler { - h := httpTreeMuxHandler - if loadTestHandler { - h = httpTreeMuxHandlerTest - } - - router := httptreemux.New() - for _, route := range routes { - router.Handle(route.method, route.path, h) - } - return router -} - -func loadHttpTreeMuxSingle(method, path string, handler httptreemux.HandlerFunc) http.Handler { - router := httptreemux.New() - router.Handle(method, path, handler) - return router -} - -// Kocha-urlrouter -type kochaHandler struct { - routerMap map[string]urlrouter.URLRouter - params []urlrouter.Param -} - -func (h *kochaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - meth, params := h.routerMap[r.Method].Lookup(r.URL.Path) - h.params = params - meth.(http.HandlerFunc).ServeHTTP(w, r) -} - -func (h *kochaHandler) Get(w http.ResponseWriter, r *http.Request) {} -func (h *kochaHandler) Post(w http.ResponseWriter, r *http.Request) {} -func (h *kochaHandler) Put(w http.ResponseWriter, r *http.Request) {} -func (h *kochaHandler) Patch(w http.ResponseWriter, r *http.Request) {} -func (h *kochaHandler) Delete(w http.ResponseWriter, r *http.Request) {} -func (h *kochaHandler) kochaHandlerWrite(w http.ResponseWriter, r *http.Request) { - var name string - for _, param := range h.params { - if param.Name == "name" { - name = param.Value - break - } - } - io.WriteString(w, name) -} - -func loadKocha(routes []route) http.Handler { - /*h := httpRouterHandle - if loadTestHandler { - h = httpRouterHandleTest - }*/ - - handler := &kochaHandler{routerMap: map[string]urlrouter.URLRouter{ - "GET": urlrouter.NewURLRouter("doublearray"), - "POST": urlrouter.NewURLRouter("doublearray"), - "PUT": urlrouter.NewURLRouter("doublearray"), - "PATCH": urlrouter.NewURLRouter("doublearray"), - "DELETE": urlrouter.NewURLRouter("doublearray"), - }} - recordMap := make(map[string][]urlrouter.Record) - for _, route := range routes { - var f http.HandlerFunc - switch route.method { - case "GET": - f = handler.Get - case "POST": - f = handler.Post - case "PUT": - f = handler.Put - case "PATCH": - f = handler.Patch - case "DELETE": - f = handler.Delete - } - recordMap[route.method] = append( - recordMap[route.method], - urlrouter.NewRecord(route.path, f), - ) - } - for method, records := range recordMap { - if err := handler.routerMap[method].Build(records); err != nil { - panic(err) - } - } - return handler -} - -func loadKochaSingle(method, path string, handler *kochaHandler, hfunc http.HandlerFunc) http.Handler { - handler.routerMap = map[string]urlrouter.URLRouter{ - method: urlrouter.NewURLRouter("doublearray"), - } - - if err := handler.routerMap[method].Build([]urlrouter.Record{ - urlrouter.NewRecord(path, hfunc), - }); err != nil { - panic(err) - } - return handler -} - -// Macaron -func macaronHandler() {} - -func macaronHandlerWrite(c *macaron.Context) string { - return c.Params("name") -} - -func macaronHandlerTest(c *macaron.Context) string { - return c.Req.RequestURI -} - -func loadMacaron(routes []route) http.Handler { - var h = []macaron.Handler{macaronHandler} - if loadTestHandler { - h[0] = macaronHandlerTest - } - - m := macaron.New() - for _, route := range routes { - m.Handle(route.method, route.path, h) - } - return m -} - -func loadMacaronSingle(method, path string, handler interface{}) http.Handler { - m := macaron.New() - m.Handle(method, path, []macaron.Handler{handler}) - return m -} - -// Martini -func martiniHandler() {} - -func martiniHandlerWrite(params martini.Params) string { - return params["name"] -} - -func initMartini() { - martini.Env = martini.Prod -} - -func loadMartini(routes []route) http.Handler { - var h interface{} = martiniHandler - if loadTestHandler { - h = httpHandlerFuncTest - } - - router := martini.NewRouter() - for _, route := range routes { - switch route.method { - case "GET": - router.Get(route.path, h) - case "POST": - router.Post(route.path, h) - case "PUT": - router.Put(route.path, h) - case "PATCH": - router.Patch(route.path, h) - case "DELETE": - router.Delete(route.path, h) - default: - panic("Unknow HTTP method: " + route.method) - } - } - martini := martini.New() - martini.Action(router.Handle) - return martini -} - -func loadMartiniSingle(method, path string, handler interface{}) http.Handler { - router := martini.NewRouter() - switch method { - case "GET": - router.Get(path, handler) - case "POST": - router.Post(path, handler) - case "PUT": - router.Put(path, handler) - case "PATCH": - router.Patch(path, handler) - case "DELETE": - router.Delete(path, handler) - default: - panic("Unknow HTTP method: " + method) - } - - martini := martini.New() - martini.Action(router.Handle) - return martini -} - -// pat -func patHandlerWrite(w http.ResponseWriter, r *http.Request) { - io.WriteString(w, r.URL.Query().Get(":name")) -} - -func loadPat(routes []route) http.Handler { - h := http.HandlerFunc(httpHandlerFunc) - if loadTestHandler { - h = http.HandlerFunc(httpHandlerFuncTest) - } - - m := pat.New() - for _, route := range routes { - switch route.method { - case "GET": - m.Get(route.path, h) - case "POST": - m.Post(route.path, h) - case "PUT": - m.Put(route.path, h) - case "DELETE": - m.Del(route.path, h) - default: - panic("Unknow HTTP method: " + route.method) - } - } - return m -} - -func loadPatSingle(method, path string, handler http.Handler) http.Handler { - m := pat.New() - switch method { - case "GET": - m.Get(path, handler) - case "POST": - m.Post(path, handler) - case "PUT": - m.Put(path, handler) - case "DELETE": - m.Del(path, handler) - default: - panic("Unknow HTTP method: " + method) - } - return m -} - -// Possum -func possumHandler(c *possum.Context) error { - return nil -} - -func possumHandlerWrite(c *possum.Context) error { - io.WriteString(c.Response, c.Request.URL.Query().Get("name")) - return nil -} - -func possumHandlerTest(c *possum.Context) error { - io.WriteString(c.Response, c.Request.RequestURI) - return nil -} - -func loadPossum(routes []route) http.Handler { - h := possumHandler - if loadTestHandler { - h = possumHandlerTest - } - - router := possum.NewServerMux() - for _, route := range routes { - router.HandleFunc(possumrouter.Simple(route.path), h, possumview.Simple("text/html", "utf-8")) - } - return router -} - -func loadPossumSingle(method, path string, handler possum.HandlerFunc) http.Handler { - router := possum.NewServerMux() - router.HandleFunc(possumrouter.Simple(path), handler, possumview.Simple("text/html", "utf-8")) - return router -} - -// R2router -func r2routerHandler(w http.ResponseWriter, req *http.Request, _ r2router.Params) {} - -func r2routerHandleWrite(w http.ResponseWriter, req *http.Request, params r2router.Params) { - io.WriteString(w, params.Get("name")) -} - -func r2routerHandleTest(w http.ResponseWriter, req *http.Request, _ r2router.Params) { - io.WriteString(w, req.RequestURI) -} - -func loadR2router(routes []route) http.Handler { - h := r2routerHandler - if loadTestHandler { - h = r2routerHandleTest - } - - router := r2router.NewRouter() - for _, r := range routes { - router.AddHandler(r.method, r.path, h) - } - return router -} - -func loadR2routerSingle(method, path string, handler r2router.HandlerFunc) http.Handler { - router := r2router.NewRouter() - router.AddHandler(method, path, handler) - return router -} - -// Revel (Router only) -// In the following code some Revel internals are modelled. -// The original revel code is copyrighted by Rob Figueiredo. -// See https://github.com/revel/revel/blob/master/LICENSE -type RevelController struct { - *revel.Controller - router *revel.Router -} - -func (rc *RevelController) Handle() revel.Result { - return revelResult{} -} - -func (rc *RevelController) HandleWrite() revel.Result { - return rc.RenderText(rc.Params.Get("name")) -} - -func (rc *RevelController) HandleTest() revel.Result { - return rc.RenderText(rc.Request.RequestURI) -} - -type revelResult struct{} - -func (rr revelResult) Apply(req *revel.Request, resp *revel.Response) {} - -func (rc *RevelController) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Dirty hacks, do NOT copy! - revel.MainRouter = rc.router - - upgrade := r.Header.Get("Upgrade") - if upgrade == "websocket" || upgrade == "Websocket" { - panic("Not implemented") - } else { - var ( - req = revel.NewRequest(r) - resp = revel.NewResponse(w) - c = revel.NewController(req, resp) - ) - req.Websocket = nil - revel.Filters[0](c, revel.Filters[1:]) - if c.Result != nil { - c.Result.Apply(req, resp) - } else if c.Response.Status != 0 { - panic("Not implemented") - } - // Close the Writer if we can - if w, ok := resp.Out.(io.Closer); ok { - w.Close() - } - } -} - -func initRevel() { - // Only use the Revel filters required for this benchmark - revel.Filters = []revel.Filter{ - revel.RouterFilter, - revel.ParamsFilter, - revel.ActionInvoker, - } - - revel.RegisterController((*RevelController)(nil), - []*revel.MethodType{ - &revel.MethodType{ - Name: "Handle", - }, - &revel.MethodType{ - Name: "HandleWrite", - }, - &revel.MethodType{ - Name: "HandleTest", - }, - }) -} - -func loadRevel(routes []route) http.Handler { - h := "RevelController.Handle" - if loadTestHandler { - h = "RevelController.HandleTest" - } - - router := revel.NewRouter("") - - // parseRoutes - var rs []*revel.Route - for _, r := range routes { - rs = append(rs, revel.NewRoute(r.method, r.path, h, "", "", 0)) - } - router.Routes = rs - - // updateTree - router.Tree = pathtree.New() - for _, r := range router.Routes { - err := router.Tree.Add(r.TreePath, r) - // Allow GETs to respond to HEAD requests. - if err == nil && r.Method == "GET" { - err = router.Tree.Add("/HEAD"+r.Path, r) - } - // Error adding a route to the pathtree. - if err != nil { - panic(err) - } - } - - rc := new(RevelController) - rc.router = router - return rc -} - -func loadRevelSingle(method, path, action string) http.Handler { - router := revel.NewRouter("") - - route := revel.NewRoute(method, path, action, "", "", 0) - if err := router.Tree.Add(route.TreePath, route); err != nil { - panic(err) - } - - rc := new(RevelController) - rc.router = router - return rc -} - -// Rivet -func rivetHandler() {} - -func rivetHandlerWrite(c rivet.Context) { - c.WriteString(c.GetParams().Get("name")) -} - -func rivetHandlerTest(c rivet.Context) { - c.WriteString(c.Request().RequestURI) -} - -func loadRivet(routes []route) http.Handler { - var h interface{} = rivetHandler - if loadTestHandler { - h = rivetHandlerTest - } - - router := rivet.NewRouter(nil) - for _, route := range routes { - router.Handle(route.method, route.path, h) - } - return router -} - -func loadRivetSingle(method, path string, handler interface{}) http.Handler { - router := rivet.NewRouter(nil) - - router.Handle(method, path, handler) - - return router -} - -// Tango -func tangoHandler(ctx *tango.Context) {} - -func tangoHandlerWrite(ctx *tango.Context) { - ctx.Write([]byte(ctx.Params().Get(":name"))) -} - -func tangoHandlerTest(ctx *tango.Context) { - ctx.Write([]byte(ctx.Req().RequestURI)) -} - -func initTango() { - llog.SetOutput(new(mockResponseWriter)) - llog.SetOutputLevel(llog.Lnone) -} - -func loadTango(routes []route) http.Handler { - h := tangoHandler - if loadTestHandler { - h = tangoHandlerTest - } - - tg := tango.NewWithLog(llog.Std) - for _, route := range routes { - tg.Route(route.method, route.path, h) - } - return tg -} - -func loadTangoSingle(method, path string, handler func(*tango.Context)) http.Handler { - tg := tango.NewWithLog(llog.Std) - tg.Route(method, path, handler) - return tg -} - -// Tiger Tonic -func tigerTonicHandlerWrite(w http.ResponseWriter, r *http.Request) { - io.WriteString(w, r.URL.Query().Get("name")) -} - -func loadTigerTonic(routes []route) http.Handler { - h := httpHandlerFunc - if loadTestHandler { - h = httpHandlerFuncTest - } - - re := regexp.MustCompile(":([^/]*)") - mux := tigertonic.NewTrieServeMux() - for _, route := range routes { - mux.HandleFunc(route.method, re.ReplaceAllString(route.path, "{$1}"), h) - } - return mux -} - -func loadTigerTonicSingle(method, path string, handler http.HandlerFunc) http.Handler { - mux := tigertonic.NewTrieServeMux() - mux.HandleFunc(method, path, handler) - return mux -} - -// Traffic -func trafficHandler(w traffic.ResponseWriter, r *traffic.Request) {} - -func trafficHandlerWrite(w traffic.ResponseWriter, r *traffic.Request) { - io.WriteString(w, r.URL.Query().Get("name")) -} - -func trafficHandlerTest(w traffic.ResponseWriter, r *traffic.Request) { - io.WriteString(w, r.RequestURI) -} - -func initTraffic() { - traffic.SetVar("env", "bench") -} - -func loadTraffic(routes []route) http.Handler { - h := trafficHandler - if loadTestHandler { - h = trafficHandlerTest - } - - router := traffic.New() - for _, route := range routes { - switch route.method { - case "GET": - router.Get(route.path, h) - case "POST": - router.Post(route.path, h) - case "PUT": - router.Put(route.path, h) - case "PATCH": - router.Patch(route.path, h) - case "DELETE": - router.Delete(route.path, h) - default: - panic("Unknow HTTP method: " + route.method) - } - } - return router -} - -func loadTrafficSingle(method, path string, handler traffic.HttpHandleFunc) http.Handler { - router := traffic.New() - switch method { - case "GET": - router.Get(path, handler) - case "POST": - router.Post(path, handler) - case "PUT": - router.Put(path, handler) - case "PATCH": - router.Patch(path, handler) - case "DELETE": - router.Delete(path, handler) - default: - panic("Unknow HTTP method: " + method) - } - return router -} - -// Mailgun Vulcan -func vulcanHandler(w http.ResponseWriter, r *http.Request) {} - -func vulcanHandlerWrite(w http.ResponseWriter, r *http.Request) { - io.WriteString(w, r.URL.Query().Get("name")) -} - -func loadVulcan(routes []route) http.Handler { - h := httpHandlerFunc - if loadTestHandler { - h = httpHandlerFuncTest - } - - re := regexp.MustCompile(":([^/]*)") - mux := vulcan.NewMux() - for _, route := range routes { - path := re.ReplaceAllString(route.path, "<$1>") - expr := fmt.Sprintf(`Method("%s") && Path("%s")`, route.method, path) - if err := mux.HandleFunc(expr, h); err != nil { - panic(err) - } - } - return mux -} - -func loadVulcanSingle(method, path string, handler http.HandlerFunc) http.Handler { - re := regexp.MustCompile(":([^/]*)") - mux := vulcan.NewMux() - path = re.ReplaceAllString(path, "<$1>") - expr := fmt.Sprintf(`Method("%s") && Path("%s")`, method, path) - if err := mux.HandleFunc(expr, httpHandlerFunc); err != nil { - panic(err) - } - return mux -} - -// Zeus -// func zeusHandlerWrite(w http.ResponseWriter, r *http.Request) { -// io.WriteString(w, zeus.Var(r, "name")) -// } - -// func loadZeus(routes []route) http.Handler { -// h := http.HandlerFunc(httpHandlerFunc) -// if loadTestHandler { -// h = http.HandlerFunc(httpHandlerFuncTest) -// } - -// m := zeus.New() -// for _, route := range routes { -// switch route.method { -// case "GET": -// m.GET(route.path, h) -// case "POST": -// m.POST(route.path, h) -// case "PUT": -// m.PUT(route.path, h) -// case "DELETE": -// m.DELETE(route.path, h) -// default: -// panic("Unknow HTTP method: " + route.method) -// } -// } -// return m -// } - -// func loadZeusSingle(method, path string, handler http.HandlerFunc) http.Handler { -// m := zeus.New() -// switch method { -// case "GET": -// m.GET(path, handler) -// case "POST": -// m.POST(path, handler) -// case "PUT": -// m.PUT(path, handler) -// case "DELETE": -// m.DELETE(path, handler) -// default: -// panic("Unknow HTTP method: " + method) -// } -// return m -// } - -// Usage notice -func main() { - fmt.Println("Usage: go test -bench=. -timeout=20m") - os.Exit(1) -} diff --git a/routers_test.go b/routers_test.go deleted file mode 100644 index b5f1aec3..00000000 --- a/routers_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "net/http" - "net/http/httptest" - "testing" -) - -var ( - // load functions of all routers - routers = []struct { - name string - load func(routes []route) http.Handler - }{ - {"Ace", loadAce}, - {"Bear", loadBear}, - {"Beego", loadBeego}, - {"Bone", loadBone}, - {"Denco", loadDenco}, - {"Echo", loadEcho}, - {"Gin", loadGin}, - {"GocraftWeb", loadGocraftWeb}, - {"Goji", loadGoji}, - {"GoJsonRest", loadGoJsonRest}, - {"GoRestful", loadGoRestful}, - {"GorillaMux", loadGorillaMux}, - {"HttpRouter", loadHttpRouter}, - {"HttpTreeMux", loadHttpTreeMux}, - //{"Kocha", loadKocha}, - {"Macaron", loadMacaron}, - {"Martini", loadMartini}, - {"Pat", loadPat}, - {"Possum", loadPossum}, - {"R2router", loadR2router}, - {"Revel", loadRevel}, - {"Rivet", loadRivet}, - {"Tango", loadTango}, - {"TigerTonic", loadTigerTonic}, - {"Traffic", loadTraffic}, - {"Vulcan", loadVulcan}, - // {"Zeus", loadZeus}, - } - - // all APIs - apis = []struct { - name string - routes []route - }{ - {"GitHub", githubAPI}, - {"GPlus", gplusAPI}, - {"Parse", parseAPI}, - {"Static", staticRoutes}, - } -) - -func TestRouters(t *testing.T) { - loadTestHandler = true - - for _, router := range routers { - req, _ := http.NewRequest("GET", "/", nil) - u := req.URL - rq := u.RawQuery - - for _, api := range apis { - r := router.load(api.routes) - - for _, route := range api.routes { - w := httptest.NewRecorder() - req.Method = route.method - req.RequestURI = route.path - u.Path = route.path - u.RawQuery = rq - r.ServeHTTP(w, req) - if w.Code != 200 || w.Body.String() != route.path { - t.Errorf( - "%s in API %s: %d - %s; expected %s %s\n", - router.name, api.name, w.Code, w.Body.String(), route.method, route.path, - ) - } - } - } - } - - loadTestHandler = false -} diff --git a/runner.go b/runner.go new file mode 100644 index 00000000..4d179964 --- /dev/null +++ b/runner.go @@ -0,0 +1,647 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "os" + "runtime" + "sort" + "strings" + "time" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" + "github.com/julienschmidt/go-http-routing-benchmark/driver/ui" + + _ "github.com/julienschmidt/go-http-routing-benchmark/router" + _ "github.com/julienschmidt/go-http-routing-benchmark/suite" +) + +const highlight = 3 + +var ( + std = flag.Bool("std", false, "use default benchmark output") + test = flag.Bool("test", false, "only run tests and not benchmarks") + list = flag.Bool("list", false, "print the names of all registered Routers") + markdown = flag.String("markdown", "", "write the benchmark output in markdown format to a file") + color = flag.String("color", "on", "colorize output, valid options are 'on', 'off' and 'ansi'") + duration = flag.Duration("time", 5*time.Second, "approximate run time for each benchmark") +) + +var ( + mdfile *os.File + mdbuf *bytes.Buffer +) + +func init() { + // beego sets it to runtime.NumCPU() and as of Go1.5 the default + // value may change anyway. Currently none of the contesters does + // concurrent routing. + runtime.GOMAXPROCS(1) + + // Disable log output + log.SetOutput(&driver.ResponseStub{}) +} + +func main() { + flag.Parse() + flag.Set("test.benchtime", duration.String()) + + isvalid := false + for _, op := range []string{"on", "off", "ansi"} { + if *color == op { + isvalid = true + } + } + if !isvalid { + fmt.Fprint(os.Stderr, "error: test: no packages registered\n") + os.Exit(1) + } + + if *list { + fmt.Print(strings.Join(driver.PackageNames(), "\n") + "\n") + os.Exit(0) + } + + console := ui.Console(*color) + console.Print("Running Tests...\n\n") + tests := driver.RunTests() + if tests != nil { + RenderTest(console, tests) + os.Exit(2) + } + + if *test { + os.Exit(0) + } + + if *markdown != "" { + f, err := os.Create(*markdown) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %s\n", err.Error()) + os.Exit(1) + } + + mdfile = f + mdbuf = &bytes.Buffer{} + } + + length := driver.NumPackages() + renderer := &Renderer{ + span: &Span{ + Names: make([]string, length), + Homepages: make([]string, length), + + HeapMemory: &markUint64{marker: &marker{marked: make([]int, length)}}, + + NsPerOp: &markInt64{marker: &marker{marked: make([]int, length)}}, + AllocsPerOp: &markInt64{marker: &marker{marked: make([]int, length)}}, + BytesPerOp: &markInt64{marker: &marker{marked: make([]int, length)}}, + }, + + NsPerOp: make([]int64, length), + BytesPerOp: make([]int64, length), + AllocsPerOp: make([]int64, length), + } + + br := &BenchmarkRenderer{ + mark: console.Green, + color: console.Yellow, + } + renderer.Add(br.Render) + + if *std { + br.std = true + br.mark = console.Print + } + + console.Print("Running Benchmarks...\n\n") + benchmarks := driver.RunBenchmarks() + + if *markdown != "" { + mdbuf.WriteString("Results\n=======\n\n\n") + RenderMemoryTable(benchmarks) + renderer.Add(RenderMarkdown) + } + + for _, result := range benchmarks { + renderer.Render(result) + } + + if mdfile != nil { + // Create router name links + urls := make(map[string]string) + for _, result := range benchmarks { + for _, pkg := range result.Packages() { + urls[pkg.Name] = pkg.Homepage + } + } + for name, url := range urls { + if url != "" { + fmt.Fprintf(mdbuf, "[%s]: %s\n", name, url) + } + } + + mdfile.Write(mdbuf.Bytes()) + mdfile.Close() + } +} + +func RenderMemoryTable(b []*driver.Result) { + tcw := []int{4} // We'll add 2 later anyway + thd := []string{"Router"} + num := driver.NumPackages() + rts := driver.PackageNames() + col := make([]*markUint64, num) + + for i, name := range rts { + col[i] = &markUint64{ + zero: true, + data: make([]uint64, len(b)), + marker: &marker{marked: make([]int, len(b))}, + } + + if tcw[0] < len(name) { + tcw[0] = len(name) + } + } + + for _, r := range b { + bname := r.Name() + if !strings.HasSuffix(bname, "All") { + continue + } + + x := len(bname) - 3 + thd = append(thd, bname[:x]) + if x < 9 { + x = 9 + } + tcw = append(tcw, x) + + for i, name := range rts { + pkgs := r.Packages() + mems := r.RouterMemory() + + for x, pkg := range pkgs { + if pkg.Name == name { + col[i].data[len(thd)-2] = mems[x] + } + } + } + } + + fmt.Fprint(mdbuf, "### Memory Consumption\n\n\n") + + for _, m := range col { + m.Set(m.data[:]) + } + + var header string + for i, cell := range thd { + tcw[i] += 2 + header += fmt.Sprintf("| %-*s ", tcw[i], cell) + } + header += "|\n" + for i, x := range tcw { + if i == 0 { + header += fmt.Sprintf("|:%s-", strings.Repeat("-", x)) + } else { + header += fmt.Sprintf("|-%s:", strings.Repeat("-", x)) + } + } + fmt.Fprint(mdbuf, header+"|\n") + + for i, name := range rts { + fmt.Fprintf(mdbuf, "| %-*s ", tcw[0], "["+name+"]") + + for x, width := range tcw[1:] { + var memory string + if col[i].data[x] == 0 { + memory = "- " + } else { + if col[i].IsMarked(x) { + memory = fmt.Sprintf("__%d__", col[i].data[x]) + } else { + memory = fmt.Sprintf("%d ", col[i].data[x]) + } + } + + fmt.Fprintf(mdbuf, "| %*s", width+1, memory) + } + + fmt.Fprint(mdbuf, "|\n") + } + + fmt.Fprint(mdbuf, "\n\n") +} + +type Renderer struct { + span *Span + funcs []func(*driver.Result, *Span) + + // Slice cache + NsPerOp []int64 + BytesPerOp []int64 + AllocsPerOp []int64 +} + +func (r *Renderer) Add(fn func(*driver.Result, *Span)) { + r.funcs = append(r.funcs, fn) +} + +func (r *Renderer) Render(result *driver.Result) { + span := r.span + pkgs := result.Packages() + slen := len(pkgs) + + for i, pkg := range pkgs { + span.Names[i] = pkg.Name + span.Homepages[i] = pkg.Homepage + } + + benchmarks := result.Results() + for i, b := range benchmarks { + r.NsPerOp[i] = b.NsPerOp() + r.AllocsPerOp[i] = b.AllocsPerOp() + r.BytesPerOp[i] = b.AllocedBytesPerOp() + } + + span.length = slen + span.NsPerOp.Set(r.NsPerOp[:slen]) + span.BytesPerOp.Set(r.BytesPerOp[:slen]) + span.AllocsPerOp.Set(r.AllocsPerOp[:slen]) + span.HeapMemory.Set(result.RouterMemory()) + + for _, fn := range r.funcs { + fn(result, span) + } +} + +type BenchmarkRenderer struct { + std bool + mark func(string) + color func(string) +} + +func (b *BenchmarkRenderer) Render(r *driver.Result, s *Span) { + if !b.std { + b.color(fmt.Sprintf("%s:\n%s\n\n", r.Name(), r.Description())) + } + + packages := r.Packages() + benchmarks := r.Results() + + colum := 0 + for _, pkg := range packages { + if colum < len(pkg.Name) { + colum = len(pkg.Name) + } + } + + if b.std { + colum += 9 + len(r.Name()) + } + + for i := 0; i < s.length; i++ { + var mwd int + + name := packages[i].Name + if b.std { + name = "Benchmark" + name + r.Name() + "\t" + } + + if b.std { + fmt.Printf("%-*s %8d ", colum, name, benchmarks[i].N) + } else { + fmt.Printf("%-*s ", colum, name) + + if s.BytesPerOp.IsMarked(i) { + b.mark(fmt.Sprintf("%8d", s.HeapMemory.data[i])) + } else { + fmt.Printf("%8d", s.HeapMemory.data[i]) + } + fmt.Print(" B ") + } + + var nsopstr string + nsop := s.NsPerOp.data[i] + if benchmarks[i].N > 0 && nsop < 100 { + if nsop < 10 { + mwd = 3 + nsopstr = fmt.Sprintf("%13.2f", float64(benchmarks[i].T.Nanoseconds())/float64(benchmarks[i].N)) + } else { + mwd = 2 + nsopstr = fmt.Sprintf("%12.1f", float64(benchmarks[i].T.Nanoseconds())/float64(benchmarks[i].N)) + } + } else { + nsopstr = fmt.Sprintf("%10d", nsop) + } + + if s.NsPerOp.IsMarked(i) { + b.mark(nsopstr) + } else { + fmt.Print(nsopstr) + } + fmt.Print(" ns/op ") + + if s.BytesPerOp.IsMarked(i) { + b.mark(fmt.Sprintf("%*d", 10-mwd, s.BytesPerOp.data[i])) + } else { + fmt.Printf("%*d", 10-mwd, s.BytesPerOp.data[i]) + } + fmt.Print(" B/op ") + + if s.AllocsPerOp.IsMarked(i) { + b.mark(fmt.Sprintf("%8d", s.AllocsPerOp.data[i])) + } else { + fmt.Printf("%8d", s.AllocsPerOp.data[i]) + } + fmt.Print(" allocs/op\n") + } + + if !b.std { + fmt.Print("\n\n") + } +} + +var widths = []int{9 /*Memory*/, 10 /*Iterations*/, 10 /*Ns*/, 8 /*B*/, 10 /*Allocs*/} + +func RenderMarkdown(r *driver.Result, s *Span) { + fmt.Fprintf(mdbuf, "#### %s\n\n%s\n\n", r.Name(), r.Description()) + + packages := r.Packages() + benchmarks := r.Results() + + c := 4 // We'll add 2 later anyway + w := widths + for _, pkg := range packages { + if c < len(pkg.Name) { + c = len(pkg.Name) + } + } + c += 2 + + fmt.Fprintf(mdbuf, "| %-*s | %-*s | %-*s | %-*s | %-*s | %-*s |\n", + c, "Router", w[0], "Memory", w[1], "Iterations", w[2], "Ns/Op", w[3], "Bytes/Op", w[4], "Allocs/Op", + ) + fmt.Fprintf(mdbuf, "|:%s-|-%s-:|-%s:|-%s-:|-%s-:|-%s-:|\n", + strings.Repeat("-", c), strings.Repeat("-", w[0]), strings.Repeat("-", w[1]), + strings.Repeat("-", w[2]), strings.Repeat("-", w[3]), strings.Repeat("-", w[4]), + ) + + for i := 0; i < s.length; i++ { + + var ( + nw int + name string + memory string + nsop string + bop string + allocs string + ) + + if packages[i].Homepage != "" { + nw = c + name = "[" + packages[i].Name + "]" + } else { + nw = c + 2 + name = packages[i].Name + } + + if s.BytesPerOp.IsMarked(i) { + memory = fmt.Sprintf("__%d__", s.HeapMemory.data[i]) + } else { + memory = fmt.Sprintf("%d ", s.HeapMemory.data[i]) + } + + ns := s.NsPerOp.data[i] + if benchmarks[i].N > 0 && ns < 100 { + if ns < 10 { + nsop = fmt.Sprintf("%.2f", float64(benchmarks[i].T.Nanoseconds())/float64(benchmarks[i].N)) + } else { + nsop = fmt.Sprintf("%.1f", float64(benchmarks[i].T.Nanoseconds())/float64(benchmarks[i].N)) + } + } else { + nsop = fmt.Sprintf("%d", ns) + } + if s.NsPerOp.IsMarked(i) { + nsop = fmt.Sprintf("__%s__", nsop) + } else { + nsop = fmt.Sprintf("%s ", nsop) + } + + if s.BytesPerOp.IsMarked(i) { + bop = fmt.Sprintf("__%d__", s.BytesPerOp.data[i]) + } else { + bop = fmt.Sprintf("%d ", s.BytesPerOp.data[i]) + } + + if s.AllocsPerOp.IsMarked(i) { + allocs = fmt.Sprintf("__%d__", s.AllocsPerOp.data[i]) + } else { + allocs = fmt.Sprintf("%d ", s.AllocsPerOp.data[i]) + } + + fmt.Fprintf(mdbuf, "| %-*s | %*s| %*d | %*s| %*s| %*s|\n", + nw, name, w[0]+2, memory, w[1], benchmarks[i].N, w[2]+2, nsop, w[3]+2, bop, w[4]+2, allocs, + ) + } + + mdbuf.WriteString("\n\n\n") +} + +func RenderTest(ui ui.ConsoleAdapter, tests []*driver.Assertion) { + // TODO: Improve dedup suppression implementation. + var l *driver.Assertion + + for i, t := range tests { + n := i + 1 + + if l == nil || l.Benchmark() != t.Benchmark() { + l = nil + print("\n\n", t.Benchmark(), "\n\n", ui.Print, ui.Yellow) + } + + if l == nil { + print("Test: ", t.Description(), "\n", ui.Print, ui.Print) + + if t.Error != "" { + if len(tests) > n { + if tests[n].Benchmark() != t.Benchmark() || tests[n].Error != t.Error { + print("Error: ", t.Error, "\n", ui.Print, ui.Red) + } + } else { + print("Error: ", t.Error, "\n", ui.Print, ui.Red) + } + + print("Router: ", t.Router(), "\n", ui.Print, ui.Yellow) + } else { + print("Expect: ", t.Expect, "\n", ui.Print, ui.Green) + print("Router: ", t.Router(), "\n", ui.Print, ui.Yellow) + + if len(tests) > n { + if tests[n].Benchmark() != t.Benchmark() || tests[n].Actual != t.Actual { + print("Actual: ", t.Actual, "\n", ui.Print, ui.Red) + } + } else { + print("Actual: ", t.Actual, "\n", ui.Print, ui.Red) + } + } + } else if len(tests) == n { + if t.Error != "" { + print("Error: ", t.Error, "\n", ui.Print, ui.Red) + } else { + print("Actual: ", t.Actual, "\n", ui.Print, ui.Red) + } + } else { + if l.Description() != t.Description() { + print("Test: ", t.Description(), "\n", ui.Print, ui.Print) + } + if l.Router() != t.Router() { + print("Router: ", t.Router(), "\n", ui.Print, ui.Yellow) + } + if t.Error != "" && l.Error != t.Error { + print("Error: ", t.Error, "\n", ui.Print, ui.Red) + } + if l.Expect != t.Expect { + print("Expect: ", t.Expect, "\n", ui.Print, ui.Green) + } + if l.Actual != t.Actual { + print("Actual: ", t.Actual, "\n", ui.Print, ui.Red) + } + } + + l = t + } +} + +type Span struct { + length int + + Names []string // Package names + Homepages []string // Package homepage URLs + + HeapMemory *markUint64 // Heap memory allocated for the router object + + NsPerOp *markInt64 // testing.B NsPerOp + AllocsPerOp *markInt64 // testing.B AllocsPerOp + BytesPerOp *markInt64 // testing.B AllocBytesPerOp +} + +type markInt64 struct { + *marker + data []int64 +} + +func (m *markInt64) Less(i, j int) bool { return m.data[i] < m.data[j] } +func (m *markInt64) cmp(i, j int) bool { return m.data[i] != m.data[j] } +func (m *markInt64) IsMarked(i int) bool { return m.marker.isMarked(m.cmp, i) } + +func (m *markInt64) Set(data []int64) { + m.data = data + m.marker.length = len(data) + m.marker.set() + sort.Sort(m) +} + +type markUint64 struct { + *marker + zero bool // Ignore zero values + data []uint64 +} + +func (m *markUint64) cmp(i, j int) bool { return m.data[i] != m.data[j] } +func (m *markUint64) IsMarked(i int) bool { return m.marker.isMarked(m.cmp, i) } + +func (m *markUint64) Less(i, j int) bool { + if m.zero { + return true + } + + return m.data[i] < m.data[j] +} + +func (m *markUint64) Set(data []uint64) { + m.data = data + m.marker.length = len(data) + m.marker.set() + sort.Sort(m) +} + +type marker struct { + number int + length int + marked []int +} + +func (m *marker) Len() int { return m.length } +func (m *marker) Swap(i, j int) { m.marked[i], m.marked[j] = m.marked[j], m.marked[i] } + +func (m *marker) set() { + m.number = -1 + + if m.length > len(m.marked) { + m.marked = make([]int, m.length) + } + + for i := 0; i < m.length; i++ { + m.marked[i] = i + } +} + +func (m *marker) isMarked(cmp func(int, int) bool, i int) bool { + if m.number == -1 { + m.markable(cmp, i) + } + + for x := 0; x < m.number; x++ { + if m.marked[x] == i { + return true + } + } + + return false +} + +func (m *marker) markable(cmp func(int, int) bool, i int) { + m.number = highlight + + if m.length < highlight { + m.number = int(m.length / 2) + } + + if m.number <= 1 { + if m.length > 1 && !cmp(0, 1) { + m.number = 0 + } + return + } + + n := m.number + c := m.number - 1 + + for { + ni := m.marked[n] + ci := m.marked[c] + + if cmp(ci, ni) { + break + } + + n++ + c++ + m.number++ + } +} + +func print(p, m, s string, w, c func(string)) { + w(p) + c(m) + w(s) +} diff --git a/static_test.go b/static_test.go deleted file mode 100644 index f7a5f85a..00000000 --- a/static_test.go +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright 2013 Julien Schmidt. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be found -// in the LICENSE file. - -package main - -import ( - "net/http" - "testing" -) - -var staticRoutes = []route{ - {"GET", "/"}, - {"GET", "/cmd.html"}, - {"GET", "/code.html"}, - {"GET", "/contrib.html"}, - {"GET", "/contribute.html"}, - {"GET", "/debugging_with_gdb.html"}, - {"GET", "/docs.html"}, - {"GET", "/effective_go.html"}, - {"GET", "/files.log"}, - {"GET", "/gccgo_contribute.html"}, - {"GET", "/gccgo_install.html"}, - {"GET", "/go-logo-black.png"}, - {"GET", "/go-logo-blue.png"}, - {"GET", "/go-logo-white.png"}, - {"GET", "/go1.1.html"}, - {"GET", "/go1.2.html"}, - {"GET", "/go1.html"}, - {"GET", "/go1compat.html"}, - {"GET", "/go_faq.html"}, - {"GET", "/go_mem.html"}, - {"GET", "/go_spec.html"}, - {"GET", "/help.html"}, - {"GET", "/ie.css"}, - {"GET", "/install-source.html"}, - {"GET", "/install.html"}, - {"GET", "/logo-153x55.png"}, - {"GET", "/Makefile"}, - {"GET", "/root.html"}, - {"GET", "/share.png"}, - {"GET", "/sieve.gif"}, - {"GET", "/tos.html"}, - {"GET", "/articles/"}, - {"GET", "/articles/go_command.html"}, - {"GET", "/articles/index.html"}, - {"GET", "/articles/wiki/"}, - {"GET", "/articles/wiki/edit.html"}, - {"GET", "/articles/wiki/final-noclosure.go"}, - {"GET", "/articles/wiki/final-noerror.go"}, - {"GET", "/articles/wiki/final-parsetemplate.go"}, - {"GET", "/articles/wiki/final-template.go"}, - {"GET", "/articles/wiki/final.go"}, - {"GET", "/articles/wiki/get.go"}, - {"GET", "/articles/wiki/http-sample.go"}, - {"GET", "/articles/wiki/index.html"}, - {"GET", "/articles/wiki/Makefile"}, - {"GET", "/articles/wiki/notemplate.go"}, - {"GET", "/articles/wiki/part1-noerror.go"}, - {"GET", "/articles/wiki/part1.go"}, - {"GET", "/articles/wiki/part2.go"}, - {"GET", "/articles/wiki/part3-errorhandling.go"}, - {"GET", "/articles/wiki/part3.go"}, - {"GET", "/articles/wiki/test.bash"}, - {"GET", "/articles/wiki/test_edit.good"}, - {"GET", "/articles/wiki/test_Test.txt.good"}, - {"GET", "/articles/wiki/test_view.good"}, - {"GET", "/articles/wiki/view.html"}, - {"GET", "/codewalk/"}, - {"GET", "/codewalk/codewalk.css"}, - {"GET", "/codewalk/codewalk.js"}, - {"GET", "/codewalk/codewalk.xml"}, - {"GET", "/codewalk/functions.xml"}, - {"GET", "/codewalk/markov.go"}, - {"GET", "/codewalk/markov.xml"}, - {"GET", "/codewalk/pig.go"}, - {"GET", "/codewalk/popout.png"}, - {"GET", "/codewalk/run"}, - {"GET", "/codewalk/sharemem.xml"}, - {"GET", "/codewalk/urlpoll.go"}, - {"GET", "/devel/"}, - {"GET", "/devel/release.html"}, - {"GET", "/devel/weekly.html"}, - {"GET", "/gopher/"}, - {"GET", "/gopher/appenginegopher.jpg"}, - {"GET", "/gopher/appenginegophercolor.jpg"}, - {"GET", "/gopher/appenginelogo.gif"}, - {"GET", "/gopher/bumper.png"}, - {"GET", "/gopher/bumper192x108.png"}, - {"GET", "/gopher/bumper320x180.png"}, - {"GET", "/gopher/bumper480x270.png"}, - {"GET", "/gopher/bumper640x360.png"}, - {"GET", "/gopher/doc.png"}, - {"GET", "/gopher/frontpage.png"}, - {"GET", "/gopher/gopherbw.png"}, - {"GET", "/gopher/gophercolor.png"}, - {"GET", "/gopher/gophercolor16x16.png"}, - {"GET", "/gopher/help.png"}, - {"GET", "/gopher/pkg.png"}, - {"GET", "/gopher/project.png"}, - {"GET", "/gopher/ref.png"}, - {"GET", "/gopher/run.png"}, - {"GET", "/gopher/talks.png"}, - {"GET", "/gopher/pencil/"}, - {"GET", "/gopher/pencil/gopherhat.jpg"}, - {"GET", "/gopher/pencil/gopherhelmet.jpg"}, - {"GET", "/gopher/pencil/gophermega.jpg"}, - {"GET", "/gopher/pencil/gopherrunning.jpg"}, - {"GET", "/gopher/pencil/gopherswim.jpg"}, - {"GET", "/gopher/pencil/gopherswrench.jpg"}, - {"GET", "/play/"}, - {"GET", "/play/fib.go"}, - {"GET", "/play/hello.go"}, - {"GET", "/play/life.go"}, - {"GET", "/play/peano.go"}, - {"GET", "/play/pi.go"}, - {"GET", "/play/sieve.go"}, - {"GET", "/play/solitaire.go"}, - {"GET", "/play/tree.go"}, - {"GET", "/progs/"}, - {"GET", "/progs/cgo1.go"}, - {"GET", "/progs/cgo2.go"}, - {"GET", "/progs/cgo3.go"}, - {"GET", "/progs/cgo4.go"}, - {"GET", "/progs/defer.go"}, - {"GET", "/progs/defer.out"}, - {"GET", "/progs/defer2.go"}, - {"GET", "/progs/defer2.out"}, - {"GET", "/progs/eff_bytesize.go"}, - {"GET", "/progs/eff_bytesize.out"}, - {"GET", "/progs/eff_qr.go"}, - {"GET", "/progs/eff_sequence.go"}, - {"GET", "/progs/eff_sequence.out"}, - {"GET", "/progs/eff_unused1.go"}, - {"GET", "/progs/eff_unused2.go"}, - {"GET", "/progs/error.go"}, - {"GET", "/progs/error2.go"}, - {"GET", "/progs/error3.go"}, - {"GET", "/progs/error4.go"}, - {"GET", "/progs/go1.go"}, - {"GET", "/progs/gobs1.go"}, - {"GET", "/progs/gobs2.go"}, - {"GET", "/progs/image_draw.go"}, - {"GET", "/progs/image_package1.go"}, - {"GET", "/progs/image_package1.out"}, - {"GET", "/progs/image_package2.go"}, - {"GET", "/progs/image_package2.out"}, - {"GET", "/progs/image_package3.go"}, - {"GET", "/progs/image_package3.out"}, - {"GET", "/progs/image_package4.go"}, - {"GET", "/progs/image_package4.out"}, - {"GET", "/progs/image_package5.go"}, - {"GET", "/progs/image_package5.out"}, - {"GET", "/progs/image_package6.go"}, - {"GET", "/progs/image_package6.out"}, - {"GET", "/progs/interface.go"}, - {"GET", "/progs/interface2.go"}, - {"GET", "/progs/interface2.out"}, - {"GET", "/progs/json1.go"}, - {"GET", "/progs/json2.go"}, - {"GET", "/progs/json2.out"}, - {"GET", "/progs/json3.go"}, - {"GET", "/progs/json4.go"}, - {"GET", "/progs/json5.go"}, - {"GET", "/progs/run"}, - {"GET", "/progs/slices.go"}, - {"GET", "/progs/timeout1.go"}, - {"GET", "/progs/timeout2.go"}, - {"GET", "/progs/update.bash"}, -} - -var ( - staticHttpServeMux http.Handler - - staticAce http.Handler - staticBear http.Handler - staticBeego http.Handler - staticBone http.Handler - staticDenco http.Handler - staticEcho http.Handler - staticGin http.Handler - staticGocraftWeb http.Handler - staticGoji http.Handler - staticGoJsonRest http.Handler - staticGoRestful http.Handler - staticGorillaMux http.Handler - staticHttpRouter http.Handler - staticHttpTreeMux http.Handler - staticKocha http.Handler - staticMacaron http.Handler - staticMartini http.Handler - staticPat http.Handler - staticPossum http.Handler - staticR2router http.Handler - staticRevel http.Handler - staticRivet http.Handler - staticTango http.Handler - staticTigerTonic http.Handler - staticTraffic http.Handler - staticVulcan http.Handler - // staticZeus http.Handler -) - -func init() { - println("#Static Routes:", len(staticRoutes)) - - calcMem("HttpServeMux", func() { - serveMux := http.NewServeMux() - for _, route := range staticRoutes { - serveMux.HandleFunc(route.path, httpHandlerFunc) - } - staticHttpServeMux = serveMux - }) - - calcMem("Ace", func() { - staticAce = loadAce(staticRoutes) - }) - calcMem("Bear", func() { - staticBear = loadBear(staticRoutes) - }) - calcMem("Beego", func() { - staticBeego = loadBeego(staticRoutes) - }) - calcMem("Bone", func() { - staticBone = loadBone(staticRoutes) - }) - calcMem("Denco", func() { - staticDenco = loadDenco(staticRoutes) - }) - calcMem("Echo", func() { - staticEcho = loadEcho(staticRoutes) - }) - calcMem("Gin", func() { - staticGin = loadGin(staticRoutes) - }) - calcMem("GocraftWeb", func() { - staticGocraftWeb = loadGocraftWeb(staticRoutes) - }) - calcMem("Goji", func() { - staticGoji = loadGoji(staticRoutes) - }) - calcMem("GoJsonRest", func() { - staticGoJsonRest = loadGoJsonRest(staticRoutes) - }) - calcMem("GoRestful", func() { - staticGoRestful = loadGoRestful(staticRoutes) - }) - calcMem("GorillaMux", func() { - staticGorillaMux = loadGorillaMux(staticRoutes) - }) - calcMem("HttpRouter", func() { - staticHttpRouter = loadHttpRouter(staticRoutes) - }) - calcMem("HttpTreeMux", func() { - staticHttpTreeMux = loadHttpTreeMux(staticRoutes) - }) - calcMem("Kocha", func() { - staticKocha = loadKocha(staticRoutes) - }) - calcMem("Macaron", func() { - staticMacaron = loadMacaron(staticRoutes) - }) - calcMem("Martini", func() { - staticMartini = loadMartini(staticRoutes) - }) - calcMem("Pat", func() { - staticPat = loadPat(staticRoutes) - }) - calcMem("Possum", func() { - staticPossum = loadPossum(staticRoutes) - }) - calcMem("R2router", func() { - staticR2router = loadR2router(staticRoutes) - }) - calcMem("Revel", func() { - staticRevel = loadRevel(staticRoutes) - }) - calcMem("Rivet", func() { - staticRivet = loadRivet(staticRoutes) - }) - calcMem("Tango", func() { - staticTango = loadTango(staticRoutes) - }) - calcMem("TigerTonic", func() { - staticTigerTonic = loadTigerTonic(staticRoutes) - }) - calcMem("Traffic", func() { - staticTraffic = loadTraffic(staticRoutes) - }) - calcMem("Vulcan", func() { - staticVulcan = loadVulcan(staticRoutes) - }) - // calcMem("Zeus", func() { - // staticZeus = loadZeus(staticRoutes) - // }) - - println() -} - -// All routes - -func BenchmarkAce_StaticAll(b *testing.B) { - benchRoutes(b, staticAce, staticRoutes) -} -func BenchmarkHttpServeMux_StaticAll(b *testing.B) { - benchRoutes(b, staticHttpServeMux, staticRoutes) -} -func BenchmarkBeego_StaticAll(b *testing.B) { - benchRoutes(b, staticBeego, staticRoutes) -} -func BenchmarkBear_StaticAll(b *testing.B) { - benchRoutes(b, staticBear, staticRoutes) -} -func BenchmarkBone_StaticAll(b *testing.B) { - benchRoutes(b, staticBone, staticRoutes) -} -func BenchmarkDenco_StaticAll(b *testing.B) { - benchRoutes(b, staticDenco, staticRoutes) -} -func BenchmarkEcho_StaticAll(b *testing.B) { - benchRoutes(b, staticEcho, staticRoutes) -} -func BenchmarkGin_StaticAll(b *testing.B) { - benchRoutes(b, staticGin, staticRoutes) -} -func BenchmarkGocraftWeb_StaticAll(b *testing.B) { - benchRoutes(b, staticGocraftWeb, staticRoutes) -} -func BenchmarkGoji_StaticAll(b *testing.B) { - benchRoutes(b, staticGoji, staticRoutes) -} -func BenchmarkGoJsonRest_StaticAll(b *testing.B) { - benchRoutes(b, staticGoJsonRest, staticRoutes) -} -func BenchmarkGoRestful_StaticAll(b *testing.B) { - benchRoutes(b, staticGoRestful, staticRoutes) -} -func BenchmarkGorillaMux_StaticAll(b *testing.B) { - benchRoutes(b, staticGorillaMux, staticRoutes) -} -func BenchmarkHttpRouter_StaticAll(b *testing.B) { - benchRoutes(b, staticHttpRouter, staticRoutes) -} -func BenchmarkHttpTreeMux_StaticAll(b *testing.B) { - benchRoutes(b, staticHttpRouter, staticRoutes) -} -func BenchmarkKocha_StaticAll(b *testing.B) { - benchRoutes(b, staticKocha, staticRoutes) -} -func BenchmarkMacaron_StaticAll(b *testing.B) { - benchRoutes(b, staticMacaron, staticRoutes) -} -func BenchmarkMartini_StaticAll(b *testing.B) { - benchRoutes(b, staticMartini, staticRoutes) -} -func BenchmarkPat_StaticAll(b *testing.B) { - benchRoutes(b, staticPat, staticRoutes) -} -func BenchmarkPossum_StaticAll(b *testing.B) { - benchRoutes(b, staticPossum, staticRoutes) -} -func BenchmarkR2router_StaticAll(b *testing.B) { - benchRoutes(b, staticR2router, staticRoutes) -} -func BenchmarkRevel_StaticAll(b *testing.B) { - benchRoutes(b, staticRevel, staticRoutes) -} -func BenchmarkRivet_StaticAll(b *testing.B) { - benchRoutes(b, staticRivet, staticRoutes) -} -func BenchmarkTango_StaticAll(b *testing.B) { - benchRoutes(b, staticTango, staticRoutes) -} -func BenchmarkTigerTonic_StaticAll(b *testing.B) { - benchRoutes(b, staticTigerTonic, staticRoutes) -} -func BenchmarkTraffic_StaticAll(b *testing.B) { - benchRoutes(b, staticTraffic, staticRoutes) -} -func BenchmarkVulcan_StaticAll(b *testing.B) { - benchRoutes(b, staticVulcan, staticRoutes) -} - -// func BenchmarkZeus_StaticAll(b *testing.B) { -// benchRoutes(b, staticZeus, staticRoutes) -// } diff --git a/suite/github.go b/suite/github.go new file mode 100644 index 00000000..a8b26dee --- /dev/null +++ b/suite/github.go @@ -0,0 +1,309 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package suite + +import "github.com/julienschmidt/go-http-routing-benchmark/driver" + +func init() { + gh1 := driver.NewBenchmark( + driver.Parameterized, + "GitHubStatic", + "Request one static GitHub endpoint with two segments", + ) + gh1.AddRoutes(githubRoutes) + gh1.AddTest(TestResponseOkWithoutOutput) + gh1.AddRequest("GET", "/user/repos") + + gh2 := driver.NewBenchmark( + driver.Parameterized, + "GitHub2Params", + "Request one parameterized GitHub endpoint with four segments, two of which are parameters", + ) + gh2.AddRoutes(githubRoutes) + gh2.AddTest(TestResponseOkWithoutOutput) + gh2.AddRequest("GET", "/repos/julienschmidt/httprouter/stargazers") + + gh3 := driver.NewBenchmark( + driver.Parameterized, + "GitHubNotFound", + "Request one unavailable GitHub endpoint", + ) + gh3.AddRoutes(githubRoutes) + gh3.AddTest(TestResponseNotFound) + gh3.AddRequest("GET", "/notfound/this-path-is-unavailable") // Without trailing slash! + + gh4 := driver.NewBenchmark( + driver.Parameterized, + "GitHubAll", + "Request all GitHub endpoints", + ) + gh4.AddRoutes(githubRoutes) + gh4.AddRequests(githubRoutes) + gh4.AddTest(TestResponseOkWithoutOutput) +} + +// http://developer.github.com/v3/ +var githubRoutes = driver.Fixtures{ + // OAuth Authorizations + {"GET", "/authorizations"}, + {"GET", "/authorizations/:id"}, + {"POST", "/authorizations"}, + //{"PUT", "/authorizations/clients/:client_id"}, + //{"PATCH", "/authorizations/:id"}, + {"DELETE", "/authorizations/:id"}, + {"GET", "/applications/:client_id/tokens/:access_token"}, + {"DELETE", "/applications/:client_id/tokens"}, + {"DELETE", "/applications/:client_id/tokens/:access_token"}, + + // Activity + {"GET", "/events"}, + {"GET", "/repos/:owner/:repo/events"}, + {"GET", "/networks/:owner/:repo/events"}, + {"GET", "/orgs/:org/events"}, + {"GET", "/users/:user/received_events"}, + {"GET", "/users/:user/received_events/public"}, + {"GET", "/users/:user/events"}, + {"GET", "/users/:user/events/public"}, + {"GET", "/users/:user/events/orgs/:org"}, + {"GET", "/feeds"}, + {"GET", "/notifications"}, + {"GET", "/repos/:owner/:repo/notifications"}, + {"PUT", "/notifications"}, + {"PUT", "/repos/:owner/:repo/notifications"}, + {"GET", "/notifications/threads/:id"}, + //{"PATCH", "/notifications/threads/:id"}, + {"GET", "/notifications/threads/:id/subscription"}, + {"PUT", "/notifications/threads/:id/subscription"}, + {"DELETE", "/notifications/threads/:id/subscription"}, + {"GET", "/repos/:owner/:repo/stargazers"}, + {"GET", "/users/:user/starred"}, + {"GET", "/user/starred"}, + {"GET", "/user/starred/:owner/:repo"}, + {"PUT", "/user/starred/:owner/:repo"}, + {"DELETE", "/user/starred/:owner/:repo"}, + {"GET", "/repos/:owner/:repo/subscribers"}, + {"GET", "/users/:user/subscriptions"}, + {"GET", "/user/subscriptions"}, + {"GET", "/repos/:owner/:repo/subscription"}, + {"PUT", "/repos/:owner/:repo/subscription"}, + {"DELETE", "/repos/:owner/:repo/subscription"}, + {"GET", "/user/subscriptions/:owner/:repo"}, + {"PUT", "/user/subscriptions/:owner/:repo"}, + {"DELETE", "/user/subscriptions/:owner/:repo"}, + + // Gists + {"GET", "/users/:user/gists"}, + {"GET", "/gists"}, + //{"GET", "/gists/public"}, + //{"GET", "/gists/starred"}, + {"GET", "/gists/:id"}, + {"POST", "/gists"}, + //{"PATCH", "/gists/:id"}, + {"PUT", "/gists/:id/star"}, + {"DELETE", "/gists/:id/star"}, + {"GET", "/gists/:id/star"}, + {"POST", "/gists/:id/forks"}, + {"DELETE", "/gists/:id"}, + + // Git Data + {"GET", "/repos/:owner/:repo/git/blobs/:sha"}, + {"POST", "/repos/:owner/:repo/git/blobs"}, + {"GET", "/repos/:owner/:repo/git/commits/:sha"}, + {"POST", "/repos/:owner/:repo/git/commits"}, + //{"GET", "/repos/:owner/:repo/git/refs/*ref"}, + {"GET", "/repos/:owner/:repo/git/refs"}, + {"POST", "/repos/:owner/:repo/git/refs"}, + //{"PATCH", "/repos/:owner/:repo/git/refs/*ref"}, + //{"DELETE", "/repos/:owner/:repo/git/refs/*ref"}, + {"GET", "/repos/:owner/:repo/git/tags/:sha"}, + {"POST", "/repos/:owner/:repo/git/tags"}, + {"GET", "/repos/:owner/:repo/git/trees/:sha"}, + {"POST", "/repos/:owner/:repo/git/trees"}, + + // Issues + {"GET", "/issues"}, + {"GET", "/user/issues"}, + {"GET", "/orgs/:org/issues"}, + {"GET", "/repos/:owner/:repo/issues"}, + {"GET", "/repos/:owner/:repo/issues/:number"}, + {"POST", "/repos/:owner/:repo/issues"}, + //{"PATCH", "/repos/:owner/:repo/issues/:number"}, + {"GET", "/repos/:owner/:repo/assignees"}, + {"GET", "/repos/:owner/:repo/assignees/:assignee"}, + {"GET", "/repos/:owner/:repo/issues/:number/comments"}, + //{"GET", "/repos/:owner/:repo/issues/comments"}, + //{"GET", "/repos/:owner/:repo/issues/comments/:id"}, + {"POST", "/repos/:owner/:repo/issues/:number/comments"}, + //{"PATCH", "/repos/:owner/:repo/issues/comments/:id"}, + //{"DELETE", "/repos/:owner/:repo/issues/comments/:id"}, + {"GET", "/repos/:owner/:repo/issues/:number/events"}, + //{"GET", "/repos/:owner/:repo/issues/events"}, + //{"GET", "/repos/:owner/:repo/issues/events/:id"}, + {"GET", "/repos/:owner/:repo/labels"}, + {"GET", "/repos/:owner/:repo/labels/:name"}, + {"POST", "/repos/:owner/:repo/labels"}, + //{"PATCH", "/repos/:owner/:repo/labels/:name"}, + {"DELETE", "/repos/:owner/:repo/labels/:name"}, + {"GET", "/repos/:owner/:repo/issues/:number/labels"}, + {"POST", "/repos/:owner/:repo/issues/:number/labels"}, + {"DELETE", "/repos/:owner/:repo/issues/:number/labels/:name"}, + {"PUT", "/repos/:owner/:repo/issues/:number/labels"}, + {"DELETE", "/repos/:owner/:repo/issues/:number/labels"}, + {"GET", "/repos/:owner/:repo/milestones/:number/labels"}, + {"GET", "/repos/:owner/:repo/milestones"}, + {"GET", "/repos/:owner/:repo/milestones/:number"}, + {"POST", "/repos/:owner/:repo/milestones"}, + //{"PATCH", "/repos/:owner/:repo/milestones/:number"}, + {"DELETE", "/repos/:owner/:repo/milestones/:number"}, + + // Miscellaneous + {"GET", "/emojis"}, + {"GET", "/gitignore/templates"}, + {"GET", "/gitignore/templates/:name"}, + {"POST", "/markdown"}, + {"POST", "/markdown/raw"}, + {"GET", "/meta"}, + {"GET", "/rate_limit"}, + + // Organizations + {"GET", "/users/:user/orgs"}, + {"GET", "/user/orgs"}, + {"GET", "/orgs/:org"}, + //{"PATCH", "/orgs/:org"}, + {"GET", "/orgs/:org/members"}, + {"GET", "/orgs/:org/members/:user"}, + {"DELETE", "/orgs/:org/members/:user"}, + {"GET", "/orgs/:org/public_members"}, + {"GET", "/orgs/:org/public_members/:user"}, + {"PUT", "/orgs/:org/public_members/:user"}, + {"DELETE", "/orgs/:org/public_members/:user"}, + {"GET", "/orgs/:org/teams"}, + {"GET", "/teams/:id"}, + {"POST", "/orgs/:org/teams"}, + //{"PATCH", "/teams/:id"}, + {"DELETE", "/teams/:id"}, + {"GET", "/teams/:id/members"}, + {"GET", "/teams/:id/members/:user"}, + {"PUT", "/teams/:id/members/:user"}, + {"DELETE", "/teams/:id/members/:user"}, + {"GET", "/teams/:id/repos"}, + {"GET", "/teams/:id/repos/:owner/:repo"}, + {"PUT", "/teams/:id/repos/:owner/:repo"}, + {"DELETE", "/teams/:id/repos/:owner/:repo"}, + {"GET", "/user/teams"}, + + // Pull Requests + {"GET", "/repos/:owner/:repo/pulls"}, + {"GET", "/repos/:owner/:repo/pulls/:number"}, + {"POST", "/repos/:owner/:repo/pulls"}, + //{"PATCH", "/repos/:owner/:repo/pulls/:number"}, + {"GET", "/repos/:owner/:repo/pulls/:number/commits"}, + {"GET", "/repos/:owner/:repo/pulls/:number/files"}, + {"GET", "/repos/:owner/:repo/pulls/:number/merge"}, + {"PUT", "/repos/:owner/:repo/pulls/:number/merge"}, + {"GET", "/repos/:owner/:repo/pulls/:number/comments"}, + //{"GET", "/repos/:owner/:repo/pulls/comments"}, + //{"GET", "/repos/:owner/:repo/pulls/comments/:number"}, + {"PUT", "/repos/:owner/:repo/pulls/:number/comments"}, + //{"PATCH", "/repos/:owner/:repo/pulls/comments/:number"}, + //{"DELETE", "/repos/:owner/:repo/pulls/comments/:number"}, + + // Repositories + {"GET", "/user/repos"}, + {"GET", "/users/:user/repos"}, + {"GET", "/orgs/:org/repos"}, + {"GET", "/repositories"}, + {"POST", "/user/repos"}, + {"POST", "/orgs/:org/repos"}, + {"GET", "/repos/:owner/:repo"}, + //{"PATCH", "/repos/:owner/:repo"}, + {"GET", "/repos/:owner/:repo/contributors"}, + {"GET", "/repos/:owner/:repo/languages"}, + {"GET", "/repos/:owner/:repo/teams"}, + {"GET", "/repos/:owner/:repo/tags"}, + {"GET", "/repos/:owner/:repo/branches"}, + {"GET", "/repos/:owner/:repo/branches/:branch"}, + {"DELETE", "/repos/:owner/:repo"}, + {"GET", "/repos/:owner/:repo/collaborators"}, + {"GET", "/repos/:owner/:repo/collaborators/:user"}, + {"PUT", "/repos/:owner/:repo/collaborators/:user"}, + {"DELETE", "/repos/:owner/:repo/collaborators/:user"}, + {"GET", "/repos/:owner/:repo/comments"}, + {"GET", "/repos/:owner/:repo/commits/:sha/comments"}, + {"POST", "/repos/:owner/:repo/commits/:sha/comments"}, + {"GET", "/repos/:owner/:repo/comments/:id"}, + //{"PATCH", "/repos/:owner/:repo/comments/:id"}, + {"DELETE", "/repos/:owner/:repo/comments/:id"}, + {"GET", "/repos/:owner/:repo/commits"}, + {"GET", "/repos/:owner/:repo/commits/:sha"}, + {"GET", "/repos/:owner/:repo/readme"}, + //{"GET", "/repos/:owner/:repo/contents/*path"}, + //{"PUT", "/repos/:owner/:repo/contents/*path"}, + //{"DELETE", "/repos/:owner/:repo/contents/*path"}, + //{"GET", "/repos/:owner/:repo/:archive_format/:ref"}, + {"GET", "/repos/:owner/:repo/keys"}, + {"GET", "/repos/:owner/:repo/keys/:id"}, + {"POST", "/repos/:owner/:repo/keys"}, + //{"PATCH", "/repos/:owner/:repo/keys/:id"}, + {"DELETE", "/repos/:owner/:repo/keys/:id"}, + {"GET", "/repos/:owner/:repo/downloads"}, + {"GET", "/repos/:owner/:repo/downloads/:id"}, + {"DELETE", "/repos/:owner/:repo/downloads/:id"}, + {"GET", "/repos/:owner/:repo/forks"}, + {"POST", "/repos/:owner/:repo/forks"}, + {"GET", "/repos/:owner/:repo/hooks"}, + {"GET", "/repos/:owner/:repo/hooks/:id"}, + {"POST", "/repos/:owner/:repo/hooks"}, + //{"PATCH", "/repos/:owner/:repo/hooks/:id"}, + {"POST", "/repos/:owner/:repo/hooks/:id/tests"}, + {"DELETE", "/repos/:owner/:repo/hooks/:id"}, + {"POST", "/repos/:owner/:repo/merges"}, + {"GET", "/repos/:owner/:repo/releases"}, + {"GET", "/repos/:owner/:repo/releases/:id"}, + {"POST", "/repos/:owner/:repo/releases"}, + //{"PATCH", "/repos/:owner/:repo/releases/:id"}, + {"DELETE", "/repos/:owner/:repo/releases/:id"}, + {"GET", "/repos/:owner/:repo/releases/:id/assets"}, + {"GET", "/repos/:owner/:repo/stats/contributors"}, + {"GET", "/repos/:owner/:repo/stats/commit_activity"}, + {"GET", "/repos/:owner/:repo/stats/code_frequency"}, + {"GET", "/repos/:owner/:repo/stats/participation"}, + {"GET", "/repos/:owner/:repo/stats/punch_card"}, + {"GET", "/repos/:owner/:repo/statuses/:ref"}, + {"POST", "/repos/:owner/:repo/statuses/:ref"}, + + // Search + {"GET", "/search/repositories"}, + {"GET", "/search/code"}, + {"GET", "/search/issues"}, + {"GET", "/search/users"}, + {"GET", "/legacy/issues/search/:owner/:repository/:state/:keyword"}, + {"GET", "/legacy/repos/search/:keyword"}, + {"GET", "/legacy/user/search/:keyword"}, + {"GET", "/legacy/user/email/:email"}, + + // Users + {"GET", "/users/:user"}, + {"GET", "/user"}, + //{"PATCH", "/user"}, + {"GET", "/users"}, + {"GET", "/user/emails"}, + {"POST", "/user/emails"}, + {"DELETE", "/user/emails"}, + {"GET", "/users/:user/followers"}, + {"GET", "/user/followers"}, + {"GET", "/users/:user/following"}, + {"GET", "/user/following"}, + {"GET", "/user/following/:user"}, + {"GET", "/users/:user/following/:target_user"}, + {"PUT", "/user/following/:user"}, + {"DELETE", "/user/following/:user"}, + {"GET", "/users/:user/keys"}, + {"GET", "/user/keys"}, + {"GET", "/user/keys/:id"}, + {"POST", "/user/keys"}, + //{"PATCH", "/user/keys/:id"}, + {"DELETE", "/user/keys/:id"}, +} diff --git a/suite/googleplus.go b/suite/googleplus.go new file mode 100644 index 00000000..1aec2449 --- /dev/null +++ b/suite/googleplus.go @@ -0,0 +1,80 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package suite + +import "github.com/julienschmidt/go-http-routing-benchmark/driver" + +func init() { + gp1 := driver.NewBenchmark( + driver.Parameterized, + "GooglePlusStatic", + "Request one static Google+ endpoint with one segment", + ) + gp1.AddRoutes(googleplusRoutes) + gp1.AddTest(TestResponseOkWithoutOutput) + gp1.AddRequest("GET", "/people") + + gp2 := driver.NewBenchmark( + driver.Parameterized, + "GooglePlusParam", + "Request one parameterized Google+ endpoint with two segments, one of which is a parameter", + ) + gp2.AddRoutes(googleplusRoutes) + gp2.AddTest(TestResponseOkWithoutOutput) + gp2.AddRequest("GET", "/people/118051310819094153327") + + gp3 := driver.NewBenchmark( + driver.Parameterized, + "GooglePlus2Params", + "Request one parameterized Google+ endpoint with four segments, two of which are parameters", + ) + gp3.AddRoutes(googleplusRoutes) + gp3.AddTest(TestResponseOkWithoutOutput) + gp3.AddRequest("GET", "/people/118051310819094153327/activities/123456789") + + gp4 := driver.NewBenchmark( + driver.Parameterized, + "GooglePlusNotFound", + "Request one unavailable Google+ endpoint", + ) + gp4.AddRoutes(googleplusRoutes) + gp4.AddTest(TestResponseNotFound) + gp4.AddRequest("GET", "/notfound/this-path-is-unavailable") // Without trailing slash! + + gp5 := driver.NewBenchmark( + driver.Parameterized, + "GooglePlusAll", + "Request all Google+ endpoints", + ) + gp5.AddRoutes(googleplusRoutes) + gp5.AddRequests(googleplusRoutes) + gp5.AddTest(TestResponseOkWithoutOutput) +} + +// Google+ +// https://developers.google.com/+/api/latest/ +// (in reality this is just a subset of a much larger API) +var googleplusRoutes = driver.Fixtures{ + // People + {"GET", "/people/:userId"}, + {"GET", "/people"}, + {"GET", "/activities/:activityId/people/:collection"}, + {"GET", "/people/:userId/people/:collection"}, + {"GET", "/people/:userId/openIdConnect"}, + + // Activities + {"GET", "/people/:userId/activities/:collection"}, + {"GET", "/activities/:activityId"}, + {"GET", "/activities"}, + + // Comments + {"GET", "/activities/:activityId/comments"}, + {"GET", "/comments/:commentId"}, + + // Moments + {"POST", "/people/:userId/moments/:collection"}, + {"GET", "/people/:userId/moments/:collection"}, + {"DELETE", "/moments/:id"}, +} diff --git a/suite/params.go b/suite/params.go new file mode 100644 index 00000000..734b5673 --- /dev/null +++ b/suite/params.go @@ -0,0 +1,77 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package suite + +import "github.com/julienschmidt/go-http-routing-benchmark/driver" + +func init() { + p1 := driver.NewBenchmark( + driver.ParameterReadWrite, + "OneParam", + "Request one parameterized endpoint with two segments, one of which is a parameter", + ) + p1.AddRoute("GET", "/user/:name") + p1.AddRequest("GET", "/user/gordon") + p1.AddTest(TestResponseOkWithoutOutput) + p1.AddTest(TestParameterizedRoutesContainsNParams(1)) + + p2 := driver.NewBenchmark( + driver.ParameterReadWrite, + "FiveParams", + "Request one parameterized endpoint with five segments, all of which are parameters", + ) + p2.AddRoute("GET", "/:a/:b/:c/:d/:e") + p2.AddRequest("GET", "/test/test/test/test/test") + p2.AddTest(TestResponseOkWithoutOutput) + p2.AddTest(TestParameterizedRoutesContainsNParams(5)) + + p3 := driver.NewBenchmark( + driver.ParameterReadWrite, + "TwentyParams", + "Request one parameterized endpoint with twenty segments, all of which are parameters", + ) + p3.AddTest(TestResponseOkWithoutOutput) + p3.AddTest(TestParameterizedRoutesContainsNParams(20)) + p3.AddRequest("GET", "/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t") + p3.AddRoute("GET", "/:a/:b/:c/:d/:e/:f/:g/:h/:i/:j/:k/:l/:m/:n/:o/:p/:q/:r/:s/:t") +} + +func init() { + rwp1 := driver.NewBenchmark( + driver.ParameterReadWrite, + "ReadWriteOneParam", + "Request one parameterized endpoint with two segments, one of which is a "+ + "parameter and respond with the value of the last parameter", + ) + rwp1.AddTest(TestParameterReadWriteName) + rwp1.AddTest(TestResponseOkParameterReadWrite) + rwp1.AddTest(TestParameterizedRoutesContainsNParams(1)) + rwp1.AddRoute("GET", "/user/:"+driver.ParamNameReadWrite) + rwp1.AddRequest("GET", "/user/"+driver.ParamNameReadWrite) + + rwp2 := driver.NewBenchmark( + driver.ParameterReadWrite, + "ReadWriteFiveParams", + "Request one parameterized endpoint with five segments, all of which "+ + "are parameters and respond with the value of the last parameter", + ) + rwp2.AddTest(TestParameterReadWriteName) + rwp2.AddTest(TestResponseOkParameterReadWrite) + rwp2.AddTest(TestParameterizedRoutesContainsNParams(5)) + rwp2.AddRoute("GET", "/:a/:b/:c/:d/:"+driver.ParamNameReadWrite) + rwp2.AddRequest("GET", "/test/test/test/test/"+driver.ParamNameReadWrite) + + rwp3 := driver.NewBenchmark( + driver.ParameterReadWrite, + "ReadWriteTwentyParams", + "Request one parameterized endpoint with twenty segments, all of which "+ + "are parameters and respond with the value of the last parameter", + ) + rwp3.AddTest(TestParameterReadWriteName) + rwp3.AddTest(TestResponseOkParameterReadWrite) + rwp3.AddTest(TestParameterizedRoutesContainsNParams(20)) + rwp3.AddRequest("GET", "/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/"+driver.ParamNameReadWrite) + rwp3.AddRoute("GET", "/:a/:b/:c/:d/:e/:f/:g/:h/:i/:j/:k/:l/:m/:n/:o/:p/:q/:r/:s/:"+driver.ParamNameReadWrite) +} diff --git a/suite/parsecom.go b/suite/parsecom.go new file mode 100644 index 00000000..e517e3a7 --- /dev/null +++ b/suite/parsecom.go @@ -0,0 +1,100 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package suite + +import "github.com/julienschmidt/go-http-routing-benchmark/driver" + +func init() { + pc1 := driver.NewBenchmark( + driver.Parameterized, + "ParseComStatic", + "Request one static parse.com endpoint with two segments", + ) + pc1.AddRoutes(parsecomRoutes) + pc1.AddTest(TestResponseOkWithoutOutput) + pc1.AddRequest("GET", "/1/users") + + pc2 := driver.NewBenchmark( + driver.Parameterized, + "ParseComParam", + "Request one parameterized parse.com endpoint with three segments, one of which is a parameter", + ) + pc2.AddRoutes(parsecomRoutes) + pc2.AddTest(TestResponseOkWithoutOutput) + pc2.AddRequest("GET", "/1/classes/go") + + pc3 := driver.NewBenchmark( + driver.Parameterized, + "ParseCom2Params", + "Request one parameterized parse.com endpoint with four segments, two of which are parameters", + ) + pc3.AddRoutes(parsecomRoutes) + pc3.AddTest(TestResponseOkWithoutOutput) + pc3.AddRequest("GET", "/1/classes/go/123456789") + + pc4 := driver.NewBenchmark( + driver.Parameterized, + "ParseComNotFound", + "Request one unavailable parse.com endpoint", + ) + pc4.AddRoutes(parsecomRoutes) + pc4.AddTest(TestResponseNotFound) + pc4.AddRequest("GET", "/notfound/this-path-is-unavailable") // Without trailing slash! + + pc5 := driver.NewBenchmark( + driver.Parameterized, + "ParseComAll", + "Request all parse.com endpoints", + ) + pc5.AddRoutes(parsecomRoutes) + pc5.AddRequests(parsecomRoutes) + pc5.AddTest(TestResponseOkWithoutOutput) +} + +// Parse +// https://parse.com/docs/rest#summary +var parsecomRoutes = driver.Fixtures{ + // Objects + {"POST", "/1/classes/:className"}, + {"GET", "/1/classes/:className/:objectId"}, + {"PUT", "/1/classes/:className/:objectId"}, + {"GET", "/1/classes/:className"}, + {"DELETE", "/1/classes/:className/:objectId"}, + + // Users + {"POST", "/1/users"}, + {"GET", "/1/login"}, + {"GET", "/1/users/:objectId"}, + {"PUT", "/1/users/:objectId"}, + {"GET", "/1/users"}, + {"DELETE", "/1/users/:objectId"}, + {"POST", "/1/requestPasswordReset"}, + + // Roles + {"POST", "/1/roles"}, + {"GET", "/1/roles/:objectId"}, + {"PUT", "/1/roles/:objectId"}, + {"GET", "/1/roles"}, + {"DELETE", "/1/roles/:objectId"}, + + // Files + {"POST", "/1/files/:fileName"}, + + // Analytics + {"POST", "/1/events/:eventName"}, + + // Push Notifications + {"POST", "/1/push"}, + + // Installations + {"POST", "/1/installations"}, + {"GET", "/1/installations/:objectId"}, + {"PUT", "/1/installations/:objectId"}, + {"GET", "/1/installations"}, + {"DELETE", "/1/installations/:objectId"}, + + // Cloud Functions + {"POST", "/1/functions"}, +} diff --git a/suite/static.go b/suite/static.go new file mode 100644 index 00000000..95be8d80 --- /dev/null +++ b/suite/static.go @@ -0,0 +1,192 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package suite + +import "github.com/julienschmidt/go-http-routing-benchmark/driver" + +func init() { + s1 := driver.NewBenchmark( + driver.Static, + "StaticNotFound", + "Request one unavailable static endpoint", + ) + s1.AddRoutes(staticRoutes) + s1.AddTest(TestResponseNotFound) + s1.AddTest(TestStaticRoutesAreStatic) + s1.AddRequest("GET", "/notfound/this-path-is-unavailable") // Without trailing slash! + + s2 := driver.NewBenchmark( + driver.Static, + "StaticAll", + "Request all static endpoints", + ) + s2.AddRoutes(staticRoutes) + s2.AddRequests(staticRoutes) + s2.AddTest(TestStaticRoutesAreStatic) + s2.AddTest(TestResponseOkWithoutOutput) +} + +var staticRoutes = driver.Fixtures{ + // Cannot register root path handler because of the way http.ServeMux + // handles unknown paths (fallback to last known directory). + // {"GET", "/"}, + + {"GET", "/cmd.html"}, + {"GET", "/code.html"}, + {"GET", "/contrib.html"}, + {"GET", "/contribute.html"}, + {"GET", "/debugging_with_gdb.html"}, + {"GET", "/docs.html"}, + {"GET", "/effective_go.html"}, + {"GET", "/files.log"}, + {"GET", "/gccgo_contribute.html"}, + {"GET", "/gccgo_install.html"}, + {"GET", "/go-logo-black.png"}, + {"GET", "/go-logo-blue.png"}, + {"GET", "/go-logo-white.png"}, + {"GET", "/go1.1.html"}, + {"GET", "/go1.2.html"}, + {"GET", "/go1.html"}, + {"GET", "/go1compat.html"}, + {"GET", "/go_faq.html"}, + {"GET", "/go_mem.html"}, + {"GET", "/go_spec.html"}, + {"GET", "/help.html"}, + {"GET", "/ie.css"}, + {"GET", "/install-source.html"}, + {"GET", "/install.html"}, + {"GET", "/logo-153x55.png"}, + {"GET", "/Makefile"}, + {"GET", "/root.html"}, + {"GET", "/share.png"}, + {"GET", "/sieve.gif"}, + {"GET", "/tos.html"}, + {"GET", "/articles/"}, + {"GET", "/articles/go_command.html"}, + {"GET", "/articles/index.html"}, + {"GET", "/articles/wiki/"}, + {"GET", "/articles/wiki/edit.html"}, + {"GET", "/articles/wiki/final-noclosure.go"}, + {"GET", "/articles/wiki/final-noerror.go"}, + {"GET", "/articles/wiki/final-parsetemplate.go"}, + {"GET", "/articles/wiki/final-template.go"}, + {"GET", "/articles/wiki/final.go"}, + {"GET", "/articles/wiki/get.go"}, + {"GET", "/articles/wiki/http-sample.go"}, + {"GET", "/articles/wiki/index.html"}, + {"GET", "/articles/wiki/Makefile"}, + {"GET", "/articles/wiki/notemplate.go"}, + {"GET", "/articles/wiki/part1-noerror.go"}, + {"GET", "/articles/wiki/part1.go"}, + {"GET", "/articles/wiki/part2.go"}, + {"GET", "/articles/wiki/part3-errorhandling.go"}, + {"GET", "/articles/wiki/part3.go"}, + {"GET", "/articles/wiki/test.bash"}, + {"GET", "/articles/wiki/test_edit.good"}, + {"GET", "/articles/wiki/test_Test.txt.good"}, + {"GET", "/articles/wiki/test_view.good"}, + {"GET", "/articles/wiki/view.html"}, + {"GET", "/codewalk/"}, + {"GET", "/codewalk/codewalk.css"}, + {"GET", "/codewalk/codewalk.js"}, + {"GET", "/codewalk/codewalk.xml"}, + {"GET", "/codewalk/functions.xml"}, + {"GET", "/codewalk/markov.go"}, + {"GET", "/codewalk/markov.xml"}, + {"GET", "/codewalk/pig.go"}, + {"GET", "/codewalk/popout.png"}, + {"GET", "/codewalk/run"}, + {"GET", "/codewalk/sharemem.xml"}, + {"GET", "/codewalk/urlpoll.go"}, + {"GET", "/devel/"}, + {"GET", "/devel/release.html"}, + {"GET", "/devel/weekly.html"}, + {"GET", "/gopher/"}, + {"GET", "/gopher/appenginegopher.jpg"}, + {"GET", "/gopher/appenginegophercolor.jpg"}, + {"GET", "/gopher/appenginelogo.gif"}, + {"GET", "/gopher/bumper.png"}, + {"GET", "/gopher/bumper192x108.png"}, + {"GET", "/gopher/bumper320x180.png"}, + {"GET", "/gopher/bumper480x270.png"}, + {"GET", "/gopher/bumper640x360.png"}, + {"GET", "/gopher/doc.png"}, + {"GET", "/gopher/frontpage.png"}, + {"GET", "/gopher/gopherbw.png"}, + {"GET", "/gopher/gophercolor.png"}, + {"GET", "/gopher/gophercolor16x16.png"}, + {"GET", "/gopher/help.png"}, + {"GET", "/gopher/pkg.png"}, + {"GET", "/gopher/project.png"}, + {"GET", "/gopher/ref.png"}, + {"GET", "/gopher/run.png"}, + {"GET", "/gopher/talks.png"}, + {"GET", "/gopher/pencil/"}, + {"GET", "/gopher/pencil/gopherhat.jpg"}, + {"GET", "/gopher/pencil/gopherhelmet.jpg"}, + {"GET", "/gopher/pencil/gophermega.jpg"}, + {"GET", "/gopher/pencil/gopherrunning.jpg"}, + {"GET", "/gopher/pencil/gopherswim.jpg"}, + {"GET", "/gopher/pencil/gopherswrench.jpg"}, + {"GET", "/play/"}, + {"GET", "/play/fib.go"}, + {"GET", "/play/hello.go"}, + {"GET", "/play/life.go"}, + {"GET", "/play/peano.go"}, + {"GET", "/play/pi.go"}, + {"GET", "/play/sieve.go"}, + {"GET", "/play/solitaire.go"}, + {"GET", "/play/tree.go"}, + {"GET", "/progs/"}, + {"GET", "/progs/cgo1.go"}, + {"GET", "/progs/cgo2.go"}, + {"GET", "/progs/cgo3.go"}, + {"GET", "/progs/cgo4.go"}, + {"GET", "/progs/defer.go"}, + {"GET", "/progs/defer.out"}, + {"GET", "/progs/defer2.go"}, + {"GET", "/progs/defer2.out"}, + {"GET", "/progs/eff_bytesize.go"}, + {"GET", "/progs/eff_bytesize.out"}, + {"GET", "/progs/eff_qr.go"}, + {"GET", "/progs/eff_sequence.go"}, + {"GET", "/progs/eff_sequence.out"}, + {"GET", "/progs/eff_unused1.go"}, + {"GET", "/progs/eff_unused2.go"}, + {"GET", "/progs/error.go"}, + {"GET", "/progs/error2.go"}, + {"GET", "/progs/error3.go"}, + {"GET", "/progs/error4.go"}, + {"GET", "/progs/go1.go"}, + {"GET", "/progs/gobs1.go"}, + {"GET", "/progs/gobs2.go"}, + {"GET", "/progs/image_draw.go"}, + {"GET", "/progs/image_package1.go"}, + {"GET", "/progs/image_package1.out"}, + {"GET", "/progs/image_package2.go"}, + {"GET", "/progs/image_package2.out"}, + {"GET", "/progs/image_package3.go"}, + {"GET", "/progs/image_package3.out"}, + {"GET", "/progs/image_package4.go"}, + {"GET", "/progs/image_package4.out"}, + {"GET", "/progs/image_package5.go"}, + {"GET", "/progs/image_package5.out"}, + {"GET", "/progs/image_package6.go"}, + {"GET", "/progs/image_package6.out"}, + {"GET", "/progs/interface.go"}, + {"GET", "/progs/interface2.go"}, + {"GET", "/progs/interface2.out"}, + {"GET", "/progs/json1.go"}, + {"GET", "/progs/json2.go"}, + {"GET", "/progs/json2.out"}, + {"GET", "/progs/json3.go"}, + {"GET", "/progs/json4.go"}, + {"GET", "/progs/json5.go"}, + {"GET", "/progs/run"}, + {"GET", "/progs/slices.go"}, + {"GET", "/progs/timeout1.go"}, + {"GET", "/progs/timeout2.go"}, + {"GET", "/progs/update.bash"}, +} diff --git a/suite/tests.go b/suite/tests.go new file mode 100644 index 00000000..83a7df98 --- /dev/null +++ b/suite/tests.go @@ -0,0 +1,102 @@ +// Copyright 2014-2015 Julien Schmidt. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +package suite + +import ( + "fmt" + "net/http" + "strings" + + "github.com/julienschmidt/go-http-routing-benchmark/driver" +) + +// TestStaticRoutesAreStatic ensures that static routes contain no parameterized segment. +var TestStaticRoutesAreStatic = &driver.Test{ + Description: "Test that the read/write parameter exists", + Route: func(f *driver.Fixture) *driver.Assertion { + x := strings.Count(f.Path, "/:") + if x > 0 { + return &driver.Assertion{ + Error: "Static routes must not contain parameters: " + f.Path, + } + } + + return nil + }, +} + +// TestParameterizedRoutesContainsNParams ensures that parameterized routes +// contain n parameterized segments. +func TestParameterizedRoutesContainsNParams(n int) *driver.Test { + return &driver.Test{ + Description: "Test that the read/write parameter exists", + Route: func(f *driver.Fixture) *driver.Assertion { + x := strings.Count(f.Path, "/:") + if x != n { + return &driver.Assertion{ + Error: fmt.Sprintf("Parameterized route must contain %d parameter(s), has %d", n, x), + } + } + + return nil + }, + } +} + +// TestParameterReadWriteName tests that the route contains the parameter +// name used to benchmark ParameterReadWrite. +var TestParameterReadWriteName = &driver.Test{ + Description: "Test that the read/write parameter exists", + Route: func(f *driver.Fixture) *driver.Assertion { + x := strings.Count(f.Path, "/:"+driver.ParamNameReadWrite) + if x == 0 { + return &driver.Assertion{ + Error: "The route contains no parameter named " + driver.ParamNameReadWrite, + } + } else if x > 1 { + return &driver.Assertion{ + Error: "The route contains too many parameters named " + driver.ParamNameReadWrite, + } + } + + return nil + }, +} + +// TestResponseOkParameterReadWrite is a test case that tests that the response status +// code is 200 and the response body is equal to the value of ParameterReadWrite name. +var TestResponseOkParameterReadWrite = &driver.Test{ + Description: "Test that the response status is 200 ok without output written", + Request: func(r *http.Request, w *driver.Response) *driver.Assertion { + return &driver.Assertion{ + Expect: fmt.Sprintf("Status code: 200, Output: \"%s\"", driver.ParamNameReadWrite), + Actual: fmt.Sprintf("Status code: %d, Output: \"%s\"", w.Status(), string(w.Output())), + } + }, +} + +// TestResponseNotFound is a test case that tests that the response status +// code is 404 with or without output. +var TestResponseNotFound = &driver.Test{ + Description: "Test that the response status is 404 not found", + Request: func(r *http.Request, w *driver.Response) *driver.Assertion { + return &driver.Assertion{ + Expect: "Status code: 404", + Actual: fmt.Sprintf("Status code: %d", w.Status()), + } + }, +} + +// TestResponseOkWithoutOutput is a test case that tests that the response status +// code is 0 and the response body is empty. +var TestResponseOkWithoutOutput = &driver.Test{ + Description: "Test that the response status is 200 without output written", + Request: func(r *http.Request, w *driver.Response) *driver.Assertion { + return &driver.Assertion{ + Expect: "Status code: 200, Output: \"\"", + Actual: fmt.Sprintf("Status code: %d, Output: \"%s\"", w.Status(), string(w.Output())), + } + }, +}