<commit_before>package revel

import (
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"testing"
)

// Data-driven tests that check that a given routes-file line translates into
// the expected Route object.
var routeTestCases = map[string]*Route{
	"get / Application.Index": &Route{
		Method:        "GET",
		Path:          "/",
		Action:        "Application.Index",
		pathPattern:   regexp.MustCompile("/$"),
		args:          []*arg{},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.Index"),
	},

	"post /app/{id} Application.SaveApp": &Route{
		Method:      "POST",
		Path:        "/app/{id}",
		Action:      "Application.SaveApp",
		pathPattern: regexp.MustCompile("/app/(?P<id>[^/]+)$"),
		args: []*arg{
			{
				name:       "id",
				constraint: regexp.MustCompile("[^/]+"),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.SaveApp"),
	},

	"post /app/{<[0-9]+>id} Application.SaveApp": &Route{
		Method:      "POST",
		Path:        "/app/{<[0-9]+>id}",
		Action:      "Application.SaveApp",
		pathPattern: regexp.MustCompile("/app/(?P<id>[0-9]+)$"),
		args: []*arg{
			{
				name:       "id",
				constraint: regexp.MustCompile("[0-9]+"),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.SaveApp"),
	},

	"get /app/? Application.List": &Route{
		Method:        "GET",
		Path:          "/app/?",
		Action:        "Application.List",
		pathPattern:   regexp.MustCompile("/app/?$"),
		args:          []*arg{},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.List"),
	},

	`get /apps/{<\d+>appId}/? Application.Show`: &Route{
		Method:      "GET",
		Path:        `/apps/{<\d+>appId}/?`,
		Action:      "Application.Show",
		pathPattern: regexp.MustCompile(`/apps/(?P<appId>\d+)/?$`),
		args: []*arg{
			{
				name:       "appId",
				constraint: regexp.MustCompile(`\d+`),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.Show"),
	},

	`GET /public/{<.+>filepath}   Static.Serve("public")`: &Route{
		Method:      "GET",
		Path:        "/public/{<.+>filepath}",
		Action:      "Static.Serve",
		pathPattern: regexp.MustCompile(`/public/(?P<filepath>.+)$`),
		args: []*arg{
			{
				name:       "filepath",
				constraint: regexp.MustCompile(`.+`),
			},
		},
		FixedParams: []string{
			"public",
		},
		actionPattern: regexp.MustCompile("Static\\.Serve"),
	},

	`GET /javascript/{<.+>filepath} Static.Serve("public/js")`: &Route{
		Method:      "GET",
		Path:        "/javascript/{<.+>filepath}",
		Action:      "Static.Serve",
		pathPattern: regexp.MustCompile(`/javascript/(?P<filepath>.+)$`),
		args: []*arg{
			{
				name:       "filepath",
				constraint: regexp.MustCompile(`.+`),
			},
		},
		FixedParams: []string{
			"public",
		},
		actionPattern: regexp.MustCompile("Static\\.Serve"),
	},

	"* /apps/{id}/{action} Application.{action}": &Route{
		Method:      "*",
		Path:        "/apps/{id}/{action}",
		Action:      "Application.{action}",
		pathPattern: regexp.MustCompile("/apps/(?P<id>[^/]+)/(?P<action>[^/]+)$"),
		args: []*arg{
			{
				name:       "id",
				constraint: regexp.MustCompile("[^/]+"),
			},
			{
				name:       "action",
				constraint: regexp.MustCompile("[^/]+"),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.(?P<action>[^/]+)"),
	},

	"* /{controller}/{action} {controller}.{action}": &Route{
		Method:      "*",
		Path:        "/{controller}/{action}",
		Action:      "{controller}.{action}",
		pathPattern: regexp.MustCompile("/(?P<controller>[^/]+)/(?P<action>[^/]+)$"),
		args: []*arg{
			{
				name:       "controller",
				constraint: regexp.MustCompile("[^/]+"),
			},
			{
				name:       "action",
				constraint: regexp.MustCompile("[^/]+"),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("(?P<controller>[^/]+)\\.(?P<action>[^/]+)"),
	},
}

// Run the test cases above.
func TestComputeRoute(t *testing.T) {
	for routeLine, expected := range routeTestCases {
		method, path, action, fixedArgs, found := parseRouteLine(routeLine)
		if !found {
			t.Error("Failed to parse route line:", routeLine)
			continue
		}
		actual := NewRoute(method, path, action, fixedArgs)
		eq(t, "Method", actual.Method, expected.Method)
		eq(t, "Path", actual.Path, expected.Path)
		eq(t, "Action", actual.Action, expected.Action)
		eq(t, "pathPattern", fmt.Sprint(actual.pathPattern), fmt.Sprint(expected.pathPattern))
		eq(t, "len(args)", len(actual.args), len(expected.args))
		for i, arg := range actual.args {
			if len(expected.args) <= i {
				break
			}
			eq(t, "arg.name", arg.name, expected.args[i].name)
			eq(t, "arg.constraint", arg.constraint.String(), expected.args[i].constraint.String())
		}
		eq(t, "actionPattern", fmt.Sprint(actual.actionPattern), fmt.Sprint(expected.actionPattern))
		if t.Failed() {
			t.Fatal("Failed on route:", routeLine)
		}
	}
}

// Router Tests

const TEST_ROUTES = `
# This is a comment
GET   /                          Application.Index
GET   /app/{id}/?                Application.Show
POST  /app/{id}                  Application.Save
PATCH /app/{id}/?                Application.Update
GET   /javascript/{<.+>filepath} Static.Serve("public/js")
GET   /public/{<.+>filepath}     Static.Serve("public")
*     /{controller}/{action}     {controller}.{action}

GET   /favicon.ico               404
`

var routeMatchTestCases = map[*http.Request]*RouteMatch{
	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Index",
		FixedParams:    []string{},
		Params:         map[string]string{},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/app/123"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Show",
		FixedParams:    []string{},
		Params:         map[string]string{"id": "123"},
	},

	&http.Request{
		Method: "PATCH",
		URL:    &url.URL{Path: "/app/123"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Update",
		FixedParams:    []string{},
		Params:         map[string]string{"id": "123"},
	},

	&http.Request{
		Method: "POST",
		URL:    &url.URL{Path: "/app/123"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Save",
		FixedParams:    []string{},
		Params:         map[string]string{"id": "123"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/app/123/"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Show",
		FixedParams:    []string{},
		Params:         map[string]string{"id": "123"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/public/style.css"},
	}: &RouteMatch{
		ControllerName: "Static",
		MethodName:     "Serve",
		FixedParams:    []string{"public"},
		Params:         map[string]string{"filepath": "style.css"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/javascript/sessvars.js"},
	}: &RouteMatch{
		ControllerName: "Static",
		MethodName:     "Serve",
		FixedParams:    []string{"public"},
		Params:         map[string]string{"filepath": "sessvars.js"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/Implicit/Route"},
	}: &RouteMatch{
		ControllerName: "Implicit",
		MethodName:     "Route",
		FixedParams:    []string{},
		Params:         map[string]string{"controller": "Implicit", "action": "Route"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/favicon.ico"},
	}: &RouteMatch{
		ControllerName: "",
		MethodName:     "",
		Action:         "404",
		FixedParams:    []string{},
		Params:         map[string]string{},
	},
}

func TestRouteMatches(t *testing.T) {
	BasePath = "/BasePath"
	router := NewRouter("")
	router.Routes, _ = parseRoutes("", TEST_ROUTES, false)
	for req, expected := range routeMatchTestCases {
		t.Log("Routing:", req.Method, req.URL)
		actual := router.Route(req)
		if !eq(t, "Found route", actual != nil, expected != nil) {
			continue
		}
		eq(t, "ControllerName", actual.ControllerName, expected.ControllerName)
		eq(t, "MethodName", actual.MethodName, expected.MethodName)
		eq(t, "len(Params)", len(actual.Params), len(expected.Params))
		for key, actualValue := range actual.Params {
			eq(t, "Params", actualValue, expected.Params[key])
		}
	}
}

// Reverse Routing

type ReverseRouteArgs struct {
	action string
	args   map[string]string
}

var reverseRoutingTestCases = map[*ReverseRouteArgs]*ActionDefinition{
	&ReverseRouteArgs{
		action: "Application.Index",
		args:   map[string]string{},
	}: &ActionDefinition{
		Url:    "/",
		Method: "GET",
		Star:   false,
		Action: "Application.Index",
	},

	&ReverseRouteArgs{
		action: "Application.Show",
		args:   map[string]string{"id": "123"},
	}: &ActionDefinition{
		Url:    "/app/123/",
		Method: "GET",
		Star:   false,
		Action: "Application.Show",
	},

	&ReverseRouteArgs{
		action: "Implicit.Route",
		args:   map[string]string{},
	}: &ActionDefinition{
		Url:    "/Implicit/Route",
		Method: "GET",
		Star:   true,
		Action: "Implicit.Route",
	},

	&ReverseRouteArgs{
		action: "Application.Save",
		args:   map[string]string{"id": "123", "c": "http://continue"},
	}: &ActionDefinition{
		Url:    "/app/123?c=http%3A%2F%2Fcontinue",
		Method: "POST",
		Star:   false,
		Action: "Application.Save",
	},
}

func TestReverseRouting(t *testing.T) {
	router := NewRouter("")
	router.Routes, _ = parseRoutes("", TEST_ROUTES, false)
	for routeArgs, expected := range reverseRoutingTestCases {
		actual := router.Reverse(routeArgs.action, routeArgs.args)
		if !eq(t, "Found route", actual != nil, expected != nil) {
			continue
		}
		eq(t, "Url", actual.Url, expected.Url)
		eq(t, "Method", actual.Method, expected.Method)
		eq(t, "Star", actual.Star, expected.Star)
		eq(t, "Action", actual.Action, expected.Action)
	}
}

func BenchmarkRouter(b *testing.B) {
	router := NewRouter("")
	router.Routes, _ = parseRoutes("", TEST_ROUTES, false)
	b.ResetTimer()
	for i := 0; i < b.N/len(routeMatchTestCases); i++ {
		for req, _ := range routeMatchTestCases {
			router.Route(req)
		}
	}
}

func BenchmarkRouterFilter(b *testing.B) {
	startFakeBookingApp()
	controllers := []*Controller{
		{Request: NewRequest(showRequest)},
		{Request: NewRequest(staticRequest)},
	}
	for _, c := range controllers {
		c.Params = &Params{}
		ParseParams(c.Params, c.Request)
	}

	b.ResetTimer()
	for i := 0; i < b.N/len(controllers); i++ {
		for _, c := range controllers {
			RouterFilter(c, NilChain)
		}
	}
}

// Helpers

func eq(t *testing.T, name string, a, b interface{}) bool {
	if a != b {
		t.Error(name, ": (actual)", a, " != ", b, "(expected)")
		return false
	}
	return true
}
<commit_msg>Add large-router benchmark<commit_after>package revel

import (
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"testing"
)

// Data-driven tests that check that a given routes-file line translates into
// the expected Route object.
var routeTestCases = map[string]*Route{
	"get / Application.Index": &Route{
		Method:        "GET",
		Path:          "/",
		Action:        "Application.Index",
		pathPattern:   regexp.MustCompile("/$"),
		args:          []*arg{},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.Index"),
	},

	"post /app/{id} Application.SaveApp": &Route{
		Method:      "POST",
		Path:        "/app/{id}",
		Action:      "Application.SaveApp",
		pathPattern: regexp.MustCompile("/app/(?P<id>[^/]+)$"),
		args: []*arg{
			{
				name:       "id",
				constraint: regexp.MustCompile("[^/]+"),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.SaveApp"),
	},

	"post /app/{<[0-9]+>id} Application.SaveApp": &Route{
		Method:      "POST",
		Path:        "/app/{<[0-9]+>id}",
		Action:      "Application.SaveApp",
		pathPattern: regexp.MustCompile("/app/(?P<id>[0-9]+)$"),
		args: []*arg{
			{
				name:       "id",
				constraint: regexp.MustCompile("[0-9]+"),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.SaveApp"),
	},

	"get /app/? Application.List": &Route{
		Method:        "GET",
		Path:          "/app/?",
		Action:        "Application.List",
		pathPattern:   regexp.MustCompile("/app/?$"),
		args:          []*arg{},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.List"),
	},

	`get /apps/{<\d+>appId}/? Application.Show`: &Route{
		Method:      "GET",
		Path:        `/apps/{<\d+>appId}/?`,
		Action:      "Application.Show",
		pathPattern: regexp.MustCompile(`/apps/(?P<appId>\d+)/?$`),
		args: []*arg{
			{
				name:       "appId",
				constraint: regexp.MustCompile(`\d+`),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.Show"),
	},

	`GET /public/{<.+>filepath}   Static.Serve("public")`: &Route{
		Method:      "GET",
		Path:        "/public/{<.+>filepath}",
		Action:      "Static.Serve",
		pathPattern: regexp.MustCompile(`/public/(?P<filepath>.+)$`),
		args: []*arg{
			{
				name:       "filepath",
				constraint: regexp.MustCompile(`.+`),
			},
		},
		FixedParams: []string{
			"public",
		},
		actionPattern: regexp.MustCompile("Static\\.Serve"),
	},

	`GET /javascript/{<.+>filepath} Static.Serve("public/js")`: &Route{
		Method:      "GET",
		Path:        "/javascript/{<.+>filepath}",
		Action:      "Static.Serve",
		pathPattern: regexp.MustCompile(`/javascript/(?P<filepath>.+)$`),
		args: []*arg{
			{
				name:       "filepath",
				constraint: regexp.MustCompile(`.+`),
			},
		},
		FixedParams: []string{
			"public",
		},
		actionPattern: regexp.MustCompile("Static\\.Serve"),
	},

	"* /apps/{id}/{action} Application.{action}": &Route{
		Method:      "*",
		Path:        "/apps/{id}/{action}",
		Action:      "Application.{action}",
		pathPattern: regexp.MustCompile("/apps/(?P<id>[^/]+)/(?P<action>[^/]+)$"),
		args: []*arg{
			{
				name:       "id",
				constraint: regexp.MustCompile("[^/]+"),
			},
			{
				name:       "action",
				constraint: regexp.MustCompile("[^/]+"),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("Application\\.(?P<action>[^/]+)"),
	},

	"* /{controller}/{action} {controller}.{action}": &Route{
		Method:      "*",
		Path:        "/{controller}/{action}",
		Action:      "{controller}.{action}",
		pathPattern: regexp.MustCompile("/(?P<controller>[^/]+)/(?P<action>[^/]+)$"),
		args: []*arg{
			{
				name:       "controller",
				constraint: regexp.MustCompile("[^/]+"),
			},
			{
				name:       "action",
				constraint: regexp.MustCompile("[^/]+"),
			},
		},
		FixedParams:   []string{},
		actionPattern: regexp.MustCompile("(?P<controller>[^/]+)\\.(?P<action>[^/]+)"),
	},
}

// Run the test cases above.
func TestComputeRoute(t *testing.T) {
	for routeLine, expected := range routeTestCases {
		method, path, action, fixedArgs, found := parseRouteLine(routeLine)
		if !found {
			t.Error("Failed to parse route line:", routeLine)
			continue
		}
		actual := NewRoute(method, path, action, fixedArgs)
		eq(t, "Method", actual.Method, expected.Method)
		eq(t, "Path", actual.Path, expected.Path)
		eq(t, "Action", actual.Action, expected.Action)
		eq(t, "pathPattern", fmt.Sprint(actual.pathPattern), fmt.Sprint(expected.pathPattern))
		eq(t, "len(args)", len(actual.args), len(expected.args))
		for i, arg := range actual.args {
			if len(expected.args) <= i {
				break
			}
			eq(t, "arg.name", arg.name, expected.args[i].name)
			eq(t, "arg.constraint", arg.constraint.String(), expected.args[i].constraint.String())
		}
		eq(t, "actionPattern", fmt.Sprint(actual.actionPattern), fmt.Sprint(expected.actionPattern))
		if t.Failed() {
			t.Fatal("Failed on route:", routeLine)
		}
	}
}

// Router Tests

const TEST_ROUTES = `
# This is a comment
GET   /                          Application.Index
GET   /app/{id}/?                Application.Show
POST  /app/{id}                  Application.Save
PATCH /app/{id}/?                Application.Update
GET   /javascript/{<.+>filepath} Static.Serve("public/js")
GET   /public/{<.+>filepath}     Static.Serve("public")
*     /{controller}/{action}     {controller}.{action}

GET   /favicon.ico               404
`

var routeMatchTestCases = map[*http.Request]*RouteMatch{
	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Index",
		FixedParams:    []string{},
		Params:         map[string]string{},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/app/123"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Show",
		FixedParams:    []string{},
		Params:         map[string]string{"id": "123"},
	},

	&http.Request{
		Method: "PATCH",
		URL:    &url.URL{Path: "/app/123"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Update",
		FixedParams:    []string{},
		Params:         map[string]string{"id": "123"},
	},

	&http.Request{
		Method: "POST",
		URL:    &url.URL{Path: "/app/123"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Save",
		FixedParams:    []string{},
		Params:         map[string]string{"id": "123"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/app/123/"},
	}: &RouteMatch{
		ControllerName: "Application",
		MethodName:     "Show",
		FixedParams:    []string{},
		Params:         map[string]string{"id": "123"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/public/style.css"},
	}: &RouteMatch{
		ControllerName: "Static",
		MethodName:     "Serve",
		FixedParams:    []string{"public"},
		Params:         map[string]string{"filepath": "style.css"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/javascript/sessvars.js"},
	}: &RouteMatch{
		ControllerName: "Static",
		MethodName:     "Serve",
		FixedParams:    []string{"public"},
		Params:         map[string]string{"filepath": "sessvars.js"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/Implicit/Route"},
	}: &RouteMatch{
		ControllerName: "Implicit",
		MethodName:     "Route",
		FixedParams:    []string{},
		Params:         map[string]string{"controller": "Implicit", "action": "Route"},
	},

	&http.Request{
		Method: "GET",
		URL:    &url.URL{Path: "/favicon.ico"},
	}: &RouteMatch{
		ControllerName: "",
		MethodName:     "",
		Action:         "404",
		FixedParams:    []string{},
		Params:         map[string]string{},
	},
}

func TestRouteMatches(t *testing.T) {
	BasePath = "/BasePath"
	router := NewRouter("")
	router.Routes, _ = parseRoutes("", TEST_ROUTES, false)
	for req, expected := range routeMatchTestCases {
		t.Log("Routing:", req.Method, req.URL)
		actual := router.Route(req)
		if !eq(t, "Found route", actual != nil, expected != nil) {
			continue
		}
		eq(t, "ControllerName", actual.ControllerName, expected.ControllerName)
		eq(t, "MethodName", actual.MethodName, expected.MethodName)
		eq(t, "len(Params)", len(actual.Params), len(expected.Params))
		for key, actualValue := range actual.Params {
			eq(t, "Params", actualValue, expected.Params[key])
		}
	}
}

// Reverse Routing

type ReverseRouteArgs struct {
	action string
	args   map[string]string
}

var reverseRoutingTestCases = map[*ReverseRouteArgs]*ActionDefinition{
	&ReverseRouteArgs{
		action: "Application.Index",
		args:   map[string]string{},
	}: &ActionDefinition{
		Url:    "/",
		Method: "GET",
		Star:   false,
		Action: "Application.Index",
	},

	&ReverseRouteArgs{
		action: "Application.Show",
		args:   map[string]string{"id": "123"},
	}: &ActionDefinition{
		Url:    "/app/123/",
		Method: "GET",
		Star:   false,
		Action: "Application.Show",
	},

	&ReverseRouteArgs{
		action: "Implicit.Route",
		args:   map[string]string{},
	}: &ActionDefinition{
		Url:    "/Implicit/Route",
		Method: "GET",
		Star:   true,
		Action: "Implicit.Route",
	},

	&ReverseRouteArgs{
		action: "Application.Save",
		args:   map[string]string{"id": "123", "c": "http://continue"},
	}: &ActionDefinition{
		Url:    "/app/123?c=http%3A%2F%2Fcontinue",
		Method: "POST",
		Star:   false,
		Action: "Application.Save",
	},
}

func TestReverseRouting(t *testing.T) {
	router := NewRouter("")
	router.Routes, _ = parseRoutes("", TEST_ROUTES, false)
	for routeArgs, expected := range reverseRoutingTestCases {
		actual := router.Reverse(routeArgs.action, routeArgs.args)
		if !eq(t, "Found route", actual != nil, expected != nil) {
			continue
		}
		eq(t, "Url", actual.Url, expected.Url)
		eq(t, "Method", actual.Method, expected.Method)
		eq(t, "Star", actual.Star, expected.Star)
		eq(t, "Action", actual.Action, expected.Action)
	}
}

func BenchmarkRouter(b *testing.B) {
	router := NewRouter("")
	router.Routes, _ = parseRoutes("", TEST_ROUTES, false)
	b.ResetTimer()
	for i := 0; i < b.N/len(routeMatchTestCases); i++ {
		for req, _ := range routeMatchTestCases {
			router.Route(req)
		}
	}
}

// The benchmark from github.com/ant0ine/go-urlrouter
func BenchmarkLargeRouter(b *testing.B) {
	router := NewRouter("")

	routePaths := []string{
		"/",
		"/signin",
		"/signout",
		"/profile",
		"/settings",
		"/upload/*file",
	}
	for i := 0; i < 10; i++ {
		for j := 0; j < 5; j++ {
			routePaths = append(routePaths, fmt.Sprintf("/resource%d/{id}/property%d", i, j))
		}
		routePaths = append(routePaths, fmt.Sprintf("/resource%d/{id}", i))
		routePaths = append(routePaths, fmt.Sprintf("/resource%d", i))
	}
	routePaths = append(routePaths, "/{any}")

	for _, p := range routePaths {
		router.Routes = append(router.Routes, NewRoute("GET", p, "Controller.Action", ""))
	}

	requestUrls := []string{
		"http://example.org/",
		"http://example.org/resource9/123",
		"http://example.org/resource9/123/property1",
		"http://example.org/doesnotexist",
	}
	var reqs []*http.Request
	for _, url := range requestUrls {
		req, _ := http.NewRequest("GET", url, nil)
		reqs = append(reqs, req)
	}

	b.ResetTimer()

	for i := 0; i < b.N/len(reqs); i++ {
		for _, req := range reqs {
			route := router.Route(req)
			if route == nil {
				b.Errorf("Failed to route: %s", req.URL.Path)
			}
		}
	}
}

func BenchmarkRouterFilter(b *testing.B) {
	startFakeBookingApp()
	controllers := []*Controller{
		{Request: NewRequest(showRequest)},
		{Request: NewRequest(staticRequest)},
	}
	for _, c := range controllers {
		c.Params = &Params{}
		ParseParams(c.Params, c.Request)
	}

	b.ResetTimer()
	for i := 0; i < b.N/len(controllers); i++ {
		for _, c := range controllers {
			RouterFilter(c, NilChain)
		}
	}
}

// Helpers

func eq(t *testing.T, name string, a, b interface{}) bool {
	if a != b {
		t.Error(name, ": (actual)", a, " != ", b, "(expected)")
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package spf

import (
	"net"

	"github.com/zaccone/goSPF/dns"
)

type SPFResult int

const (
	Illegal SPFResult = iota

	None
	Neutral
	Pass
	Fail
	Softfail
	Temperror
	Permerror

	// end of SPF
	SPFEnd
)

func (spf SPFResult) String() string {
	switch *spf {
	case None:
		return "None"
	case Neutral:
		return "Neutral"
	case Pass:
		return "Pass"
	case Fail:
		return "Fail"
	case Temperror:
		return "Temperror"
	case Permerror:
		return "Permerror"
	default:
		return "Permerror"
	}
}

// CheckHost is a main entrypoint function evaluating e-mail with regard to SPF
// As per RFC 7208 it will accept 3 parameters (strings):
// <ip> - IP{4,6} address of the connected client
// <domain> - domain portion of the MAIL FROM or HELO identity
// <sender> - MAIL FROM or HELO identity
// All the parameters should be parsed and dereferenced from real email fields.
// This means domain should already be extracted from MAIL FROM field so this
// function can focus on the core part.
func checkHost(ip net.IP, domain, sender string) (SPFResult, error) {

	// TODO(zaccone) s/_/spfRecord/
	_, dnsErr := dns.LookupSPF(domain)

	if dnsErr != nil {
		switch dnsErr.(type) {
		// as per RFC7208 section 4.4, and DNS query errors result in Temperror
		//result immediately
		case *net.DNSError:
			return Temperror, dnsErr
		default:
			return Permerror, dnsErr

		}
	}
	query := "v=spf1 a -all"
	parser := NewParser(sender, domain, ip, query)

	var result SPFResult = Neutral
	var err error

	if result, err = parser.Parse(); err != nil {
		// handle error, something went wrong.
		// Let's set PermError for now.
		return Permerror, nil
	}

	// check if there is redirect and recursively call check_host with redirect
	// as domain
	if parser.Redirect != nil {
		result, err = checkHost(ip, domain, sender)
	}

	// return SPF evaluation result
	return result, nil
}
<commit_msg>Fix SPFResult.String().<commit_after>package spf

import (
	"net"

	"github.com/zaccone/goSPF/dns"
)

type SPFResult int

const (
	Illegal SPFResult = iota

	None
	Neutral
	Pass
	Fail
	Softfail
	Temperror
	Permerror

	// end of SPF
	SPFEnd
)

func (spf SPFResult) String() string {
	switch spf {
	case None:
		return "None"
	case Neutral:
		return "Neutral"
	case Pass:
		return "Pass"
	case Fail:
		return "Fail"
	case Temperror:
		return "Temperror"
	case Permerror:
		return "Permerror"
	default:
		return "Permerror"
	}
}

// CheckHost is a main entrypoint function evaluating e-mail with regard to SPF
// As per RFC 7208 it will accept 3 parameters (strings):
// <ip> - IP{4,6} address of the connected client
// <domain> - domain portion of the MAIL FROM or HELO identity
// <sender> - MAIL FROM or HELO identity
// All the parameters should be parsed and dereferenced from real email fields.
// This means domain should already be extracted from MAIL FROM field so this
// function can focus on the core part.
func checkHost(ip net.IP, domain, sender string) (SPFResult, error) {

	// TODO(zaccone) s/_/spfRecord/
	_, dnsErr := dns.LookupSPF(domain)

	if dnsErr != nil {
		switch dnsErr.(type) {
		// as per RFC7208 section 4.4, and DNS query errors result in Temperror
		//result immediately
		case *net.DNSError:
			return Temperror, dnsErr
		default:
			return Permerror, dnsErr

		}
	}
	query := "v=spf1 a -all"
	parser := NewParser(sender, domain, ip, query)

	var result SPFResult = Neutral
	var err error

	if result, err = parser.Parse(); err != nil {
		// handle error, something went wrong.
		// Let's set PermError for now.
		return Permerror, nil
	}

	// check if there is redirect and recursively call check_host with redirect
	// as domain
	if parser.Redirect != nil {
		result, err = checkHost(ip, domain, sender)
	}

	// return SPF evaluation result
	return result, nil
}
<|endoftext|>


<commit_before>package term

import "syscall"

type attr syscall.Termios

func (a *attr) setSpeed(baud int) error {
	var rate uint32
	switch baud {
	case 50:
		rate = syscall.B50
	case 75:
		rate = syscall.B75
	case 110:
		rate = syscall.B110
	case 134:
		rate = syscall.B134
	case 150:
		rate = syscall.B150
	case 200:
		rate = syscall.B200
	case 300:
		rate = syscall.B300
	case 600:
		rate = syscall.B600
	case 1200:
		rate = syscall.B1200
	case 1800:
		rate = syscall.B1800
	case 2400:
		rate = syscall.B2400
	case 4800:
		rate = syscall.B4800
	case 9600:
		rate = syscall.B9600
	case 19200:
		rate = syscall.B19200
	case 38400:
		rate = syscall.B38400
	case 57600:
		rate = syscall.B57600
	case 115200:
		rate = syscall.B115200
	case 230400:
		rate = syscall.B230400
	case 460800:
		rate = syscall.B460800
	case 500000:
		rate = syscall.B500000
	case 576000:
		rate = syscall.B576000
	case 921600:
		rate = syscall.B921600
	case 1000000:
		rate = syscall.B1000000
	case 1152000:
		rate = syscall.B1152000
	case 1500000:
		rate = syscall.B1500000
	case 2000000:
		rate = syscall.B2000000
	case 2500000:
		rate = syscall.B2500000
	case 3000000:
		rate = syscall.B3000000
	case 3500000:
		rate = syscall.B3500000
	case 4000000:
		rate = syscall.B4000000
	default:
		return syscall.EINVAL
	}
	(*syscall.Termios)(a).Cflag = syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate
	(*syscall.Termios)(a).Ispeed = rate
	(*syscall.Termios)(a).Ospeed = rate
	return nil
}

// Available returns the number of bytes available in the local buffer.
func (t *Term) Available() (int, error) {
	var n int
	err := termios.Tiocinq(uintptr(t.fd), &n)
	return n, err
}
<commit_msg>added Buffered and fix term_linux.go<commit_after>package term

import "syscall"
import "github.com/pkg/term/termios"

type attr syscall.Termios

func (a *attr) setSpeed(baud int) error {
	var rate uint32
	switch baud {
	case 50:
		rate = syscall.B50
	case 75:
		rate = syscall.B75
	case 110:
		rate = syscall.B110
	case 134:
		rate = syscall.B134
	case 150:
		rate = syscall.B150
	case 200:
		rate = syscall.B200
	case 300:
		rate = syscall.B300
	case 600:
		rate = syscall.B600
	case 1200:
		rate = syscall.B1200
	case 1800:
		rate = syscall.B1800
	case 2400:
		rate = syscall.B2400
	case 4800:
		rate = syscall.B4800
	case 9600:
		rate = syscall.B9600
	case 19200:
		rate = syscall.B19200
	case 38400:
		rate = syscall.B38400
	case 57600:
		rate = syscall.B57600
	case 115200:
		rate = syscall.B115200
	case 230400:
		rate = syscall.B230400
	case 460800:
		rate = syscall.B460800
	case 500000:
		rate = syscall.B500000
	case 576000:
		rate = syscall.B576000
	case 921600:
		rate = syscall.B921600
	case 1000000:
		rate = syscall.B1000000
	case 1152000:
		rate = syscall.B1152000
	case 1500000:
		rate = syscall.B1500000
	case 2000000:
		rate = syscall.B2000000
	case 2500000:
		rate = syscall.B2500000
	case 3000000:
		rate = syscall.B3000000
	case 3500000:
		rate = syscall.B3500000
	case 4000000:
		rate = syscall.B4000000
	default:
		return syscall.EINVAL
	}
	(*syscall.Termios)(a).Cflag = syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate
	(*syscall.Termios)(a).Ispeed = rate
	(*syscall.Termios)(a).Ospeed = rate
	return nil
}

// Available returns how many bytes are unused in the buffer.
func (t *Term) Available() (int, error) {
	var n int
	err := termios.Tiocinq(uintptr(t.fd), &n)
	return n, err
}

// Buffered returns the number of bytes that have been written into the current buffer.
func (t *Term) Buffered() (int, error) {
	var n int
	err := termios.Tiocoutq(uintptr(t.fd), &n)
	return n, err
}
<|endoftext|>


<commit_before>package template_test

import (
	"testing"

	socktmpl "github.com/hashicorp/go-sockaddr/template"
)

func TestSockAddr_Parse(t *testing.T) {
	tests := []struct {
		name          string
		input         string
		output        string
		fail          bool
		requireOnline bool
	}{
		{
			name:   `basic include "name"`,
			input:  `{{GetAllInterfaces | include "name" "lo0" | printf "%v"}}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast} ::1 {1 16384 lo0  up|loopback|multicast} fe80::1/64 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   "GetDefaultInterface",
			input:  `{{GetDefaultInterfaces | include "type" "IPv4" | attr "name" }}`,
			output: `en0`,
		},
		{
			name:   `include "name" regexp`,
			input:  `{{GetAllInterfaces | include "name" "^(en|lo)0$" | exclude "name" "^en0$" | sort "type" | sort "address" | join "address" " " }}`,
			output: `127.0.0.1 ::1 fe80::1`,
		},
		{
			name:   `exclude "name"`,
			input:  `{{. | include "name" "^(en|lo)0$" | exclude "name" "^en0$" | sort "type" | sort "address" | join "address" " " }}`,
			output: `127.0.0.1 ::1 fe80::1`,
		},
		{
			name:   `"dot" pipeline, IPv4 type`,
			input:  `{{. | include "type" "IPv4" | include "name" "^lo0$" | sort "type" | sort "address" }}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   `include "type" "IPv6`,
			input:  `{{. | include "type" "IPv6" | include "name" "^lo0$" | sort "address" }}`,
			output: `[::1 {1 16384 lo0  up|loopback|multicast} fe80::1/64 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   "better example for IP types",
			input:  `{{. | include "type" "IPv4|IPv6" | include "name" "^lo0$" | sort "type" | sort "address" }}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast} ::1 {1 16384 lo0  up|loopback|multicast} fe80::1/64 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   "ifAddrs1",
			input:  `{{. | include "type" "IPv4" | include "name" "^lo0$"}}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   "ifAddrs2",
			input:  `{{. | include "type" "IP" | include "name" "^lo0$" | sort "type" | sort "address" }}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast} ::1 {1 16384 lo0  up|loopback|multicast} fe80::1/64 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   `range "dot" example`,
			input:  `{{range . | include "type" "IP" | include "name" "^lo0$"}}{{.Name}} {{.SockAddr}} {{end}}`,
			output: `lo0 127.0.0.1/8 lo0 ::1 lo0 fe80::1/64 `,
		},
		{
			name:   `exclude "type"`,
			input:  `{{. | exclude "type" "IPv4" | include "name" "^lo0$" | sort "address" | unique "name" | join "name" " "}} {{range . | exclude "type" "IPv4" | include "name" "^lo0$"}}{{.SockAddr}} {{end}}`,
			output: `lo0 ::1 fe80::1/64 `,
		},
		{
			name:   "with variable pipeline",
			input:  `{{with $ifSet := include "type" "IPv4" . | include "name" "^lo0$"}}{{range $ifSet }}{{.Name}} {{end}}{{range $ifSet}}{{.SockAddr}} {{end}}{{end}}`,
			output: `lo0 127.0.0.1/8 `,
		},
		{
			name:   "range sample on lo0",
			input:  `{{with $ifAddrs := . | exclude "rfc" "1918" | include "name" "lo0" | sort "type,address" }}{{range $ifAddrs }}{{.Name}}/{{.SockAddr.NetIP}} {{end}}{{end}}`,
			output: `lo0/127.0.0.1 lo0/::1 lo0/fe80::1 `,
		},
		{
			name:   "non-RFC1918 on on lo0",
			input:  `{{. | exclude "rfc" "1918" | include "name" "lo0" | sort "type,address" | len | eq 3}}`,
			output: `true`,
		},
		{
			// NOTE(sean@): Difficult to reliably test includeByRFC.
			// In this case, we ass-u-me that the host running the
			// test has at least one RFC1918 address on their host.
			name:          `include "rfc"`,
			input:         `{{(. | include "rfc" "1918" | attr "name")}}`,
			output:        `en0`,
			requireOnline: true,
		},
		{
			name:   "test for non-empty array",
			input:  `{{. | include "type" "IPv4" | include "rfc" "1918" | print | len | eq (len "[]")}}`,
			output: `false`,
		},
		{
			// NOTE(sean@): This will fail if there is a public IPv4 address on loopback.
			name:   "non-IPv4 RFC1918",
			input:  `{{. | include "name" "lo0" | exclude "type" "IPv4" | include "rfc" "1918" | len | eq 0}}`,
			output: `true`,
		},
		{
			// NOTE(sean@): There are no RFC6598 addresses on most testing hosts so this should be empty.
			name:   "rfc6598",
			input:  `{{. | include "type" "IPv4" | include "rfc" "6598" | print | len | eq (len "[]")}}`,
			output: `true`,
		},
		{
			name:   "invalid RFC",
			input:  `{{. | include "type" "IPv4" | include "rfc" "99999999999" | print | len | eq (len "[]")}}`,
			output: `true`,
			fail:   true,
		},
		{
			name:   `sort asc address`,
			input:  `{{ . | include "name" "lo0" | sort "type,address" | join "address" " " }}`,
			output: `127.0.0.1 ::1 fe80::1`,
		},
		{
			name:   `sort asc address old`,
			input:  `{{with $ifSet := include "name" "lo0" . }}{{ range include "type" "IPv4" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{ range include "type" "IPv6" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{end}}`,
			output: `127.0.0.1/8 ::1 fe80::1/64 `,
		},
		{
			name:   `sort desc address`,
			input:  `{{ . | include "name" "lo0" | sort "type,-address" | join "address" " " }}`,
			output: `127.0.0.1 fe80::1 ::1`,
		},
		{
			name:   `sort desc address`,
			input:  `{{ . | include "name" "lo0" | include "type" "IPv6" | sort "type,-address" | join "address" " " }}`,
			output: `fe80::1 ::1`,
		},
		{
			name:   `sort asc address`,
			input:  `{{with $ifSet := include "name" "lo0" . }}{{ range include "type" "IPv6" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{end}}`,
			output: `::1 fe80::1/64 `,
		},
		{
			name:   "lo0 limit 1",
			input:  `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | limit 1 | len}}`,
			output: `1`,
		},
		{
			name:   "join address",
			input:  `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "address" " " }}`,
			output: `::1 fe80::1`,
		},
		{
			name:   "join name",
			input:  `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "name" " " }}`,
			output: `lo0 lo0`,
		},
		{
			name:   "lo0 flags up and limit 1",
			input:  `{{. | include "name" "lo0" | include "flag" "up" | sort "-type,+address" | attr "address" }}`,
			output: `::1`,
		},
		{
			// NOTE(sean@): This is the HashiCorp default in 2016.
			// Indented for effect.  Using "true" as the output
			// instead of printing the correct $rfc*Addrs values.
			name: "HashiCorpDefault2016",
			input: `
{{- with $addr := GetAllInterfaces | include "type" "IP" | include "rfc" "1918|6598" | sort "address" | attr "address" -}}

  {{- if ($addr | len) gt 0 -}}
    {{- print "true" -}}{{/* print $addr*/ -}}
  {{- end -}}
{{- end -}}`,
			output: `true`,
		},
	}

	for i, test := range tests {
		test := test // capture range variable
		if test.name == "" {
			t.Fatalf("test number %d has an empty test name", i)
		}
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()
			out, err := socktmpl.Parse(test.input)
			if err != nil && !test.fail {
				t.Fatalf("%q: bad: %v", test.name, err)
			}

			if out != test.output && !test.fail {
				t.Fatalf("%q: Expected %+q, received %+q\n%+q", test.name, test.output, out, test.input)
			}
		})
	}
}
<commit_msg>Cover the last of the gap for the template/ package.<commit_after>package template_test

import (
	"testing"

	socktmpl "github.com/hashicorp/go-sockaddr/template"
)

func TestSockAddr_Parse(t *testing.T) {
	tests := []struct {
		name          string
		input         string
		output        string
		fail          bool
		requireOnline bool
	}{
		{
			name:   `basic include "name"`,
			input:  `{{GetAllInterfaces | include "name" "lo0" | printf "%v"}}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast} ::1 {1 16384 lo0  up|loopback|multicast} fe80::1/64 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   "invalid input",
			input:  `{{`,
			output: ``,
			fail:   true,
		},
		{
			name:   "GetDefaultInterface",
			input:  `{{GetDefaultInterfaces | include "type" "IPv4" | attr "name" }}`,
			output: `en0`,
		},
		{
			name:   `include "name" regexp`,
			input:  `{{GetAllInterfaces | include "name" "^(en|lo)0$" | exclude "name" "^en0$" | sort "type" | sort "address" | join "address" " " }}`,
			output: `127.0.0.1 ::1 fe80::1`,
		},
		{
			name:   `exclude "name"`,
			input:  `{{. | include "name" "^(en|lo)0$" | exclude "name" "^en0$" | sort "type" | sort "address" | join "address" " " }}`,
			output: `127.0.0.1 ::1 fe80::1`,
		},
		{
			name:   `"dot" pipeline, IPv4 type`,
			input:  `{{. | include "type" "IPv4" | include "name" "^lo0$" | sort "type" | sort "address" }}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   `include "type" "IPv6`,
			input:  `{{. | include "type" "IPv6" | include "name" "^lo0$" | sort "address" }}`,
			output: `[::1 {1 16384 lo0  up|loopback|multicast} fe80::1/64 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   "better example for IP types",
			input:  `{{. | include "type" "IPv4|IPv6" | include "name" "^lo0$" | sort "type" | sort "address" }}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast} ::1 {1 16384 lo0  up|loopback|multicast} fe80::1/64 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   "ifAddrs1",
			input:  `{{. | include "type" "IPv4" | include "name" "^lo0$"}}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   "ifAddrs2",
			input:  `{{. | include "type" "IP" | include "name" "^lo0$" | sort "type" | sort "address" }}`,
			output: `[127.0.0.1/8 {1 16384 lo0  up|loopback|multicast} ::1 {1 16384 lo0  up|loopback|multicast} fe80::1/64 {1 16384 lo0  up|loopback|multicast}]`,
		},
		{
			name:   `range "dot" example`,
			input:  `{{range . | include "type" "IP" | include "name" "^lo0$"}}{{.Name}} {{.SockAddr}} {{end}}`,
			output: `lo0 127.0.0.1/8 lo0 ::1 lo0 fe80::1/64 `,
		},
		{
			name:   `exclude "type"`,
			input:  `{{. | exclude "type" "IPv4" | include "name" "^lo0$" | sort "address" | unique "name" | join "name" " "}} {{range . | exclude "type" "IPv4" | include "name" "^lo0$"}}{{.SockAddr}} {{end}}`,
			output: `lo0 ::1 fe80::1/64 `,
		},
		{
			name:   "with variable pipeline",
			input:  `{{with $ifSet := include "type" "IPv4" . | include "name" "^lo0$"}}{{range $ifSet }}{{.Name}} {{end}}{{range $ifSet}}{{.SockAddr}} {{end}}{{end}}`,
			output: `lo0 127.0.0.1/8 `,
		},
		{
			name:   "range sample on lo0",
			input:  `{{with $ifAddrs := . | exclude "rfc" "1918" | include "name" "lo0" | sort "type,address" }}{{range $ifAddrs }}{{.Name}}/{{.SockAddr.NetIP}} {{end}}{{end}}`,
			output: `lo0/127.0.0.1 lo0/::1 lo0/fe80::1 `,
		},
		{
			name:   "non-RFC1918 on on lo0",
			input:  `{{. | exclude "rfc" "1918" | include "name" "lo0" | sort "type,address" | len | eq 3}}`,
			output: `true`,
		},
		{
			// NOTE(sean@): Difficult to reliably test includeByRFC.
			// In this case, we ass-u-me that the host running the
			// test has at least one RFC1918 address on their host.
			name:          `include "rfc"`,
			input:         `{{(. | include "rfc" "1918" | attr "name")}}`,
			output:        `en0`,
			requireOnline: true,
		},
		{
			name:   "test for non-empty array",
			input:  `{{. | include "type" "IPv4" | include "rfc" "1918" | print | len | eq (len "[]")}}`,
			output: `false`,
		},
		{
			// NOTE(sean@): This will fail if there is a public IPv4 address on loopback.
			name:   "non-IPv4 RFC1918",
			input:  `{{. | include "name" "lo0" | exclude "type" "IPv4" | include "rfc" "1918" | len | eq 0}}`,
			output: `true`,
		},
		{
			// NOTE(sean@): There are no RFC6598 addresses on most testing hosts so this should be empty.
			name:   "rfc6598",
			input:  `{{. | include "type" "IPv4" | include "rfc" "6598" | print | len | eq (len "[]")}}`,
			output: `true`,
		},
		{
			name:   "invalid RFC",
			input:  `{{. | include "type" "IPv4" | include "rfc" "99999999999" | print | len | eq (len "[]")}}`,
			output: `true`,
			fail:   true,
		},
		{
			name:   `sort asc address`,
			input:  `{{ . | include "name" "lo0" | sort "type,address" | join "address" " " }}`,
			output: `127.0.0.1 ::1 fe80::1`,
		},
		{
			name:   `sort asc address old`,
			input:  `{{with $ifSet := include "name" "lo0" . }}{{ range include "type" "IPv4" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{ range include "type" "IPv6" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{end}}`,
			output: `127.0.0.1/8 ::1 fe80::1/64 `,
		},
		{
			name:   `sort desc address`,
			input:  `{{ . | include "name" "lo0" | sort "type,-address" | join "address" " " }}`,
			output: `127.0.0.1 fe80::1 ::1`,
		},
		{
			name:   `sort desc address`,
			input:  `{{ . | include "name" "lo0" | include "type" "IPv6" | sort "type,-address" | join "address" " " }}`,
			output: `fe80::1 ::1`,
		},
		{
			name:   `sort asc address`,
			input:  `{{with $ifSet := include "name" "lo0" . }}{{ range include "type" "IPv6" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{end}}`,
			output: `::1 fe80::1/64 `,
		},
		{
			name:   "lo0 limit 1",
			input:  `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | limit 1 | len}}`,
			output: `1`,
		},
		{
			name:   "join address",
			input:  `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "address" " " }}`,
			output: `::1 fe80::1`,
		},
		{
			name:   "join name",
			input:  `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "name" " " }}`,
			output: `lo0 lo0`,
		},
		{
			name:   "lo0 flags up and limit 1",
			input:  `{{. | include "name" "lo0" | include "flag" "up" | sort "-type,+address" | attr "address" }}`,
			output: `::1`,
		},
		{
			// NOTE(sean@): This is the HashiCorp default in 2016.
			// Indented for effect.  Using "true" as the output
			// instead of printing the correct $rfc*Addrs values.
			name: "HashiCorpDefault2016",
			input: `
{{- with $addr := GetAllInterfaces | include "type" "IP" | include "rfc" "1918|6598" | sort "address" | attr "address" -}}

  {{- if ($addr | len) gt 0 -}}
    {{- print "true" -}}{{/* print $addr*/ -}}
  {{- end -}}
{{- end -}}`,
			output: `true`,
		},
	}

	for i, test := range tests {
		test := test // capture range variable
		if test.name == "" {
			t.Fatalf("test number %d has an empty test name", i)
		}
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()
			out, err := socktmpl.Parse(test.input)
			if err != nil && !test.fail {
				t.Fatalf("%q: bad: %v", test.name, err)
			}

			if out != test.output && !test.fail {
				t.Fatalf("%q: Expected %+q, received %+q\n%+q", test.name, test.output, out, test.input)
			}
		})
	}
}
<|endoftext|>


<commit_before>package fsck

import (
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/pkg/xattr"

	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/tests/test_helpers"
)

func TestBrokenFsV14(t *testing.T) {
	// git does not save extended attributes, so we apply them here.
	// xattr_good
	xattr.Set("broken_fs_v1.4/6nGs4Ugr3EAHd0KzkyLZ-Q",
		"user.gocryptfs.0a5e7yWl0SGUGeWB0Sy2Kg",
		[]byte("hxnZvXSkDicfwVS9w4r1yYkFF61Qou6NaL-VhObYEdu6kuM"))
	// xattr_corrupt_name
	xattr.Set("broken_fs_v1.4/CMyUifVTjW5fsgXonWBT_RDkvLkdGrLttkZ45T3Oi3A",
		"user.gocryptfs.0a5e7yWl0SGUGeWB0Sy2K0",
		[]byte("QHUMDTgbnl8Sv_A2dFQic_G2vN4_gmDna3651JAhF7OZ-YI"))
	// xattr_corrupt_value
	xattr.Set("broken_fs_v1.4/b00sbnGXGToadr01GHZaYQn8tjyRhe1OXNBZoQtMlcQ",
		"user.gocryptfs.0a5e7yWl0SGUGeWB0Sy2Kg",
		[]byte("A0hvCePeKpL8bCpijhDKtf7cIijXYQsPnEbNJ84M2ONW0dd"))

	cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", "broken_fs_v1.4")
	outBin, err := cmd.CombinedOutput()
	out := string(outBin)
	t.Log(out)
	code := test_helpers.ExtractCmdExitCode(err)
	if code != exitcodes.FsckErrors {
		t.Errorf("wrong exit code, have=%d want=%d", code, exitcodes.FsckErrors)
	}
}

func TestExampleFses(t *testing.T) {
	dirfd, err := os.Open("../example_filesystems")
	if err != nil {
		t.Fatal(err)
	}
	var fsNames []string
	entries, err := dirfd.Readdir(0)
	if err != nil {
		t.Fatal(err)
	}
	for _, e := range entries {
		if !e.IsDir() {
			continue
		}
		if strings.Contains(e.Name(), "reverse") {
			continue
		}
		if e.Name() == "content" {
			continue
		}
		fsNames = append(fsNames, e.Name())
	}
	for _, n := range fsNames {
		path := "../example_filesystems/" + n
		cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", path)
		outBin, err := cmd.CombinedOutput()
		out := string(outBin)
		code := test_helpers.ExtractCmdExitCode(err)
		if code == exitcodes.DeprecatedFS {
			continue
		}
		if code != 0 {
			t.Log(out)
			t.Errorf("fsck returned code %d but fs should be clean", code)
		}
	}
}
<commit_msg>fsck: update testcases with binary xattrs<commit_after>package fsck

import (
	"encoding/base64"
	"os"
	"os/exec"
	"strings"
	"testing"

	"github.com/pkg/xattr"

	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/tests/test_helpers"
)

func dec64(in string) (out []byte) {
	out, err := base64.RawURLEncoding.DecodeString(in)
	if err != nil {
		panic(err)
	}
	return out
}

func TestBrokenFsV14(t *testing.T) {
	// git does not save extended attributes, so we apply them here.
	// xattr_good
	xattr.Set("broken_fs_v1.4/6nGs4Ugr3EAHd0KzkyLZ-Q",
		"user.gocryptfs.0a5e7yWl0SGUGeWB0Sy2Kg",
		dec64("hxnZvXSkDicfwVS9w4r1yYkFF61Qou6NaL-VhObYEdu6kuM"))
	// xattr_corrupt_name
	xattr.Set("broken_fs_v1.4/CMyUifVTjW5fsgXonWBT_RDkvLkdGrLttkZ45T3Oi3A",
		"user.gocryptfs.0a5e7yWl0SGUGeWB0Sy2K0",
		dec64("QHUMDTgbnl8Sv_A2dFQic_G2vN4_gmDna3651JAhF7OZ-YI"))
	// xattr_corrupt_value
	xattr.Set("broken_fs_v1.4/b00sbnGXGToadr01GHZaYQn8tjyRhe1OXNBZoQtMlcQ",
		"user.gocryptfs.0a5e7yWl0SGUGeWB0Sy2Kg",
		dec64("A0hvCePeKpL8bCpijhDKtf7cIijXYQsPnEbNJ84M2ONW0dd"))

	cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", "broken_fs_v1.4")
	outBin, err := cmd.CombinedOutput()
	out := string(outBin)
	t.Log(out)
	code := test_helpers.ExtractCmdExitCode(err)
	if code != exitcodes.FsckErrors {
		t.Errorf("wrong exit code, have=%d want=%d", code, exitcodes.FsckErrors)
	}
}

func TestExampleFses(t *testing.T) {
	dirfd, err := os.Open("../example_filesystems")
	if err != nil {
		t.Fatal(err)
	}
	var fsNames []string
	entries, err := dirfd.Readdir(0)
	if err != nil {
		t.Fatal(err)
	}
	for _, e := range entries {
		if !e.IsDir() {
			continue
		}
		if strings.Contains(e.Name(), "reverse") {
			continue
		}
		if e.Name() == "content" {
			continue
		}
		fsNames = append(fsNames, e.Name())
	}
	for _, n := range fsNames {
		path := "../example_filesystems/" + n
		cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", path)
		outBin, err := cmd.CombinedOutput()
		out := string(outBin)
		code := test_helpers.ExtractCmdExitCode(err)
		if code == exitcodes.DeprecatedFS {
			continue
		}
		if code != 0 {
			t.Log(out)
			t.Errorf("fsck returned code %d but fs should be clean", code)
		}
	}
}
<|endoftext|>


<commit_before>package template

import (
	"bytes"
	"gnd.la/loaders"
	"gnd.la/template/assets"
	"testing"
)

type templateTest struct {
	tmpl   string
	data   interface{}
	result string
}

type testType struct {
}

func (t *testType) Foo() string {
	return "bar"
}

func (t *testType) Bar(s string) string {
	return "bared-" + s
}

var (
	ftests = []*templateTest{
		{"{{ $one := 1 }}{{ $two := 2 }}{{ $three := 3 }}{{ $one }}+{{ $two }}+{{ $three }}={{ add $one $two $three }}", nil, "1+2+3=6"},
		{"{{ add 2 3 }}", nil, "5"},
		{"{{ to_lower .foo }}", map[string]string{"foo": "BAR"}, "bar"},
		{"{{ to_upper .foo }}", map[string]string{"foo": "bar"}, "BAR"},
		{"{{ join .chars .sep }}", map[string]interface{}{"chars": []string{"a", "b", "c"}, "sep": ","}, "a,b,c"},
		{"{{ to_html .s }}", map[string]string{"s": "<foo\nbar"}, "<foo
bar"},
		{"{{ mult 2 1.1 }}", nil, "2.2"},
		{"{{ imult 2 1.1 }}", nil, "2"},
		{"{{ concat \"foo\" \"bar\" }}", nil, "foobar"},
		{"{{ concat (concat \"foo\" \"bar\") \"baz\" }}", nil, "foobarbaz"},
		{"{{ if divisible 5 2 }}1{{ else }}0{{ end }}", nil, "0"},
		{"{{ if divisible 4 2 }}1{{ else }}0{{ end }}", nil, "1"},
	}
	compilerTests = []*templateTest{
		{"{{ \"output\" | printf \"%s\" }}", nil, "output"},
		{"{{ call .foo }}", map[string]interface{}{"foo": func() string { return "bar" }}, "bar"},
		{"{{ .Foo }}", struct{ Foo string }{"bar"}, "bar"},
		{"{{ .Foo }}", &testType{}, "bar"},
		{"{{ .Bar \"this\" }}", &testType{}, "bared-this"},
		{"{{ .t.Bar .foo }}", map[string]interface{}{"t": &testType{}, "foo": "foo"}, "bared-foo"},
		{"{{ .t.Bar (concat .foo \"bar\") }}", map[string]interface{}{"t": &testType{}, "foo": "foo"}, "bared-foobar"},
		{"{{ with .A }}{{ . }}{{ else }}no{{ end }}", map[string]string{"A": "yes"}, "yes"},
		{"{{ with .A }}{{ . }}{{ else }}no{{ end }}", nil, "no"},
		{"{{ with .A }}{{ . }}{{ end }}", nil, ""},
		{"{{ range . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range . }}{{ . }}{{ end }}{{ . }}", []int{1, 2, 3}, "123[1 2 3]"},
		{"{{ range $idx, $el := . }}{{ $idx }}{{ $el }}{{ end }}", []int{1, 2, 3}, "011223"},
		{"{{ range $el := . }}{{ $el }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range $el := . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range $idx, $el := . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range . }}{{ else }}nope{{ end }}", nil, "nope"},
		{"{{ range $k, $v := . }}{{ $k }}={{ $v }}{{ end }}", map[string]int{"b": 2, "c": 3, "a": 1}, "a=1b=2c=3"},
		{"{{ range . }}{{ range . }}{{ if even . }}{{ . }}{{ end }}{{ end }}{{ end }}", [][]int{[]int{1, 2, 3, 4, 5, 6}}, "246"},
		{"{{ define \"a\" }}a{{ end }}{{ range . }}{{ template \"a\" . }}{{ end }}", []int{1, 2, 3}, "aaa"},
		{"{{ define \"a\" }}a{{ . }}{{ . }}{{ end }}{{ range . }}{{ template \"a\" . }}{{ end }}", []int{1, 2, 3}, "a11a22a33"},
		{"{{ define \"a\" }}a{{ . }}{{ . }}{{ end }}{{ if . }}{{ template \"a\" . }}{{ end }}", 0, ""},
		{"{{ define \"a\" }}a{{ . }}{{ . }}{{ end }}{{ if . }}{{ template \"a\" . }}{{ end }}", 1, "a11"},
	}
	compilerErrorTests = []*templateTest{
		{"{{ range . }}{{ else }}nope{{ end }}", 5, "template.html:1:9: can't range over int"},
		{"{{ . }}\n{{ range . }}{{ else }}nope{{ end }}", 5, "template.html:2:9: can't range over int"},
		{"{{ . }}\n{{ range .foo }}{{ else }}nope{{ end }}\n{{ range .bar }}{{ . }}{{ end }} ", map[string]interface{}{"foo": []int{}, "bar": ""}, "template.html:3:9: can't range over string"},
	}
)

func parseText(tb testing.TB, text string) *Template {
	loader := loaders.MapLoader(map[string][]byte{"template.html": []byte(text)})
	tmpl, err := Parse(loader, nil, "template.html")
	if err != nil {
		tb.Errorf("error parsing %q: %s", text, err)
		return nil
	}
	if err := tmpl.Compile(); err != nil {
		tb.Errorf("error compiling %q: %s", text, err)
		return nil
	}
	return tmpl
}

func parseTestTemplate(tb testing.TB, name string) *Template {
	loader := loaders.FSLoader("_testdata")
	tmpl := New(loader, assets.NewManager(loader, ""))
	tmpl.Funcs(FuncMap{"t": func(s string) string { return s }})
	if err := tmpl.Parse(name); err != nil {
		tb.Errorf("error parsing %q: %s", name, err)
		return nil
	}
	if err := tmpl.Compile(); err != nil {
		tb.Errorf("error compiling %q: %s", name, err)
		return nil
	}
	return tmpl
}

func TestFunctions(t *testing.T) {
	for _, v := range ftests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, v.data); err != nil {
			t.Errorf("error executing %q: %s", v.tmpl, err)
			continue
		}
		if buf.String() != v.result {
			t.Errorf("expecting %q executing %q, got %q", v.result, v.tmpl, buf.String())
		}
	}
}

func TestCompiler(t *testing.T) {
	var tests []*templateTest
	tests = append(tests, compilerTests...)
	for _, v := range tests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, v.data); err != nil {
			t.Errorf("error executing %q: %s", v.tmpl, err)
			continue
		}
		if buf.String() != v.result {
			t.Errorf("expecting %q executing %q, got %q", v.result, v.tmpl, buf.String())
		}
	}
}

func TestCompilerErrors(t *testing.T) {
	for _, v := range compilerErrorTests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		var buf bytes.Buffer
		err := tmpl.Execute(&buf, v.data)
		if err == nil {
			t.Errorf("expecting an error when executing %q, got nil", v.tmpl)
			continue
		}
		if err.Error() != v.result {
			t.Logf("template is %q", v.tmpl)
			t.Errorf("expecting error %q, got %q", v.result, err.Error())
		}
	}
}

func TestBigTemplate(t *testing.T) {
	const name = "1.html"
	tmpl := parseTestTemplate(t, name)
	if tmpl != nil {
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, nil); err != nil {
			t.Errorf("error executing template %s: %s", name, err)
		}
	}
}

func benchmarkTests() []*templateTest {
	var tests []*templateTest
	tests = append(tests, ftests...)
	tests = append(tests, compilerTests...)
	return tests
}

func BenchmarkExecute(b *testing.B) {
	b.ReportAllocs()
	tests := benchmarkTests()
	templates := make([]*Template, len(tests))
	for ii, v := range tests {
		tmpl := parseText(b, v.tmpl)
		if tmpl == nil {
			b.Fatalf("can't parse %q", v.tmpl)
		}
		templates[ii] = tmpl
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		for ii, v := range templates {
			v.Execute(&buf, tests[ii].data)
		}
		buf.Reset()
	}
}

func BenchmarkBig(b *testing.B) {
	b.ReportAllocs()
	const name = "1.html"
	tmpl := parseTestTemplate(b, name)
	if tmpl == nil {
		return
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		tmpl.Execute(&buf, nil)
		buf.Reset()
	}
}
<commit_msg>Add benchmarks comparing the performance with html/template<commit_after>package template

import (
	"bytes"
	"gnd.la/loaders"
	"gnd.la/template/assets"
	"html/template"
	"io/ioutil"
	"path/filepath"
	"testing"
)

type templateTest struct {
	tmpl   string
	data   interface{}
	result string
}

type testType struct {
}

func (t *testType) Foo() string {
	return "bar"
}

func (t *testType) Bar(s string) string {
	return "bared-" + s
}

var (
	ftests = []*templateTest{
		{"{{ $one := 1 }}{{ $two := 2 }}{{ $three := 3 }}{{ $one }}+{{ $two }}+{{ $three }}={{ add $one $two $three }}", nil, "1+2+3=6"},
		{"{{ add 2 3 }}", nil, "5"},
		{"{{ to_lower .foo }}", map[string]string{"foo": "BAR"}, "bar"},
		{"{{ to_upper .foo }}", map[string]string{"foo": "bar"}, "BAR"},
		{"{{ join .chars .sep }}", map[string]interface{}{"chars": []string{"a", "b", "c"}, "sep": ","}, "a,b,c"},
		{"{{ to_html .s }}", map[string]string{"s": "<foo\nbar"}, "<foo
bar"},
		{"{{ mult 2 1.1 }}", nil, "2.2"},
		{"{{ imult 2 1.1 }}", nil, "2"},
		{"{{ concat \"foo\" \"bar\" }}", nil, "foobar"},
		{"{{ concat (concat \"foo\" \"bar\") \"baz\" }}", nil, "foobarbaz"},
		{"{{ if divisible 5 2 }}1{{ else }}0{{ end }}", nil, "0"},
		{"{{ if divisible 4 2 }}1{{ else }}0{{ end }}", nil, "1"},
	}
	compilerTests = []*templateTest{
		{"{{ \"output\" | printf \"%s\" }}", nil, "output"},
		{"{{ call .foo }}", map[string]interface{}{"foo": func() string { return "bar" }}, "bar"},
		{"{{ .Foo }}", struct{ Foo string }{"bar"}, "bar"},
		{"{{ .Foo }}", &testType{}, "bar"},
		{"{{ .Bar \"this\" }}", &testType{}, "bared-this"},
		{"{{ .t.Bar .foo }}", map[string]interface{}{"t": &testType{}, "foo": "foo"}, "bared-foo"},
		{"{{ .t.Bar (concat .foo \"bar\") }}", map[string]interface{}{"t": &testType{}, "foo": "foo"}, "bared-foobar"},
		{"{{ with .A }}{{ . }}{{ else }}no{{ end }}", map[string]string{"A": "yes"}, "yes"},
		{"{{ with .A }}{{ . }}{{ else }}no{{ end }}", nil, "no"},
		{"{{ with .A }}{{ . }}{{ end }}", nil, ""},
		{"{{ range . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range . }}{{ . }}{{ end }}{{ . }}", []int{1, 2, 3}, "123[1 2 3]"},
		{"{{ range $idx, $el := . }}{{ $idx }}{{ $el }}{{ end }}", []int{1, 2, 3}, "011223"},
		{"{{ range $el := . }}{{ $el }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range $el := . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range $idx, $el := . }}{{ . }}{{ end }}", []int{1, 2, 3}, "123"},
		{"{{ range . }}{{ else }}nope{{ end }}", nil, "nope"},
		{"{{ range $k, $v := . }}{{ $k }}={{ $v }}{{ end }}", map[string]int{"b": 2, "c": 3, "a": 1}, "a=1b=2c=3"},
		{"{{ range . }}{{ range . }}{{ if even . }}{{ . }}{{ end }}{{ end }}{{ end }}", [][]int{[]int{1, 2, 3, 4, 5, 6}}, "246"},
		{"{{ define \"a\" }}a{{ end }}{{ range . }}{{ template \"a\" . }}{{ end }}", []int{1, 2, 3}, "aaa"},
		{"{{ define \"a\" }}a{{ . }}{{ . }}{{ end }}{{ range . }}{{ template \"a\" . }}{{ end }}", []int{1, 2, 3}, "a11a22a33"},
		{"{{ define \"a\" }}a{{ . }}{{ . }}{{ end }}{{ if . }}{{ template \"a\" . }}{{ end }}", 0, ""},
		{"{{ define \"a\" }}a{{ . }}{{ . }}{{ end }}{{ if . }}{{ template \"a\" . }}{{ end }}", 1, "a11"},
	}
	compilerErrorTests = []*templateTest{
		{"{{ range . }}{{ else }}nope{{ end }}", 5, "template.html:1:9: can't range over int"},
		{"{{ . }}\n{{ range . }}{{ else }}nope{{ end }}", 5, "template.html:2:9: can't range over int"},
		{"{{ . }}\n{{ range .foo }}{{ else }}nope{{ end }}\n{{ range .bar }}{{ . }}{{ end }} ", map[string]interface{}{"foo": []int{}, "bar": ""}, "template.html:3:9: can't range over string"},
	}
)

func parseText(tb testing.TB, text string) *Template {
	loader := loaders.MapLoader(map[string][]byte{"template.html": []byte(text)})
	tmpl, err := Parse(loader, nil, "template.html")
	if err != nil {
		tb.Errorf("error parsing %q: %s", text, err)
		return nil
	}
	if err := tmpl.Compile(); err != nil {
		tb.Errorf("error compiling %q: %s", text, err)
		return nil
	}
	return tmpl
}

func parseTestTemplate(tb testing.TB, name string) *Template {
	loader := loaders.FSLoader("_testdata")
	tmpl := New(loader, assets.NewManager(loader, ""))
	tmpl.Funcs(FuncMap{"t": func(s string) string { return s }})
	if err := tmpl.Parse(name); err != nil {
		tb.Errorf("error parsing %q: %s", name, err)
		return nil
	}
	if err := tmpl.Compile(); err != nil {
		tb.Errorf("error compiling %q: %s", name, err)
		return nil
	}
	return tmpl
}

func TestFunctions(t *testing.T) {
	for _, v := range ftests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, v.data); err != nil {
			t.Errorf("error executing %q: %s", v.tmpl, err)
			continue
		}
		if buf.String() != v.result {
			t.Errorf("expecting %q executing %q, got %q", v.result, v.tmpl, buf.String())
		}
	}
}

func TestCompiler(t *testing.T) {
	var tests []*templateTest
	tests = append(tests, compilerTests...)
	for _, v := range tests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, v.data); err != nil {
			t.Errorf("error executing %q: %s", v.tmpl, err)
			continue
		}
		if buf.String() != v.result {
			t.Errorf("expecting %q executing %q, got %q", v.result, v.tmpl, buf.String())
		}
	}
}

func TestCompilerErrors(t *testing.T) {
	for _, v := range compilerErrorTests {
		tmpl := parseText(t, v.tmpl)
		if tmpl == nil {
			continue
		}
		var buf bytes.Buffer
		err := tmpl.Execute(&buf, v.data)
		if err == nil {
			t.Errorf("expecting an error when executing %q, got nil", v.tmpl)
			continue
		}
		if err.Error() != v.result {
			t.Logf("template is %q", v.tmpl)
			t.Errorf("expecting error %q, got %q", v.result, err.Error())
		}
	}
}

func TestBigTemplate(t *testing.T) {
	const name = "1.html"
	tmpl := parseTestTemplate(t, name)
	if tmpl != nil {
		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, nil); err != nil {
			t.Errorf("error executing template %s: %s", name, err)
		}
	}
}

func benchmarkTests() []*templateTest {
	var tests []*templateTest
	tests = append(tests, ftests...)
	tests = append(tests, compilerTests...)
	return tests
}

func BenchmarkTemplate(b *testing.B) {
	b.ReportAllocs()
	tests := benchmarkTests()
	templates := make([]*Template, len(tests))
	for ii, v := range tests {
		tmpl := parseText(b, v.tmpl)
		if tmpl == nil {
			b.Fatalf("can't parse %q", v.tmpl)
		}
		templates[ii] = tmpl
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		for ii, v := range templates {
			v.Execute(&buf, tests[ii].data)
		}
		buf.Reset()
	}
}

func BenchmarkHTMLTemplate(b *testing.B) {
	b.ReportAllocs()
	tests := benchmarkTests()
	templates := make([]*template.Template, len(tests))
	for ii, v := range tests {
		tmpl := template.New("template.html")
		tmpl.Funcs(template.FuncMap(templateFuncs))
		_, err := tmpl.Parse(v.tmpl)
		if err != nil {
			b.Fatalf("can't parse %q: %s", v.tmpl, err)
		}
		// Execute once to add the escaping hooks
		tmpl.Execute(ioutil.Discard, nil)
		templates[ii] = tmpl
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		for ii, v := range templates {
			v.Execute(&buf, tests[ii].data)
		}
		buf.Reset()
	}
}

func BenchmarkBig(b *testing.B) {
	b.ReportAllocs()
	const name = "1.html"
	tmpl := parseTestTemplate(b, name)
	if tmpl == nil {
		return
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		tmpl.Execute(&buf, nil)
		buf.Reset()
	}
}

func BenchmarkHTMLBig(b *testing.B) {
	b.ReportAllocs()
	tmpl := template.New("")
	tmpl.Funcs(template.FuncMap{"t": func(s string) string { return s }})
	tmpl.Funcs(template.FuncMap(templateFuncs))
	readFile := func(name string) string {
		data, err := ioutil.ReadFile(filepath.Join("_testdata", name))
		if err != nil {
			b.Fatal(err)
		}
		return "{{ $Vars := .Vars }}\n" + string(data)
	}
	if _, err := tmpl.Parse(readFile("1.html")); err != nil {
		b.Fatal(err)
	}
	t2 := tmpl.New("2.html")
	if _, err := t2.Parse(readFile("2.html")); err != nil {
		b.Fatal(err)
	}
	if err := tmpl.Execute(ioutil.Discard, nil); err != nil {
		b.Fatal(err)
	}
	var buf bytes.Buffer
	b.ResetTimer()
	for ii := 0; ii < b.N; ii++ {
		tmpl.Execute(&buf, nil)
		buf.Reset()
	}
}
<|endoftext|>


<commit_before>package sql // import "eriol.xyz/piken/sql"

import (
	"database/sql"
	"os"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

const (
	// Full-text search need only TEXT column.
	createTablesQuery = `
	CREATE TABLE last_update (
		filename TEXT NOT NULL PRIMARY KEY,
		date TEXT
	);
	CREATE VIRTUAL TABLE unicode_data USING fts4(
		codepoint TEXT NOT NULL PRIMARY KEY,
		name TEXT NOT NULL,
		category TEXT NOT NULL,
		canonical_class TEXT NOT NULL,
		bidi_class TEXT NOT NULL,
		decomposition_type TEXT,
		numeric_type TEXT,
		numeric_digit TEXT,
		numeric_value TEXT,
		bidi_mirrored TEXT,
		unicode_1_name TEXT,
		iso_comment TEXT,
		simple_uppercase_mapping TEXT,
		simple_lowercase_mapping TEXT,
		simple_titlecase_mapping TEXT
	);`
	insertUnicodeDataQuery = `INSERT INTO unicode_data (
		codepoint,
		name,
		category,
		canonical_class,
		bidi_class,
		decomposition_type,
		numeric_type,
		numeric_digit,
		numeric_value,
		bidi_mirrored,
		unicode_1_name,
		iso_comment,
		simple_uppercase_mapping,
		simple_lowercase_mapping,
		simple_titlecase_mapping)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
	createLastUpdateQuery = `INSERT INTO last_update (filename, date) VALUES (?, ?)`
	getLastUpdateQuery    = `SELECT date FROM last_update WHERE filename = ?`
	getUnicodeQuery       = `SELECT codepoint, name, category FROM unicode_data WHERE name MATCH ?`
	countUnicodeQuery     = `SELECT count(*) FROM unicode_data`
)

type Store struct {
	db *sql.DB
}

type UnicodeData struct {
	CodePoint              string
	Name                   string
	Category               string
	CanonicalClass         string
	BidiClass              string
	DecompositionType      string
	NumericType            string
	NumericDigit           string
	NumericValue           string
	BidiMirrored           string
	Unicode1Name           string
	ISOComment             string
	SimpleUppercaseMapping string
	SimpleLowercaseMapping string
	SimpleTitlecaseMapping string
}

// Open SQLite 3 database used by piken or create it if it doesn't exist yet.
func (s *Store) Open(database string) error {

	db, err := sql.Open("sqlite3", database)
	if err != nil {
		return err
	}

	s.db = db

	if _, err := os.Stat(database); err != nil {
		if os.IsNotExist(err) {
			if err := s.createDatabase(); err != nil {
				return err
			}
		}
	}

	return nil
}

// Close SQLite3 database used by piken.
func (s *Store) Close() error {
	return s.db.Close()
}

func (s *Store) createDatabase() error {

	_, err := s.db.Exec(createTablesQuery)
	if err != nil {
		return err
	}
	return nil
}

// Load multiple records into piken database.
func (s *Store) LoadFromRecords(records [][]string) error {

	tx, err := s.db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare(insertUnicodeDataQuery)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, record := range records {

		args := make([]interface{}, len(record))
		for i, v := range record {
			args[i] = v
		}

		if _, err = stmt.Exec(args...); err != nil {
			return err
		}
	}
	tx.Commit()

	return nil
}

// Create latest update entry for given file.
func (s *Store) CreateLastUpdate(filename string, t time.Time) error {

	ts := t.Format(time.RFC3339)

	_, err := s.db.Exec(createLastUpdateQuery, filename, ts)

	return err
}

// Get latest update for given file.
func (s *Store) GetLastUpdate(filename string) (time.Time, error) {
	var t string

	s.db.QueryRow(getLastUpdateQuery, filename).Scan(&t)

	// If the query is empty return the beginning of time.
	if t == "" {
		return time.Unix(0, 0), nil
	}

	tp, err := time.Parse(time.RFC3339Nano, t)
	if err != nil {
		return time.Unix(0, 0), err
	}

	return tp, nil
}

// Search unicode data using name.
func (s *Store) SearchUnicode(name string) (records []UnicodeData, err error) {

	rows, err := s.db.Query(getUnicodeQuery, name)
	defer rows.Close()
	if err != nil {
		return []UnicodeData{}, err
	}

	for rows.Next() {
		var row UnicodeData

		if err := rows.Scan(&row.CodePoint, &row.Name, &row.Category); err != nil {
			return []UnicodeData{}, err
		}

		records = append(records, row)

	}

	return records, nil
}

// Count total rows inside unicode data table.
func (s *Store) CountUnicodeData() int {
	var total int

	s.db.QueryRow(countUnicodeQuery).Scan(&total)

	return total
}
<commit_msg>Build UnicodeData using all data inside unicode_data table<commit_after>package sql // import "eriol.xyz/piken/sql"

import (
	"database/sql"
	"os"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

const (
	// Full-text search need only TEXT column.
	createTablesQuery = `
	CREATE TABLE last_update (
		filename TEXT NOT NULL PRIMARY KEY,
		date TEXT
	);
	CREATE VIRTUAL TABLE unicode_data USING fts4(
		codepoint TEXT NOT NULL PRIMARY KEY,
		name TEXT NOT NULL,
		category TEXT NOT NULL,
		canonical_class TEXT NOT NULL,
		bidi_class TEXT NOT NULL,
		decomposition_type TEXT,
		numeric_type TEXT,
		numeric_digit TEXT,
		numeric_value TEXT,
		bidi_mirrored TEXT,
		unicode_1_name TEXT,
		iso_comment TEXT,
		simple_uppercase_mapping TEXT,
		simple_lowercase_mapping TEXT,
		simple_titlecase_mapping TEXT
	);`
	insertUnicodeDataQuery = `INSERT INTO unicode_data (
		codepoint,
		name,
		category,
		canonical_class,
		bidi_class,
		decomposition_type,
		numeric_type,
		numeric_digit,
		numeric_value,
		bidi_mirrored,
		unicode_1_name,
		iso_comment,
		simple_uppercase_mapping,
		simple_lowercase_mapping,
		simple_titlecase_mapping)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
	createLastUpdateQuery = `INSERT INTO last_update (filename, date) VALUES (?, ?)`
	getLastUpdateQuery    = `SELECT date FROM last_update WHERE filename = ?`
	getUnicodeQuery       = `SELECT * FROM unicode_data WHERE name MATCH ?`
	countUnicodeQuery     = `SELECT count(*) FROM unicode_data`
)

type Store struct {
	db *sql.DB
}

type UnicodeData struct {
	CodePoint              string
	Name                   string
	Category               string
	CanonicalClass         string
	BidiClass              string
	DecompositionType      string
	NumericType            string
	NumericDigit           string
	NumericValue           string
	BidiMirrored           string
	Unicode1Name           string
	ISOComment             string
	SimpleUppercaseMapping string
	SimpleLowercaseMapping string
	SimpleTitlecaseMapping string
}

// Open SQLite 3 database used by piken or create it if it doesn't exist yet.
func (s *Store) Open(database string) error {

	db, err := sql.Open("sqlite3", database)
	if err != nil {
		return err
	}

	s.db = db

	if _, err := os.Stat(database); err != nil {
		if os.IsNotExist(err) {
			if err := s.createDatabase(); err != nil {
				return err
			}
		}
	}

	return nil
}

// Close SQLite3 database used by piken.
func (s *Store) Close() error {
	return s.db.Close()
}

func (s *Store) createDatabase() error {

	_, err := s.db.Exec(createTablesQuery)
	if err != nil {
		return err
	}
	return nil
}

// Load multiple records into piken database.
func (s *Store) LoadFromRecords(records [][]string) error {

	tx, err := s.db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare(insertUnicodeDataQuery)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, record := range records {

		args := make([]interface{}, len(record))
		for i, v := range record {
			args[i] = v
		}

		if _, err = stmt.Exec(args...); err != nil {
			return err
		}
	}
	tx.Commit()

	return nil
}

// Create latest update entry for given file.
func (s *Store) CreateLastUpdate(filename string, t time.Time) error {

	ts := t.Format(time.RFC3339)

	_, err := s.db.Exec(createLastUpdateQuery, filename, ts)

	return err
}

// Get latest update for given file.
func (s *Store) GetLastUpdate(filename string) (time.Time, error) {
	var t string

	s.db.QueryRow(getLastUpdateQuery, filename).Scan(&t)

	// If the query is empty return the beginning of time.
	if t == "" {
		return time.Unix(0, 0), nil
	}

	tp, err := time.Parse(time.RFC3339Nano, t)
	if err != nil {
		return time.Unix(0, 0), err
	}

	return tp, nil
}

// Search unicode data using name.
func (s *Store) SearchUnicode(name string) (records []UnicodeData, err error) {

	rows, err := s.db.Query(getUnicodeQuery, name)
	defer rows.Close()
	if err != nil {
		return []UnicodeData{}, err
	}

	for rows.Next() {
		var row UnicodeData

		if err := rows.Scan(
			&row.CodePoint,
			&row.Name,
			&row.Category,
			&row.CanonicalClass,
			&row.BidiClass,
			&row.DecompositionType,
			&row.NumericType,
			&row.NumericDigit,
			&row.NumericValue,
			&row.BidiMirrored,
			&row.Unicode1Name,
			&row.ISOComment,
			&row.SimpleUppercaseMapping,
			&row.SimpleLowercaseMapping,
			&row.SimpleTitlecaseMapping); err != nil {
			return []UnicodeData{}, err
		}

		records = append(records, row)

	}

	return records, nil
}

// Count total rows inside unicode data table.
func (s *Store) CountUnicodeData() int {
	var total int

	s.db.QueryRow(countUnicodeQuery).Scan(&total)

	return total
}
<|endoftext|>


<commit_before>// Package rpc contains all the methods exposed to the rpc interface
package rpc

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/levenlabs/go-llog"
	"github.com/levenlabs/golib/rpcutil"
	"github.com/levenlabs/postmaster/db"
	"github.com/levenlabs/postmaster/sender"
)

// Enqueue queues an email to be sent to sendgrid it accepts an instance of
// sender.Mail
func (Postmaster) Enqueue(r *http.Request, args *sender.Mail, reply *SuccessResult) error {
	kv := rpcutil.RequestKV(r)
	// validation of email addresses is done with the validation library
	// more advanced validation is done in validateEnqueueArgs
	if err := validateEnqueueArgs(args); err != nil {
		kv["err"] = err
		llog.Warn("badly formed Enqueue request", kv)
		return err
	}

	allowed := db.VerifyEmailAllowed(args.To, args.Flags)
	if !allowed {
		kv["flags"] = fmt.Sprintf("%b", args.Flags)
		llog.Warn("cannot send email due to flags", kv)
		//even though we didn't send it, it didn't fail, the user just doesn't want this email
		reply.Success = true
		return nil
	}

	contents, err := json.Marshal(args)
	if err != nil {
		return err
	}
	err = db.StoreSendJob(string(contents))
	if err != nil {
		return err
	}
	reply.Success = true
	return nil
}

func validateEnqueueArgs(args *sender.Mail) error {
	if strings.HasSuffix(args.To, "@test") {
		return errors.New("to address cannot end in @test.com")
	}
	if args.HTML == "" && args.Text == "" {
		return errors.New("you must send either html or text")
	}
	return nil
}
<commit_msg>Info log when a new email job is being stored<commit_after>// Package rpc contains all the methods exposed to the rpc interface
package rpc

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/levenlabs/go-llog"
	"github.com/levenlabs/golib/rpcutil"
	"github.com/levenlabs/postmaster/db"
	"github.com/levenlabs/postmaster/sender"
)

// Enqueue queues an email to be sent to sendgrid it accepts an instance of
// sender.Mail
func (Postmaster) Enqueue(r *http.Request, args *sender.Mail, reply *SuccessResult) error {
	kv := rpcutil.RequestKV(r)
	kv["to"] = args.To
	kv["flags"] = args.Flags
	kv["subject"] = args.Subject
	// validation of email addresses is done with the validation library
	// more advanced validation is done in validateEnqueueArgs
	if err := validateEnqueueArgs(args); err != nil {
		kv["err"] = err
		llog.Warn("badly formed Enqueue request", kv)
		return err
	}

	allowed := db.VerifyEmailAllowed(args.To, args.Flags)
	if !allowed {
		kv["flags"] = fmt.Sprintf("%b", args.Flags)
		llog.Warn("cannot send email due to flags", kv)
		//even though we didn't send it, it didn't fail, the user just doesn't want this email
		reply.Success = true
		return nil
	}

	contents, err := json.Marshal(args)
	if err != nil {
		return err
	}

	llog.Info("storing new email job", kv)

	err = db.StoreSendJob(string(contents))
	if err != nil {
		return err
	}
	reply.Success = true
	return nil
}

func validateEnqueueArgs(args *sender.Mail) error {
	if strings.HasSuffix(args.To, "@test") {
		return errors.New("to address cannot end in @test.com")
	}
	if args.HTML == "" && args.Text == "" {
		return errors.New("you must send either html or text")
	}
	return nil
}
<|endoftext|>


<commit_before>package rpc

import "io"
import "github.com/zrepl/zrepl/zfs"

type RequestId [16]byte
type RequestType uint8

const (
	RTProtocolVersionRequest     RequestType = 1
	RTFilesystemRequest                      = 16
	RTInitialTransferRequest                 = 17
	RTIncrementalTransferRequest             = 18
	RTFilesystemVersionsRequest              = 19
)

type RequestHeader struct {
	Type RequestType
	Id   [16]byte // UUID
}

type Direction string

const (
	DirectionPush Direction = "push"
	DirectionPull Direction = "pull"
)

type FilesystemRequest struct {
	Roots     []string // may be nil, indicating interest in all filesystems
	Direction Direction
}

type FilesystemVersionsRequest struct {
	Filesystem zfs.DatasetPath
}

type InitialTransferRequest struct {
	Snapshot string // tank/my/db@ljlsdjflksdf
}

func (r InitialTransferRequest) Respond(snapshotReader io.Reader) {

}

type IncrementalTransferRequest struct {
	FromSnapshot string
	ToSnapshot   string
}

func (r IncrementalTransferRequest) Respond(snapshotReader io.Reader) {

}

type ByteStreamRPCProtocolVersionRequest struct {
	ClientVersion uint8
}

type ErrorId uint8

const (
	ENoError                 ErrorId = 0
	EDecodeHeader                    = 1
	EUnknownRequestType              = 2
	EDecodeRequestBody               = 3
	EProtocolVersionMismatch         = 4
	EHandler                         = 5
)

type ResponseType uint8

const (
	RNONE          ResponseType = 0
	ROK                         = 1
	RFilesystems                = 2
	RChunkedStream              = 3
	RFilesystemDiff             = 4
)

type ResponseHeader struct {
	RequestId    RequestId
	ErrorId      ErrorId
	Message      string
	ResponseType ResponseType
}

func NewByteStreamRPCProtocolVersionRequest() ByteStreamRPCProtocolVersionRequest {
	return ByteStreamRPCProtocolVersionRequest{
		ClientVersion: ByteStreamRPCProtocolVersion,
	}
}

func newUUID() [16]byte {
	return [16]byte{}
}
<commit_msg>rpc: hex-ints for RequestType definitions<commit_after>package rpc

import "io"
import "github.com/zrepl/zrepl/zfs"

type RequestId [16]byte
type RequestType uint8

const (
	RTProtocolVersionRequest     RequestType = 0x01
	RTFilesystemRequest                      = 0x10
	RTFilesystemVersionsRequest              = 0x11
	RTInitialTransferRequest                 = 0x12
	RTIncrementalTransferRequest             = 0x13
)

type RequestHeader struct {
	Type RequestType
	Id   [16]byte // UUID
}

type Direction string

const (
	DirectionPush Direction = "push"
	DirectionPull Direction = "pull"
)

type FilesystemRequest struct {
	Roots     []string // may be nil, indicating interest in all filesystems
	Direction Direction
}

type FilesystemVersionsRequest struct {
	Filesystem zfs.DatasetPath
}

type InitialTransferRequest struct {
	Snapshot string // tank/my/db@ljlsdjflksdf
}

func (r InitialTransferRequest) Respond(snapshotReader io.Reader) {

}

type IncrementalTransferRequest struct {
	FromSnapshot string
	ToSnapshot   string
}

func (r IncrementalTransferRequest) Respond(snapshotReader io.Reader) {

}

type ByteStreamRPCProtocolVersionRequest struct {
	ClientVersion uint8
}

type ErrorId uint8

const (
	ENoError                 ErrorId = 0
	EDecodeHeader                    = 1
	EUnknownRequestType              = 2
	EDecodeRequestBody               = 3
	EProtocolVersionMismatch         = 4
	EHandler                         = 5
)

type ResponseType uint8

const (
	RNONE           ResponseType = 0x0
	ROK                          = 0x1
	RFilesystems                 = 0x10
	RFilesystemDiff              = 0x11
	RChunkedStream               = 0x20
)

type ResponseHeader struct {
	RequestId    RequestId
	ErrorId      ErrorId
	Message      string
	ResponseType ResponseType
}

func NewByteStreamRPCProtocolVersionRequest() ByteStreamRPCProtocolVersionRequest {
	return ByteStreamRPCProtocolVersionRequest{
		ClientVersion: ByteStreamRPCProtocolVersion,
	}
}

func newUUID() [16]byte {
	return [16]byte{}
}
<|endoftext|>


<commit_before>package rpcc

import (
	"context"
	"encoding/json"
	"errors"
	"sync"
)

var (
	// ErrStreamClosing indicates that the operation is illegal because
	// the stream is closing and there are no pending messages.
	ErrStreamClosing = errors.New("rpcc: the stream is closing")
)

type messageBuffer struct {
	ch    chan []byte
	mu    sync.Mutex
	queue [][]byte
}

func (b *messageBuffer) store(m []byte) {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.ch) == 0 && len(b.queue) == 0 {
		select {
		case b.ch <- m:
			return
		default:
		}
	}
	b.queue = append(b.queue, m)
}

func (b *messageBuffer) load() {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.queue) > 0 {
		select {
		case b.ch <- b.queue[0]:
			b.queue[0] = nil // Remove reference from underlying array.
			b.queue = b.queue[1:]
		default:
		}
	}
}

func (b *messageBuffer) get() <-chan []byte {
	return b.ch
}

// Stream represents a stream of notifications for a certain method.
type Stream interface {
	// RecvMsg unmarshals pending messages onto m. Blocks until the
	// next message is received, context is canceled or stream is
	// closed.
	RecvMsg(m interface{}) error
	// Close closes the stream and no new messages will be received.
	// RecvMsg will return ErrStreamClosing once all pending messages
	// have been received.
	Close() error
}

// NewStream creates a new stream that listens to notifications from the
// RPC server. This function is called by generated code.
func NewStream(ctx context.Context, method string, conn *Conn) (Stream, error) {
	if ctx == nil {
		ctx = context.Background()
	}

	s := &streamClient{userCtx: ctx, done: make(chan struct{})}
	s.msgBuf.ch = make(chan []byte, 1)
	s.ctx, s.cancel = context.WithCancel(context.Background())

	var err error
	s.remove, err = conn.listen(method, s)
	if err != nil {
		return nil, err
	}

	go func() {
		select {
		case <-s.ctx.Done():
		case <-conn.ctx.Done():
			s.close(ErrConnClosing)
		case <-ctx.Done():
			s.close(ctx.Err())
		}
	}()

	return s, nil
}

type streamClient struct {
	userCtx context.Context
	ctx     context.Context
	cancel  context.CancelFunc

	// msgBuf stores all incoming messages
	// until they are ready to be received.
	msgBuf messageBuffer

	mu     sync.Mutex // Protects following.
	remove func()     // Unsubscribes from messages.

	done chan struct{} // Protects err.
	err  error
}

func (s *streamClient) RecvMsg(m interface{}) (err error) {
	var data []byte

	userCancelled := func() bool {
		select {
		case <-s.userCtx.Done():
			return true
		default:
			return false
		}
	}

	select {
	case <-s.userCtx.Done():
		return s.userCtx.Err()
	case <-s.ctx.Done():
		// Give precedence for user cancellation.
		if userCancelled() {
			return s.userCtx.Err()
		}

		// Send all messages before returning error.
		select {
		case data = <-s.msgBuf.get():
		default:
			<-s.done
			return s.err
		}
	case data = <-s.msgBuf.get():
		// Give precedence for user cancellation.
		if userCancelled() {
			return s.userCtx.Err()
		}
	}

	// Preload the next message.
	s.msgBuf.load()

	return json.Unmarshal(data, m)
}

// Close closes the stream client.
func (s *streamClient) close(err error) error {
	s.mu.Lock()
	remove := s.remove
	s.remove = nil
	s.mu.Unlock()

	if remove == nil {
		return errors.New("rpcc: the stream is already closed")
	}

	if err == nil {
		err = ErrStreamClosing
	}

	// Unsubscribe first to prevent incoming messages.
	remove()
	s.cancel()
	s.err = err
	close(s.done)

	return nil
}

// Close closes the stream client.
func (s *streamClient) Close() error {
	return s.close(nil)
}

// streamClients handles multiple instances of streamClient and
// enables sending of the same message to multiple clients.
type streamClients struct {
	mu      sync.Mutex
	seq     uint64
	clients map[uint64]*streamClient
}

func newStreamService() *streamClients {
	return &streamClients{
		clients: make(map[uint64]*streamClient),
	}
}

func (s *streamClients) add(client *streamClient) (seq uint64) {
	s.mu.Lock()
	seq = s.seq
	s.seq++
	s.clients[seq] = client
	s.mu.Unlock()
	return seq
}

func (s *streamClients) remove(seq uint64) {
	s.mu.Lock()
	delete(s.clients, seq)
	s.mu.Unlock()
}

func (s *streamClients) send(args []byte) {
	s.mu.Lock()
	for _, client := range s.clients {
		client.msgBuf.store(args)
	}
	s.mu.Unlock()
}
<commit_msg>rpcc: Avoid checking len(ch) in messageBuffer.store()<commit_after>package rpcc

import (
	"context"
	"encoding/json"
	"errors"
	"sync"
)

var (
	// ErrStreamClosing indicates that the operation is illegal because
	// the stream is closing and there are no pending messages.
	ErrStreamClosing = errors.New("rpcc: the stream is closing")
)

type messageBuffer struct {
	ch    chan []byte
	mu    sync.Mutex
	queue [][]byte
}

func (b *messageBuffer) store(m []byte) {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.queue) == 0 {
		select {
		case b.ch <- m:
			return
		default:
		}
	}
	b.queue = append(b.queue, m)
}

func (b *messageBuffer) load() {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.queue) > 0 {
		select {
		case b.ch <- b.queue[0]:
			b.queue[0] = nil // Remove reference from underlying array.
			b.queue = b.queue[1:]
		default:
		}
	}
}

func (b *messageBuffer) get() <-chan []byte {
	return b.ch
}

// Stream represents a stream of notifications for a certain method.
type Stream interface {
	// RecvMsg unmarshals pending messages onto m. Blocks until the
	// next message is received, context is canceled or stream is
	// closed.
	RecvMsg(m interface{}) error
	// Close closes the stream and no new messages will be received.
	// RecvMsg will return ErrStreamClosing once all pending messages
	// have been received.
	Close() error
}

// NewStream creates a new stream that listens to notifications from the
// RPC server. This function is called by generated code.
func NewStream(ctx context.Context, method string, conn *Conn) (Stream, error) {
	if ctx == nil {
		ctx = context.Background()
	}

	s := &streamClient{userCtx: ctx, done: make(chan struct{})}
	s.msgBuf.ch = make(chan []byte, 1)
	s.ctx, s.cancel = context.WithCancel(context.Background())

	var err error
	s.remove, err = conn.listen(method, s)
	if err != nil {
		return nil, err
	}

	go func() {
		select {
		case <-s.ctx.Done():
		case <-conn.ctx.Done():
			s.close(ErrConnClosing)
		case <-ctx.Done():
			s.close(ctx.Err())
		}
	}()

	return s, nil
}

type streamClient struct {
	userCtx context.Context
	ctx     context.Context
	cancel  context.CancelFunc

	// msgBuf stores all incoming messages
	// until they are ready to be received.
	msgBuf messageBuffer

	mu     sync.Mutex // Protects following.
	remove func()     // Unsubscribes from messages.

	done chan struct{} // Protects err.
	err  error
}

func (s *streamClient) RecvMsg(m interface{}) (err error) {
	var data []byte

	userCancelled := func() bool {
		select {
		case <-s.userCtx.Done():
			return true
		default:
			return false
		}
	}

	select {
	case <-s.userCtx.Done():
		return s.userCtx.Err()
	case <-s.ctx.Done():
		// Give precedence for user cancellation.
		if userCancelled() {
			return s.userCtx.Err()
		}

		// Send all messages before returning error.
		select {
		case data = <-s.msgBuf.get():
		default:
			<-s.done
			return s.err
		}
	case data = <-s.msgBuf.get():
		// Give precedence for user cancellation.
		if userCancelled() {
			return s.userCtx.Err()
		}
	}

	// Preload the next message.
	s.msgBuf.load()

	return json.Unmarshal(data, m)
}

// Close closes the stream client.
func (s *streamClient) close(err error) error {
	s.mu.Lock()
	remove := s.remove
	s.remove = nil
	s.mu.Unlock()

	if remove == nil {
		return errors.New("rpcc: the stream is already closed")
	}

	if err == nil {
		err = ErrStreamClosing
	}

	// Unsubscribe first to prevent incoming messages.
	remove()
	s.cancel()
	s.err = err
	close(s.done)

	return nil
}

// Close closes the stream client.
func (s *streamClient) Close() error {
	return s.close(nil)
}

// streamClients handles multiple instances of streamClient and
// enables sending of the same message to multiple clients.
type streamClients struct {
	mu      sync.Mutex
	seq     uint64
	clients map[uint64]*streamClient
}

func newStreamService() *streamClients {
	return &streamClients{
		clients: make(map[uint64]*streamClient),
	}
}

func (s *streamClients) add(client *streamClient) (seq uint64) {
	s.mu.Lock()
	seq = s.seq
	s.seq++
	s.clients[seq] = client
	s.mu.Unlock()
	return seq
}

func (s *streamClients) remove(seq uint64) {
	s.mu.Lock()
	delete(s.clients, seq)
	s.mu.Unlock()
}

func (s *streamClients) send(args []byte) {
	s.mu.Lock()
	for _, client := range s.clients {
		client.msgBuf.store(args)
	}
	s.mu.Unlock()
}
<|endoftext|>


<commit_before>/*
	Go Language Raspberry Pi Interface
	(c) Copyright David Thorpe 2016
	All Rights Reserved

	For Licensing and Usage information, please see LICENSE.md
*/
package egl

/*
	#cgo CFLAGS:   -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads
	#cgo LDFLAGS:  -L/opt/vc/lib -lEGL -lGLESv2
	#include "EGL/egl.h"
*/
import "C"

import (
	"unsafe"
)

type (
	Display uintptr
	Config  uintptr
	Surface uintptr
	Enum    uint32
)

func Initialize(disp Display, major, minor *int32) error {
	if C.eglInitialize(C.EGLDisplay(unsafe.Pointer(disp)), (*C.EGLint)(major), (*C.EGLint)(minor)) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func Terminate(disp Display) error {
	if C.eglTerminate(C.EGLDisplay(unsafe.Pointer(disp))) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func GetDisplay() Display {
	return Display(C.eglGetDisplay(C.EGLNativeDisplayType(unsafe.Pointer(nil))))
}

func GetConfigs(disp Display, configs *Config, configSize int32, numConfig *int32) error {
	if C.eglGetConfigs(C.EGLDisplay(unsafe.Pointer(disp)), (*C.EGLConfig)(unsafe.Pointer(configs)), C.EGLint(configSize), (*C.EGLint)(unsafe.Pointer(numConfig))) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func GetConfigAttrib(disp Display, config Config, attribute int32) (int32, error) {
	var value int32
	if C.eglGetConfigAttrib(C.EGLDisplay(unsafe.Pointer(disp)), C.EGLConfig(config), C.EGLint(attribute), (*C.EGLint)(unsafe.Pointer(&value))) == EGL_TRUE {
		return value, nil
	}
	return -1, toError(GetLastError())
}

func ChooseConfig(disp Display, attribList []int32, configs *Config, configSize int32, numConfig *int32) error {
	if C.eglChooseConfig(C.EGLDisplay(unsafe.Pointer(disp)), (*C.EGLint)(&attribList[0]), (*C.EGLConfig)(unsafe.Pointer(configs)), C.EGLint(configSize), (*C.EGLint)(numConfig)) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func BindAPI(api Enum) error {
	if C.eglBindAPI(C.EGLenum(api)) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func QueryAPI() Enum {
	return Enum(C.eglQueryAPI())
}

func GetLastError() int32 {
	return int32(C.eglGetError())
}
<commit_msg>Updated<commit_after>/*
	Go Language Raspberry Pi Interface
	(c) Copyright David Thorpe 2016
	All Rights Reserved

	For Licensing and Usage information, please see LICENSE.md
*/
package egl

/*
	#cgo CFLAGS:   -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads
	#cgo LDFLAGS:  -L/opt/vc/lib -lEGL -lGLESv2
	#include "EGL/egl.h"
*/
import "C"

import (
	"unsafe"
)

type (
	Display uintptr
	Config  uintptr
	Surface uintptr
	Enum    uint32
)

func Initialize(disp Display, major, minor *int32) error {
	if C.eglInitialize(C.EGLDisplay(unsafe.Pointer(disp)), (*C.EGLint)(major), (*C.EGLint)(minor)) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func Terminate(disp Display) error {
	if C.eglTerminate(C.EGLDisplay(unsafe.Pointer(disp))) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func GetDisplay() Display {
	return Display(C.eglGetDisplay(C.EGLNativeDisplayType(unsafe.Pointer(nil))))
}

func getConfigs(disp Display, configs *Config, configSize int32, numConfig *int32) error {
	if C.eglGetConfigs(C.EGLDisplay(unsafe.Pointer(disp)), (*C.EGLConfig)(unsafe.Pointer(configs)), C.EGLint(configSize), (*C.EGLint)(unsafe.Pointer(numConfig))) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func GetNumConfigs(disp Display) (int32,error) {
	var numConfigs int32
	if err := egl.GetConfigs(disp,nil,0,&numConfigs); err != nil {
		return -1,err
	}
	return numConfigs,nil
}

func GetConfigs(disp Display) ([]Config,error) {
	numConfigs, err := GetNumConfigs(disp)
	if err != nil {
		return nil,err
	}
	return make(Config,numConfigs)
}

func GetConfigAttrib(disp Display, config Config, attribute int32) (int32, error) {
	var value int32
	if C.eglGetConfigAttrib(C.EGLDisplay(unsafe.Pointer(disp)), C.EGLConfig(config), C.EGLint(attribute), (*C.EGLint)(unsafe.Pointer(&value))) == EGL_TRUE {
		return value, nil
	}
	return -1, toError(GetLastError())
}

func ChooseConfig(disp Display, attribList []int32, configs *Config, configSize int32, numConfig *int32) error {
	if C.eglChooseConfig(C.EGLDisplay(unsafe.Pointer(disp)), (*C.EGLint)(&attribList[0]), (*C.EGLConfig)(unsafe.Pointer(configs)), C.EGLint(configSize), (*C.EGLint)(numConfig)) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func BindAPI(api Enum) error {
	if C.eglBindAPI(C.EGLenum(api)) == EGL_TRUE {
		return nil
	}
	return toError(GetLastError())
}

func QueryAPI() Enum {
	return Enum(C.eglQueryAPI())
}

func GetLastError() int32 {
	return int32(C.eglGetError())
}
<|endoftext|>


<commit_before>package digests

import (
	"fmt"
	"sort"
	"strings"

	"github.com/ion-channel/ionic/rulesets"
	"github.com/ion-channel/ionic/scanner"
	"github.com/ion-channel/ionic/scans"
)

const (
	differenceIndex = iota
	virusFoundIndex
	criticalVulnerabilitiesIndex
	highVulnerabilitiesIndex
	totalVulnerabilitiesIndex
	uniqueVulnerabilitiesIndex
	licensesIndex
	filesScannedIndex
	directDependencyIndex
	transitiveDependencyIndex
	dependencyOutdatedIndex
	noVersionIndex
	languagesIndex
	uniqueCommittersIndex
	codeCoverageIndex
)

// NewDigests takes an applied ruleset and returns the relevant digests derived
// from all the evaluations in it, and any errors it encounters.
func NewDigests(appliedRuleset *rulesets.AppliedRulesetSummary, statuses []scanner.ScanStatus) ([]Digest, error) {
	ds := make([]Digest, 0)
	errs := make([]string, 0, 0)

	for i := range statuses {
		s := statuses[i]

		var e *scans.Evaluation
		if appliedRuleset != nil && appliedRuleset.RuleEvaluationSummary != nil {
			for i := range appliedRuleset.RuleEvaluationSummary.Ruleresults {
				if appliedRuleset.RuleEvaluationSummary.Ruleresults[i].ID == s.ID {
					e = &appliedRuleset.RuleEvaluationSummary.Ruleresults[i]
					break
				}
			}
		}

		d, err := _newDigests(&s, e)
		if err != nil {
			errs = append(errs, fmt.Sprintf("failed to make digest(s) from scan: %v", err.Error()))
			continue
		}

		ds = append(ds, d...)
	}

	sort.Slice(ds, func(i, j int) bool { return ds[i].Index < ds[j].Index })

	if len(errs) > 0 {
		return ds, fmt.Errorf("failed to make some digests: %v", strings.Join(errs, "; "))
	}

	return ds, nil
}

func _newDigests(status *scanner.ScanStatus, eval *scans.Evaluation) ([]Digest, error) {
	if eval != nil {
		err := eval.Translate()
		if err != nil {
			return nil, fmt.Errorf("evaluation translate error: %v", err.Error())
		}
	}

	switch strings.ToLower(status.Name) {
	case "ecosystems":
		return ecosystemsDigests(status, eval)

	case "dependency":
		return dependencyDigests(status, eval)

	case "vulnerability":
		return vulnerabilityDigests(status, eval)

	case "virus":
		return virusDigests(status, eval)

	case "community":
		return communityDigests(status, eval)

	case "license":
		return licenseDigests(status, eval)

	case "external_coverage", "code_coverage", "coverage":
		return coveragDigests(status, eval)

	case "difference":
		return differenceDigests(status, eval)

	case "about_yml", "file_type":
		return nil, nil

	default:
		return nil, fmt.Errorf("Couldn't figure out how to map '%v' to a digest", status.Name)
	}
}
<commit_msg>Update to only add digest if not nil<commit_after>package digests

import (
	"fmt"
	"sort"
	"strings"

	"github.com/ion-channel/ionic/rulesets"
	"github.com/ion-channel/ionic/scanner"
	"github.com/ion-channel/ionic/scans"
)

const (
	differenceIndex = iota
	virusFoundIndex
	criticalVulnerabilitiesIndex
	highVulnerabilitiesIndex
	totalVulnerabilitiesIndex
	uniqueVulnerabilitiesIndex
	licensesIndex
	filesScannedIndex
	directDependencyIndex
	transitiveDependencyIndex
	dependencyOutdatedIndex
	noVersionIndex
	languagesIndex
	uniqueCommittersIndex
	codeCoverageIndex
)

// NewDigests takes an applied ruleset and returns the relevant digests derived
// from all the evaluations in it, and any errors it encounters.
func NewDigests(appliedRuleset *rulesets.AppliedRulesetSummary, statuses []scanner.ScanStatus) ([]Digest, error) {
	ds := make([]Digest, 0)
	errs := make([]string, 0, 0)

	for i := range statuses {
		s := statuses[i]

		var e *scans.Evaluation
		if appliedRuleset != nil && appliedRuleset.RuleEvaluationSummary != nil {
			for i := range appliedRuleset.RuleEvaluationSummary.Ruleresults {
				if appliedRuleset.RuleEvaluationSummary.Ruleresults[i].ID == s.ID {
					e = &appliedRuleset.RuleEvaluationSummary.Ruleresults[i]
					break
				}
			}
		}

		d, err := _newDigests(&s, e)
		if err != nil {
			errs = append(errs, fmt.Sprintf("failed to make digest(s) from scan: %v", err.Error()))
			continue
		}

		if d != nil {
			ds = append(ds, d...)
		}
	}

	sort.Slice(ds, func(i, j int) bool { return ds[i].Index < ds[j].Index })

	if len(errs) > 0 {
		return ds, fmt.Errorf("failed to make some digests: %v", strings.Join(errs, "; "))
	}

	return ds, nil
}

func _newDigests(status *scanner.ScanStatus, eval *scans.Evaluation) ([]Digest, error) {
	if eval != nil {
		err := eval.Translate()
		if err != nil {
			return nil, fmt.Errorf("evaluation translate error: %v", err.Error())
		}
	}

	switch strings.ToLower(status.Name) {
	case "ecosystems":
		return ecosystemsDigests(status, eval)

	case "dependency":
		return dependencyDigests(status, eval)

	case "vulnerability":
		return vulnerabilityDigests(status, eval)

	case "virus":
		return virusDigests(status, eval)

	case "community":
		return communityDigests(status, eval)

	case "license":
		return licenseDigests(status, eval)

	case "external_coverage", "code_coverage", "coverage":
		return coveragDigests(status, eval)

	case "difference":
		return differenceDigests(status, eval)

	case "about_yml", "file_type":
		return nil, nil

	default:
		return nil, fmt.Errorf("Couldn't figure out how to map '%v' to a digest", status.Name)
	}
}
<|endoftext|>