<commit_before>package swag

import (
	"encoding/json"
	goparser "go/parser"
	"go/token"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestNew(t *testing.T) {
	New()
}

func TestParser_ParseGeneralApiInfo(t *testing.T) {
	var expected = `{
    "swagger": "2.0",
    "info": {
        "description": "This is a sample server Petstore server.",
        "title": "Swagger Example API",
        "termsOfService": "http://swagger.io/terms/",
        "contact": {
            "name": "API Support",
            "url": "http://www.swagger.io/support",
            "email": "support@swagger.io"
        },
        "license": {
            "name": "Apache 2.0",
            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
        },
        "version": "1.0"
    },
    "host": "petstore.swagger.io",
    "basePath": "/v2",
    "paths": {}
}`
	gopath := os.Getenv("GOPATH")
	assert.NotNil(t, gopath)
	p := New()
	p.ParseGeneralApiInfo("testdata/main.go")

	b, _ := json.MarshalIndent(p.swagger, "", "    ")
	assert.Equal(t, expected, string(b))
}

func TestParser_ParseGeneralApiInfoFailed(t *testing.T) {
	gopath := os.Getenv("GOPATH")
	assert.NotNil(t, gopath)
	p := New()
	assert.Panics(t, func() {
		p.ParseGeneralApiInfo("testdata/noexist.go")
	})
}

func TestGetAllGoFileInfo(t *testing.T) {
	searchDir := "example/simple"

	p := New()
	p.getAllGoFileInfo(searchDir)

	assert.NotEmpty(t, p.files["example/simple/main.go"])
	assert.NotEmpty(t, p.files["example/simple/web/handler.go"])
	assert.Equal(t, 4, len(p.files))
}

func TestParser_ParseType(t *testing.T) {
	searchDir := "example/simple/"

	p := New()
	p.getAllGoFileInfo(searchDir)

	for _, file := range p.files {
		p.ParseType(file)
	}

	assert.NotNil(t, p.TypeDefinitions["api"]["Pet3"])
	assert.NotNil(t, p.TypeDefinitions["web"]["Pet"])
	assert.NotNil(t, p.TypeDefinitions["web"]["Pet2"])
}

func TestGetSchemes(t *testing.T) {
	//TODO:
	//fmt.Println(GetSchemes("@schemes http https"))

}
func TestParseSimpleApi(t *testing.T) {
	var expected = `{
    "swagger": "2.0",
    "info": {
        "description": "This is a sample server Petstore server.",
        "title": "Swagger Example API",
        "termsOfService": "http://swagger.io/terms/",
        "contact": {
            "name": "API Support",
            "url": "http://www.swagger.io/support",
            "email": "support@swagger.io"
        },
        "license": {
            "name": "Apache 2.0",
            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
        },
        "version": "1.0"
    },
    "host": "petstore.swagger.io",
    "basePath": "/v2",
    "paths": {
        "/file/upload": {
            "post": {
                "description": "Upload file",
                "consumes": [
                    "multipart/form-data"
                ],
                "produces": [
                    "application/json"
                ],
                "summary": "Upload file",
                "operationId": "file.upload",
                "parameters": [
                    {
                        "type": "file",
                        "description": "this is a test file",
                        "name": "file",
                        "in": "formData",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "ok",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "We need ID!!",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    },
                    "404": {
                        "description": "Can not find ID",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    }
                }
            }
        },
        "/testapi/get-string-by-int/{some_id}": {
            "get": {
                "description": "get string by ID",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "summary": "Add a new pet to the store",
                "operationId": "get-string-by-int",
                "parameters": [
                    {
                        "type": "int",
                        "description": "Some ID",
                        "name": "some_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Some ID",
                        "name": "some_id",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.Pet"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "ok",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "We need ID!!",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    },
                    "404": {
                        "description": "Can not find ID",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    }
                }
            }
        },
        "/testapi/get-struct-array-by-string/{some_id}": {
            "get": {
                "description": "get struct array by ID",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "operationId": "get-struct-array-by-string",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Some ID",
                        "name": "some_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "int",
                        "description": "Offset",
                        "name": "offset",
                        "in": "query",
                        "required": true
                    },
                    {
                        "type": "int",
                        "description": "Offset",
                        "name": "limit",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "ok",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "We need ID!!",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    },
                    "404": {
                        "description": "Can not find ID",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    }
                }
            }
        }
    },
    "definitions": {
        "web.APIError": {
            "type": "object",
            "properties": {
                "CreatedAt": {
                    "type": "string"
                },
                "ErrorCode": {
                    "type": "int"
                },
                "ErrorMessage": {
                    "type": "string"
                }
            }
        },
        "web.Pet": {
            "type": "object",
            "properties": {
                "Category": {
                    "type": "object"
                },
                "ID": {
                    "type": "int"
                },
                "Name": {
                    "type": "string"
                },
                "PhotoUrls": {
                    "type": "array"
                },
                "Status": {
                    "type": "string"
                },
                "Tags": {
                    "type": "array"
                }
            }
        }
    }
}`
	searchDir := "example/simple"
	mainApiFile := "main.go"
	p := New()
	p.ParseApi(searchDir, mainApiFile)
	b, _ := json.MarshalIndent(p.swagger, "", "    ")

	assert.Equal(t, expected, string(b))
}

func TestParsePetApi(t *testing.T) {
	expected := `{
    "schemes": [
        "http",
        "https"
    ],
    "swagger": "2.0",
    "info": {
        "description": "This is a sample server Petstore server.  You can find out more about     Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).      For this sample, you can use the api key 'special-key' to test the authorization     filters.",
        "title": "Swagger Petstore",
        "termsOfService": "http://swagger.io/terms/",
        "contact": {
            "email": "apiteam@swagger.io"
        },
        "license": {
            "name": "Apache 2.0",
            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
        },
        "version": "1.0"
    },
    "host": "petstore.swagger.io",
    "basePath": "/v2",
    "paths": {}
}`
	searchDir := "example/pet"
	mainApiFile := "main.go"
	p := New()
	p.ParseApi(searchDir, mainApiFile)

	b, _ := json.MarshalIndent(p.swagger, "", "    ")
	assert.Equal(t, expected, string(b))
}

func TestParser_ParseRouterApiInfoErr(t *testing.T) {
	src := `
package test

// @Accept unknown
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	// Print the AST.
	//ast.Print(fset, f)

	p := New()
	assert.Panics(t, func() {
		p.ParseRouterApiInfo(f)
	})
}

func TestParser_ParseRouterApiGet(t *testing.T) {
	src := `
package test

// @Router /api/{id} [get]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Get)
}

func TestParser_ParseRouterApiPOST(t *testing.T) {
	src := `
package test

// @Router /api/{id} [post]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Post)
}

func TestParser_ParseRouterApiDELETE(t *testing.T) {
	src := `
package test

// @Router /api/{id} [delete]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Delete)
}

func TestParser_ParseRouterApiPUT(t *testing.T) {
	src := `
package test

// @Router /api/{id} [put]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Put)
}

func TestParser_ParseRouterApiPATCH(t *testing.T) {
	src := `
package test

// @Router /api/{id} [patch]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Patch)
}

func TestParser_ParseRouterApiHead(t *testing.T) {
	src := `
package test

// @Router /api/{id} [head]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Head)
}

func TestParser_ParseRouterApiOptions(t *testing.T) {
	src := `
package test

// @Router /api/{id} [options]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Options)
}

func TestParser_ParseRouterApiMultiple(t *testing.T) {
	src := `
package test

// @Router /api/{id} [get]
func Test1(){
}

// @Router /api/{id} [patch]
func Test2(){
}

// @Router /api/{id} [delete]
func Test3(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Get)
	assert.NotNil(t, val.Patch)
	assert.NotNil(t, val.Delete)
}
<commit_msg>fix the unit test<commit_after>package swag

import (
	"encoding/json"
	goparser "go/parser"
	"go/token"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestNew(t *testing.T) {
	New()
}

func TestParser_ParseGeneralApiInfo(t *testing.T) {
	var expected = `{
    "swagger": "2.0",
    "info": {
        "description": "This is a sample server Petstore server.",
        "title": "Swagger Example API",
        "termsOfService": "http://swagger.io/terms/",
        "contact": {
            "name": "API Support",
            "url": "http://www.swagger.io/support",
            "email": "support@swagger.io"
        },
        "license": {
            "name": "Apache 2.0",
            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
        },
        "version": "1.0"
    },
    "host": "petstore.swagger.io",
    "basePath": "/v2",
    "paths": {}
}`
	gopath := os.Getenv("GOPATH")
	assert.NotNil(t, gopath)
	p := New()
	p.ParseGeneralApiInfo("testdata/main.go")

	b, _ := json.MarshalIndent(p.swagger, "", "    ")
	assert.Equal(t, expected, string(b))
}

func TestParser_ParseGeneralApiInfoFailed(t *testing.T) {
	gopath := os.Getenv("GOPATH")
	assert.NotNil(t, gopath)
	p := New()
	assert.Panics(t, func() {
		p.ParseGeneralApiInfo("testdata/noexist.go")
	})
}

func TestGetAllGoFileInfo(t *testing.T) {
	searchDir := "example/simple"

	p := New()
	p.getAllGoFileInfo(searchDir)

	assert.NotEmpty(t, p.files["example/simple/main.go"])
	assert.NotEmpty(t, p.files["example/simple/web/handler.go"])
	assert.Equal(t, 4, len(p.files))
}

func TestParser_ParseType(t *testing.T) {
	searchDir := "example/simple/"

	p := New()
	p.getAllGoFileInfo(searchDir)

	for _, file := range p.files {
		p.ParseType(file)
	}

	assert.NotNil(t, p.TypeDefinitions["api"]["Pet3"])
	assert.NotNil(t, p.TypeDefinitions["web"]["Pet"])
	assert.NotNil(t, p.TypeDefinitions["web"]["Pet2"])
}

func TestGetSchemes(t *testing.T) {
	//TODO:
	//fmt.Println(GetSchemes("@schemes http https"))

}
func TestParseSimpleApi(t *testing.T) {
	var expected = `{
    "swagger": "2.0",
    "info": {
        "description": "This is a sample server Petstore server.",
        "title": "Swagger Example API",
        "termsOfService": "http://swagger.io/terms/",
        "contact": {
            "name": "API Support",
            "url": "http://www.swagger.io/support",
            "email": "support@swagger.io"
        },
        "license": {
            "name": "Apache 2.0",
            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
        },
        "version": "1.0"
    },
    "host": "petstore.swagger.io",
    "basePath": "/v2",
    "paths": {
        "/file/upload": {
            "post": {
                "description": "Upload file",
                "consumes": [
                    "multipart/form-data"
                ],
                "produces": [
                    "application/json"
                ],
                "summary": "Upload file",
                "operationId": "file.upload",
                "parameters": [
                    {
                        "type": "file",
                        "description": "this is a test file",
                        "name": "file",
                        "in": "formData",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "ok",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "We need ID!!",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    },
                    "404": {
                        "description": "Can not find ID",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    }
                }
            }
        },
        "/testapi/get-string-by-int/{some_id}": {
            "get": {
                "description": "get string by ID",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "summary": "Add a new pet to the store",
                "operationId": "get-string-by-int",
                "parameters": [
                    {
                        "type": "int",
                        "description": "Some ID",
                        "name": "some_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Some ID",
                        "name": "some_id",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.Pet"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "ok",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "We need ID!!",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    },
                    "404": {
                        "description": "Can not find ID",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    }
                }
            }
        },
        "/testapi/get-struct-array-by-string/{some_id}": {
            "get": {
                "description": "get struct array by ID",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "operationId": "get-struct-array-by-string",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Some ID",
                        "name": "some_id",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "int",
                        "description": "Offset",
                        "name": "offset",
                        "in": "query",
                        "required": true
                    },
                    {
                        "type": "int",
                        "description": "Offset",
                        "name": "limit",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "ok",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "We need ID!!",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    },
                    "404": {
                        "description": "Can not find ID",
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/web.APIError"
                        }
                    }
                }
            }
        }
    },
    "definitions": {
        "web.APIError": {
            "type": "object",
            "properties": {
                "CreatedAt": {
                    "type": "string"
                },
                "ErrorCode": {
                    "type": "integer"
                },
                "ErrorMessage": {
                    "type": "string"
                }
            }
        },
        "web.Pet": {
            "type": "object",
            "properties": {
                "Category": {
                    "type": "object"
                },
                "ID": {
                    "type": "integer"
                },
                "Name": {
                    "type": "string"
                },
                "PhotoUrls": {
                    "type": "array"
                },
                "Status": {
                    "type": "string"
                },
                "Tags": {
                    "type": "array"
                }
            }
        }
    }
}`
	searchDir := "example/simple"
	mainApiFile := "main.go"
	p := New()
	p.ParseApi(searchDir, mainApiFile)
	b, _ := json.MarshalIndent(p.swagger, "", "    ")

	assert.Equal(t, expected, string(b))
}

func TestParsePetApi(t *testing.T) {
	expected := `{
    "schemes": [
        "http",
        "https"
    ],
    "swagger": "2.0",
    "info": {
        "description": "This is a sample server Petstore server.  You can find out more about     Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).      For this sample, you can use the api key 'special-key' to test the authorization     filters.",
        "title": "Swagger Petstore",
        "termsOfService": "http://swagger.io/terms/",
        "contact": {
            "email": "apiteam@swagger.io"
        },
        "license": {
            "name": "Apache 2.0",
            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
        },
        "version": "1.0"
    },
    "host": "petstore.swagger.io",
    "basePath": "/v2",
    "paths": {}
}`
	searchDir := "example/pet"
	mainApiFile := "main.go"
	p := New()
	p.ParseApi(searchDir, mainApiFile)

	b, _ := json.MarshalIndent(p.swagger, "", "    ")
	assert.Equal(t, expected, string(b))
}

func TestParser_ParseRouterApiInfoErr(t *testing.T) {
	src := `
package test

// @Accept unknown
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	// Print the AST.
	//ast.Print(fset, f)

	p := New()
	assert.Panics(t, func() {
		p.ParseRouterApiInfo(f)
	})
}

func TestParser_ParseRouterApiGet(t *testing.T) {
	src := `
package test

// @Router /api/{id} [get]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Get)
}

func TestParser_ParseRouterApiPOST(t *testing.T) {
	src := `
package test

// @Router /api/{id} [post]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Post)
}

func TestParser_ParseRouterApiDELETE(t *testing.T) {
	src := `
package test

// @Router /api/{id} [delete]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Delete)
}

func TestParser_ParseRouterApiPUT(t *testing.T) {
	src := `
package test

// @Router /api/{id} [put]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Put)
}

func TestParser_ParseRouterApiPATCH(t *testing.T) {
	src := `
package test

// @Router /api/{id} [patch]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Patch)
}

func TestParser_ParseRouterApiHead(t *testing.T) {
	src := `
package test

// @Router /api/{id} [head]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Head)
}

func TestParser_ParseRouterApiOptions(t *testing.T) {
	src := `
package test

// @Router /api/{id} [options]
func Test(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Options)
}

func TestParser_ParseRouterApiMultiple(t *testing.T) {
	src := `
package test

// @Router /api/{id} [get]
func Test1(){
}

// @Router /api/{id} [patch]
func Test2(){
}

// @Router /api/{id} [delete]
func Test3(){
}
`
	f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
	if err != nil {
		panic(err)
	}
	p := New()
	p.ParseRouterApiInfo(f)

	ps := p.swagger.Paths.Paths

	val, ok := ps["/api/{id}"]

	assert.True(t, ok)
	assert.NotNil(t, val.Get)
	assert.NotNil(t, val.Patch)
	assert.NotNil(t, val.Delete)
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"strings"
	"testing"
)

func TestParser_Scan(t *testing.T) {
	var tests = []struct {
		s    string
		expr Expression
		err  string
	}{
		{
			s: `a = b`,
			expr: Statement{
				Left:  "a",
				Right: "b",
			},
		},
		{
			s: `1 + 2`,
			expr: Binary{
				Left:     Int(1),
				Right:    Int(2),
				Operator: "+",
			},
		},
		{
			s:   `a`,
			err: `found "", expected '='`,
		},
	}

	for i, tt := range tests {
		expr, err := NewParser(strings.NewReader(tt.s)).Parse()
		if !strings.Contains(errstring(err), tt.err) {
			t.Errorf("%d. %q: error mismatch:\n  exp=%s\n  got=%s\n\n", i, tt.s, tt.err, err)
		} else if tt.err == "" && !reflect.DeepEqual(tt.expr.Evaluate(), (*expr).Evaluate()) {
			t.Errorf("%d. %q\n\nstmt mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.s, tt.expr.Evaluate(), (*expr).Evaluate())
		}
	}
}

// errstring returns the string representation of an error.
func errstring(err error) string {
	if err != nil {
		return err.Error()
	}
	return ""
}
<commit_msg>Add minus and times test<commit_after>package main

import (
	"reflect"
	"strings"
	"testing"
)

func TestParser_Scan(t *testing.T) {
	var tests = []struct {
		s    string
		expr Expression
		err  string
	}{
		{
			s: `a = b`,
			expr: Statement{
				Left:  "a",
				Right: "b",
			},
		},
		{
			s: `1 + 2`,
			expr: Binary{
				Left:     Int(1),
				Right:    Int(2),
				Operator: "+",
			},
		},
		{
			s: `1 - 2`,
			expr: Binary{
				Left:     Int(1),
				Right:    Int(2),
				Operator: "-",
			},
		},
		{
			s: `1* 2`,
			expr: Binary{
				Left:     Int(1),
				Right:    Int(2),
				Operator: "*",
			},
		},
		{
			s:   `a`,
			err: `found "", expected '='`,
		},
	}

	for i, tt := range tests {
		expr, err := NewParser(strings.NewReader(tt.s)).Parse()
		if !strings.Contains(errstring(err), tt.err) {
			t.Errorf("%d. %q: error mismatch:\n  exp=%s\n  got=%s\n\n", i, tt.s, tt.err, err)
		} else if tt.err == "" && !reflect.DeepEqual(tt.expr.Evaluate(), (*expr).Evaluate()) {
			t.Errorf("%d. %q\n\nstmt mismatch:\n\nexp=%#v\n\ngot=%#v\n\n", i, tt.s, tt.expr.Evaluate(), (*expr).Evaluate())
		}
	}
}

// errstring returns the string representation of an error.
func errstring(err error) string {
	if err != nil {
		return err.Error()
	}
	return ""
}
<|endoftext|>


<commit_before>package nmea

import (
	"encoding/json"
	"strings"
	"testing"
)

func TestChecksum(t *testing.T) {
	tests := map[string]bool{
		"$GPRMC,162254.00,A,3723.02837,N,12159.39853,W,0.820,188.36,110706,,,A*74": true,
		"$GPRMC,162254.00,A,3723.02837,N,12159.39853,W,0.820,188.36,110706,,,A*72": false,
	}

	for in, exp := range tests {
		if checkChecksum(in) != exp {
			t.Errorf("Failed on %v/%v", in, exp)
		}
	}
}

func TestSampleParsing(t *testing.T) {
	for _, s := range strings.Split(ubloxSample, "\n") {
		parseMessage(s, nil)
	}
}

type rmcHandler struct{ t *testing.T }

func (r rmcHandler) HandleRMC(rmc RMC) {
	j, err := json.Marshal(rmc)
	if err != nil {
		panic(err)
	}
	r.t.Logf("%s", j)
}

func TestRMCHandling(t *testing.T) {
	for _, s := range strings.Split(ubloxSample, "\n") {
		parseMessage(s, rmcHandler{t})
	}
}
<commit_msg>Increase checksum coverage<commit_after>package nmea

import (
	"encoding/json"
	"strings"
	"testing"
)

func TestChecksum(t *testing.T) {
	tests := map[string]bool{
		"":     false,
		"*00":  false,
		"$*00": true,
		"$*01": false,
		"^0*0": false,
		"$0*0": false,
		"$*xx": false,
		"$GPRMC,162254.00,A,3723.02837,N,12159.39853,W,0.820,188.36,110706,,,A*74": true,
		"$GPRMC,162254.00,A,3723.02837,N,12159.39853,W,0.820,188.36,110706,,,A*72": false,
	}

	for in, exp := range tests {
		if checkChecksum(in) != exp {
			t.Errorf("Failed on %v/%v", in, exp)
		}
	}
}

func TestSampleParsing(t *testing.T) {
	for _, s := range strings.Split(ubloxSample, "\n") {
		parseMessage(s, nil)
	}
}

type rmcHandler struct{ t *testing.T }

func (r rmcHandler) HandleRMC(rmc RMC) {
	j, err := json.Marshal(rmc)
	if err != nil {
		panic(err)
	}
	r.t.Logf("%s", j)
}

func TestRMCHandling(t *testing.T) {
	for _, s := range strings.Split(ubloxSample, "\n") {
		parseMessage(s, rmcHandler{t})
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Damjan Cvetko. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package pcapgo

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
)

// Reader wraps an underlying io.Reader to read packet data in PCAP
// format.  See http://wiki.wireshark.org/Development/LibpcapFileFormat
// for information on the file format.
//
// We currenty read v2.4 file format with nanosecond and microsecdond
// timestamp resolution in little-endian and big-endian encoding.
type Reader struct {
	r              io.Reader
	byteOrder      binary.ByteOrder
	nanoSecsFactor uint32
	versionMajor   uint16
	versionMinor   uint16
	// timezone
	// sigfigs
	snaplen  uint32
	linkType layers.LinkType
	// reusable buffer
	buf []byte
}

const magicNanoseconds = 0xA1B23C4D
const magicMicrosecondsBigendian = 0xD4C3B2A1
const magicNanosecondsBigendian = 0x4D3CB2A1

// NewReader returns a new reader object, for reading packet data from
// the given reader. The reader must be open and header data is
// read from it at this point.
// If the file format is not supported an error is returned
//
//  // Create new reader:
//  f, _ := os.Open("/tmp/file.pcap")
//  defer f.Close()
//  r, err := NewReader(f)
//  data, ci, err := r.ReadPacketData()
func NewReader(r io.Reader) (*Reader, error) {
	ret := Reader{r: r}
	if err := ret.readHeader(); err != nil {
		return nil, err
	}
	return &ret, nil
}

func (r *Reader) readHeader() error {
	buf := make([]byte, 24)
	if n, err := io.ReadFull(r.r, buf); err != nil {
		return err
	} else if n < 24 {
		return errors.New("Not enough data for read")
	}
	if magic := binary.LittleEndian.Uint32(buf[0:4]); magic == magicNanoseconds {
		r.byteOrder = binary.LittleEndian
		r.nanoSecsFactor = 1
	} else if magic == magicNanosecondsBigendian {
		r.byteOrder = binary.BigEndian
		r.nanoSecsFactor = 1
	} else if magic == magicMicroseconds {
		r.byteOrder = binary.LittleEndian
		r.nanoSecsFactor = 1000
	} else if magic == magicMicrosecondsBigendian {
		r.byteOrder = binary.BigEndian
		r.nanoSecsFactor = 1000
	} else {
		return errors.New(fmt.Sprintf("Unknown maigc %x", magic))
	}
	if r.versionMajor = r.byteOrder.Uint16(buf[4:6]); r.versionMajor != versionMajor {
		return errors.New(fmt.Sprintf("Unknown major version %d", r.versionMajor))
	}
	if r.versionMinor = r.byteOrder.Uint16(buf[6:8]); r.versionMinor != versionMinor {
		return errors.New(fmt.Sprintf("Unknown minor version %d", r.versionMinor))
	}
	// ignore timezone 8:12 and sigfigs 12:16
	r.snaplen = r.byteOrder.Uint32(buf[16:20])
	r.buf = make([]byte, r.snaplen+16)
	r.linkType = layers.LinkType(r.byteOrder.Uint32(buf[20:24]))
	return nil
}

// Read next packet from file
func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
	if ci, err = r.readPacketHeader(); err != nil {
		return
	}

	var n int
	if 16+ci.CaptureLength > len(r.buf) {
		return
	}
	data = r.buf[16 : 16+ci.CaptureLength]
	if n, err = io.ReadFull(r.r, data); err != nil {
		err = fmt.Errorf("capture length with header exceeds buffer size: %d > %d", 16+ci.CaptureLength, len(r.buf))
		return
	} else if n < ci.CaptureLength {
		err = io.ErrUnexpectedEOF
	}
	return
}

func (r *Reader) readPacketHeader() (ci gopacket.CaptureInfo, err error) {
	var n int
	if n, err = io.ReadFull(r.r, r.buf[0:16]); err != nil {
		return
	} else if n < 16 {
		err = io.ErrUnexpectedEOF
		return
	}
	ci.Timestamp = time.Unix(int64(r.byteOrder.Uint32(r.buf[0:4])), int64(r.byteOrder.Uint32(r.buf[4:8])*r.nanoSecsFactor)).UTC()
	ci.CaptureLength = int(r.byteOrder.Uint32(r.buf[8:12]))
	ci.Length = int(r.byteOrder.Uint32(r.buf[12:16]))
	return
}

// LinkType returns network, as a layers.LinkType.
func (r *Reader) LinkType() layers.LinkType {
	return r.linkType
}

// Reader formater
func (r *Reader) String() string {
	return fmt.Sprintf("PcapFile  maj: %x min: %x snaplen: %d linktype: %s", r.versionMajor, r.versionMinor, r.snaplen, r.linkType)
}
<commit_msg>Return error on correct line<commit_after>// Copyright 2014 Damjan Cvetko. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package pcapgo

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
)

// Reader wraps an underlying io.Reader to read packet data in PCAP
// format.  See http://wiki.wireshark.org/Development/LibpcapFileFormat
// for information on the file format.
//
// We currenty read v2.4 file format with nanosecond and microsecdond
// timestamp resolution in little-endian and big-endian encoding.
type Reader struct {
	r              io.Reader
	byteOrder      binary.ByteOrder
	nanoSecsFactor uint32
	versionMajor   uint16
	versionMinor   uint16
	// timezone
	// sigfigs
	snaplen  uint32
	linkType layers.LinkType
	// reusable buffer
	buf []byte
}

const magicNanoseconds = 0xA1B23C4D
const magicMicrosecondsBigendian = 0xD4C3B2A1
const magicNanosecondsBigendian = 0x4D3CB2A1

// NewReader returns a new reader object, for reading packet data from
// the given reader. The reader must be open and header data is
// read from it at this point.
// If the file format is not supported an error is returned
//
//  // Create new reader:
//  f, _ := os.Open("/tmp/file.pcap")
//  defer f.Close()
//  r, err := NewReader(f)
//  data, ci, err := r.ReadPacketData()
func NewReader(r io.Reader) (*Reader, error) {
	ret := Reader{r: r}
	if err := ret.readHeader(); err != nil {
		return nil, err
	}
	return &ret, nil
}

func (r *Reader) readHeader() error {
	buf := make([]byte, 24)
	if n, err := io.ReadFull(r.r, buf); err != nil {
		return err
	} else if n < 24 {
		return errors.New("Not enough data for read")
	}
	if magic := binary.LittleEndian.Uint32(buf[0:4]); magic == magicNanoseconds {
		r.byteOrder = binary.LittleEndian
		r.nanoSecsFactor = 1
	} else if magic == magicNanosecondsBigendian {
		r.byteOrder = binary.BigEndian
		r.nanoSecsFactor = 1
	} else if magic == magicMicroseconds {
		r.byteOrder = binary.LittleEndian
		r.nanoSecsFactor = 1000
	} else if magic == magicMicrosecondsBigendian {
		r.byteOrder = binary.BigEndian
		r.nanoSecsFactor = 1000
	} else {
		return errors.New(fmt.Sprintf("Unknown maigc %x", magic))
	}
	if r.versionMajor = r.byteOrder.Uint16(buf[4:6]); r.versionMajor != versionMajor {
		return errors.New(fmt.Sprintf("Unknown major version %d", r.versionMajor))
	}
	if r.versionMinor = r.byteOrder.Uint16(buf[6:8]); r.versionMinor != versionMinor {
		return errors.New(fmt.Sprintf("Unknown minor version %d", r.versionMinor))
	}
	// ignore timezone 8:12 and sigfigs 12:16
	r.snaplen = r.byteOrder.Uint32(buf[16:20])
	r.buf = make([]byte, r.snaplen+16)
	r.linkType = layers.LinkType(r.byteOrder.Uint32(buf[20:24]))
	return nil
}

// Read next packet from file
func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
	if ci, err = r.readPacketHeader(); err != nil {
		return
	}

	var n int
	if 16+ci.CaptureLength > len(r.buf) {
		err = fmt.Errorf("capture length with header exceeds buffer size: %d > %d", 16+ci.CaptureLength, len(r.buf))
		return
	}
	data = r.buf[16 : 16+ci.CaptureLength]
	if n, err = io.ReadFull(r.r, data); err != nil {
		return
	} else if n < ci.CaptureLength {
		err = io.ErrUnexpectedEOF
	}
	return
}

func (r *Reader) readPacketHeader() (ci gopacket.CaptureInfo, err error) {
	var n int
	if n, err = io.ReadFull(r.r, r.buf[0:16]); err != nil {
		return
	} else if n < 16 {
		err = io.ErrUnexpectedEOF
		return
	}
	ci.Timestamp = time.Unix(int64(r.byteOrder.Uint32(r.buf[0:4])), int64(r.byteOrder.Uint32(r.buf[4:8])*r.nanoSecsFactor)).UTC()
	ci.CaptureLength = int(r.byteOrder.Uint32(r.buf[8:12]))
	ci.Length = int(r.byteOrder.Uint32(r.buf[12:16]))
	return
}

// LinkType returns network, as a layers.LinkType.
func (r *Reader) LinkType() layers.LinkType {
	return r.linkType
}

// Reader formater
func (r *Reader) String() string {
	return fmt.Sprintf("PcapFile  maj: %x min: %x snaplen: %d linktype: %s", r.versionMajor, r.versionMinor, r.snaplen, r.linkType)
}
<|endoftext|>


<commit_before>package speed

// Instance wraps a PCP compatible Instance
type Instance struct {
	name  string
	id    uint32
	indom InstanceDomain
}

// newInstance generates a new Instance type based on the passed parameters
// the id is passed explicitly as it is assumed that this will be constructed
// after initializing the InstanceDomain
// this is not a part of the public API as this is not supposed to be used directly,
// but instead added using the AddInstance method of InstanceDomain
func newInstance(id uint32, name string, indom InstanceDomain) *Instance {
	return &Instance{
		name, id, indom,
	}
}

func (i *Instance) String() string {
	return "Instance: " + i.name
}
<commit_msg>instance: add offset<commit_after>package speed

// Instance wraps a PCP compatible Instance
type Instance struct {
	name   string
	id     uint32
	indom  InstanceDomain
	offset int
}

// newInstance generates a new Instance type based on the passed parameters
// the id is passed explicitly as it is assumed that this will be constructed
// after initializing the InstanceDomain
// this is not a part of the public API as this is not supposed to be used directly,
// but instead added using the AddInstance method of InstanceDomain
func newInstance(id uint32, name string, indom InstanceDomain) *Instance {
	return &Instance{
		name, id, indom, 0,
	}
}

func (i *Instance) String() string {
	return "Instance: " + i.name
}

func (i *Instance) Offset() int { return i.offset }

func (i *Instance) SetOffset(offset int) { i.offset = offset }
<|endoftext|>


<commit_before>package sudoku

import (
	"reflect"
	"testing"
)

var sampleSolveDirections SolveDirections

func init() {
	sampleSolveDirections = SolveDirections{
		&SolveStep{
			nil,
			nil,
			nil,
			nil,
			techniquesByName["Necessary In Row"],
		},
		&SolveStep{
			nil,
			nil,
			nil,
			nil,
			techniquesByName["Guess"],
		},
		&SolveStep{
			nil,
			nil,
			nil,
			nil,
			techniquesByName["Naked Pair Block"],
		},
		&SolveStep{
			nil,
			nil,
			nil,
			nil,
			techniquesByName["Guess"],
		},
	}
}

//TODO: the other solvedirections tests should be in this file.

func TestDifficultySignals(t *testing.T) {
	signals := DifficultySignals{"a": 1.0, "b": 5.0}
	other := DifficultySignals{"a": 3.2, "c": 6.0}

	golden := DifficultySignals{"a": 3.2, "b": 5.0, "c": 6.0}
	signals.Add(other)

	if !reflect.DeepEqual(signals, golden) {
		t.Error("Signals when added didn't have right values. Got", signals, "expected", golden)
	}
}

func TestSolveDirectionsSignals(t *testing.T) {
	result := sampleSolveDirections.Signals()
	golden := DifficultySignals{}

	for _, technique := range AllTechniques {
		golden[technique.Name()+" Count"] = 0.0
		golden[technique.Name()+" Percentage"] = 0.0
	}
	golden["Guess Count"] = 2.0
	golden["Necessary In Row Count"] = 1.0
	golden["Naked Pair Block Count"] = 1.0
	golden["Number of Steps"] = 4.0
	golden["Percentage Fill Steps"] = 0.75
	golden["Number Unfilled Cells"] = 3.0
	golden["Steps Until Nonfill"] = 2.0
	golden["Guess Percentage"] = 0.5
	golden["Necessary In Row Percentage"] = 0.25
	golden["Naked Pair Block Percentage"] = 0.25

	if !reflect.DeepEqual(result, golden) {
		t.Error("SolveDirections.Signals on sampleSolveDirections didn't return right value. Got: ", result, " expected: ", golden)
	}
}

func TestTechniqueSignal(t *testing.T) {

	result := signalTechnique(sampleSolveDirections)

	golden := DifficultySignals{}

	for _, technique := range AllTechniques {
		golden[technique.Name()+" Count"] = 0.0
	}

	golden["Guess Count"] = 2.0
	golden["Necessary In Row Count"] = 1.0
	golden["Naked Pair Block Count"] = 1.0

	if !reflect.DeepEqual(result, golden) {
		t.Error("Technique signal didn't work as expected. Got", result, "expected", golden)
	}
}

func TestTechniqueSignalPercentage(t *testing.T) {

	result := signalTechniquePercentage(sampleSolveDirections)

	golden := DifficultySignals{}

	for _, technique := range AllTechniques {
		golden[technique.Name()+" Percentage"] = 0.0
	}

	golden["Guess Percentage"] = 0.5
	golden["Necessary In Row Percentage"] = 0.25
	golden["Naked Pair Block Percentage"] = 0.25

	if !reflect.DeepEqual(result, golden) {
		t.Error("Technique signal percentage didn't work as expected. Got", result, "expected", golden)
	}
}

func TestSignalNumberOfSteps(t *testing.T) {
	result := signalNumberOfSteps(sampleSolveDirections)
	golden := DifficultySignals{
		"Number of Steps": 4.0,
	}
	if !reflect.DeepEqual(result, golden) {
		t.Error("Number of steps signal didn't work as expected. Got ", result, "expected", golden)
	}
}

func TestSignalPercentageFillSteps(t *testing.T) {
	result := signalPercentageFilledSteps(sampleSolveDirections)
	golden := DifficultySignals{
		"Percentage Fill Steps": 0.75,
	}
	if !reflect.DeepEqual(result, golden) {
		t.Error("Percentage fill steps didn't work as expected. Got ", result, " expected ", golden)
	}
}

func TestSignalNumberUnfilled(t *testing.T) {
	result := signalNumberUnfilled(sampleSolveDirections)
	golden := DifficultySignals{
		"Number Unfilled Cells": 3.0,
	}
	if !reflect.DeepEqual(result, golden) {
		t.Error("Number unfilled cells didn't work as expected. Got", result, "expected ", golden)
	}

}

func TestSignalStepsUntilNonFill(t *testing.T) {
	result := signalStepsUntilNonFill(sampleSolveDirections)
	golden := DifficultySignals{
		"Steps Until Nonfill": 2.0,
	}
	if !reflect.DeepEqual(result, golden) {
		t.Error("Steps until nonfill didn't work as expected. Got ", result, "expected", golden)
	}
}
<commit_msg>Added a test to verify Signals.Difficulty() is working.<commit_after>package sudoku

import (
	"math"
	"reflect"
	"testing"
)

var sampleSolveDirections SolveDirections

func init() {
	sampleSolveDirections = SolveDirections{
		&SolveStep{
			nil,
			nil,
			nil,
			nil,
			techniquesByName["Necessary In Row"],
		},
		&SolveStep{
			nil,
			nil,
			nil,
			nil,
			techniquesByName["Guess"],
		},
		&SolveStep{
			nil,
			nil,
			nil,
			nil,
			techniquesByName["Naked Pair Block"],
		},
		&SolveStep{
			nil,
			nil,
			nil,
			nil,
			techniquesByName["Guess"],
		},
	}
}

//TODO: the other solvedirections tests should be in this file.

func TestDifficultySignals(t *testing.T) {
	signals := DifficultySignals{"a": 1.0, "b": 5.0}
	other := DifficultySignals{"a": 3.2, "c": 6.0}

	golden := DifficultySignals{"a": 3.2, "b": 5.0, "c": 6.0}
	signals.Add(other)

	if !reflect.DeepEqual(signals, golden) {
		t.Error("Signals when added didn't have right values. Got", signals, "expected", golden)
	}
}

func TestSolveDirectionsSignals(t *testing.T) {
	result := sampleSolveDirections.Signals()
	golden := DifficultySignals{}

	for _, technique := range AllTechniques {
		golden[technique.Name()+" Count"] = 0.0
		golden[technique.Name()+" Percentage"] = 0.0
	}
	golden["Guess Count"] = 2.0
	golden["Necessary In Row Count"] = 1.0
	golden["Naked Pair Block Count"] = 1.0
	golden["Number of Steps"] = 4.0
	golden["Percentage Fill Steps"] = 0.75
	golden["Number Unfilled Cells"] = 3.0
	golden["Steps Until Nonfill"] = 2.0
	golden["Guess Percentage"] = 0.5
	golden["Necessary In Row Percentage"] = 0.25
	golden["Naked Pair Block Percentage"] = 0.25

	if !reflect.DeepEqual(result, golden) {
		t.Error("SolveDirections.Signals on sampleSolveDirections didn't return right value. Got: ", result, " expected: ", golden)
	}

	//We're going to swap out the real difficulty signal weights for the test.
	realWeights := DifficultySignalWeights
	defer func() {
		DifficultySignalWeights = realWeights
	}()

	DifficultySignalWeights = map[string]float64{
		"Constant":               0.5,
		"Guess Count":            -0.09,
		"Necessary In Row Count": -0.07,
		"Naked Pair Block Count": 0.13,
		"Number of Steps":        0.11,
	}

	difficulty := result.Difficulty()
	expectedDifficulty := 0.82

	if math.Abs(difficulty-expectedDifficulty) > 0.00000000001 {
		t.Error("Got wrong difficulty from baked signals: ", difficulty, "expected", 0.82)
	}

}

func TestTechniqueSignal(t *testing.T) {

	result := signalTechnique(sampleSolveDirections)

	golden := DifficultySignals{}

	for _, technique := range AllTechniques {
		golden[technique.Name()+" Count"] = 0.0
	}

	golden["Guess Count"] = 2.0
	golden["Necessary In Row Count"] = 1.0
	golden["Naked Pair Block Count"] = 1.0

	if !reflect.DeepEqual(result, golden) {
		t.Error("Technique signal didn't work as expected. Got", result, "expected", golden)
	}
}

func TestTechniqueSignalPercentage(t *testing.T) {

	result := signalTechniquePercentage(sampleSolveDirections)

	golden := DifficultySignals{}

	for _, technique := range AllTechniques {
		golden[technique.Name()+" Percentage"] = 0.0
	}

	golden["Guess Percentage"] = 0.5
	golden["Necessary In Row Percentage"] = 0.25
	golden["Naked Pair Block Percentage"] = 0.25

	if !reflect.DeepEqual(result, golden) {
		t.Error("Technique signal percentage didn't work as expected. Got", result, "expected", golden)
	}
}

func TestSignalNumberOfSteps(t *testing.T) {
	result := signalNumberOfSteps(sampleSolveDirections)
	golden := DifficultySignals{
		"Number of Steps": 4.0,
	}
	if !reflect.DeepEqual(result, golden) {
		t.Error("Number of steps signal didn't work as expected. Got ", result, "expected", golden)
	}
}

func TestSignalPercentageFillSteps(t *testing.T) {
	result := signalPercentageFilledSteps(sampleSolveDirections)
	golden := DifficultySignals{
		"Percentage Fill Steps": 0.75,
	}
	if !reflect.DeepEqual(result, golden) {
		t.Error("Percentage fill steps didn't work as expected. Got ", result, " expected ", golden)
	}
}

func TestSignalNumberUnfilled(t *testing.T) {
	result := signalNumberUnfilled(sampleSolveDirections)
	golden := DifficultySignals{
		"Number Unfilled Cells": 3.0,
	}
	if !reflect.DeepEqual(result, golden) {
		t.Error("Number unfilled cells didn't work as expected. Got", result, "expected ", golden)
	}

}

func TestSignalStepsUntilNonFill(t *testing.T) {
	result := signalStepsUntilNonFill(sampleSolveDirections)
	golden := DifficultySignals{
		"Steps Until Nonfill": 2.0,
	}
	if !reflect.DeepEqual(result, golden) {
		t.Error("Steps until nonfill didn't work as expected. Got ", result, "expected", golden)
	}
}
<|endoftext|>


<commit_before>package sudoku

import (
	"fmt"
	"log"
	"strconv"
)

type forcingChainsTechnique struct {
	*basicSolveTechnique
}

func (self *forcingChainsTechnique) HumanLikelihood() float64 {
	//TODO: figure out what the baseDifficulty should be
	return self.difficultyHelper(200.0)
}

func (self *forcingChainsTechnique) Description(step *SolveStep) string {
	//TODO: implement this
	return "ERROR: NOT IMPLEMENTED"
}

func (self *forcingChainsTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {
	//TODO: test that this will find multiple if they exist.
	//TODO: Implement this.

	getter := grid.queue().DefaultGetter()

	_MAX_IMPLICATION_STEPS := 6

	for {

		//Check if it's time to stop.
		select {
		case <-done:
			return
		default:
		}

		candidate := getter.GetSmallerThan(3)

		if candidate == nil {
			break
		}

		candidateCell := candidate.(*Cell)

		if len(candidateCell.Possibilities()) != 2 {
			//We found one with 1 possibility, which isn't interesting for us--nakedSingle should do that one.
			continue
		}

		firstPossibilityNum := candidateCell.Possibilities()[0]
		secondPossibilityNum := candidateCell.Possibilities()[1]

		firstGrid := grid.Copy()
		secondGrid := grid.Copy()

		//Check that the neighbor isn't just already having a single possibility, because then this technique is overkill.

		firstAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)
		secondAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(firstGrid),
			firstPossibilityNum,
			firstAccumulator)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(secondGrid),
			secondPossibilityNum,
			secondAccumulator)

		//TODO:Check if the sets overlap.

		//For these debugging purposes, only print out the candidateCell we know to be interesting in the test case.
		if candidateCell.Row() == 1 && candidateCell.Col() == 0 {
			log.Println(firstAccumulator)
			log.Println(secondAccumulator)
		}

		//See if either branch, at some generation, has the same cell forced to the same number in either generation.

		//accumulate forward, so the last generation has ALL cells affected in any generation on that branch
		firstAccumulator.accumulateGenerations()
		secondAccumulator.accumulateGenerations()

		//Check for any overlap at the last generation
		firstAffectedCells := firstAccumulator[0].filledNumbers
		secondAffectedCells := secondAccumulator[0].filledNumbers

		for key, val := range firstAffectedCells {
			if num, ok := secondAffectedCells[key]; ok {
				//Found cell overlap! ... is the forced number the same?
				if val == num {
					//Yup, seems like we've found a cell that is forced to the same value on either branch.
					step := &SolveStep{self,
						CellSlice{key.Cell(grid)},
						IntSlice{val},
						CellSlice{candidateCell},
						candidateCell.Possibilities(),
					}

					if step.IsUseful(grid) {
						select {
						case results <- step:
						case <-done:
							return
						}
					}
				}
			}
		}

		//TODO: do we really need the cellSet? (if we remove it, add a note to remove it from cellslice.go)
		//TODO: figure out a way to only compute a generation if required on each branch (don't compute all the way to _MAX_IMPLICATIONS to start)

	}
}

type chainSearcherGenerationDetails struct {
	affectedCells cellSet
	filledNumbers map[cellRef]int
}

func (c chainSearcherGenerationDetails) String() string {
	result := "Begin map\n"
	for cell, num := range c.filledNumbers {
		result += "\t" + cell.String() + " : " + strconv.Itoa(num) + "\n"
	}
	result += "End map\n"
	return result
}

type chainSearcherAccumulator []*chainSearcherGenerationDetails

func (c chainSearcherAccumulator) String() string {
	result := "Accumulator[\n"
	for _, rec := range c {
		result += fmt.Sprintf("%s\n", rec)
	}
	result += "]\n"
	return result
}

//accumulateGenerations goes through each generation (youngest to newest)
//and squaches older generation maps into each generation, so each
//generation's map represents the totality of all cells seen at that point.
func (c chainSearcherAccumulator) accumulateGenerations() {
	for i := len(c) - 2; i >= 0; i-- {
		lastGeneration := c[i+1].filledNumbers
		currentGeneration := c[i].filledNumbers
		for key, val := range lastGeneration {
			currentGeneration[key] = val
		}
	}
}

func makeChainSeacherAccumulator(size int) chainSearcherAccumulator {
	result := make(chainSearcherAccumulator, size)
	for i := 0; i < size; i++ {
		result[i] = &chainSearcherGenerationDetails{
			affectedCells: make(cellSet),
			filledNumbers: make(map[cellRef]int),
		}
	}
	return result
}

func chainSearcher(i int, cell *Cell, numToApply int, accumulator chainSearcherAccumulator) {
	if i <= 0 || cell == nil {
		//Base case
		return
	}

	if i-1 >= len(accumulator) {
		panic("The accumulator provided was not big enough for the i provided.")
	}

	generationDetails := accumulator[i-1]

	//Find the nextCells that WILL have their numbers forced by the cell we're thinking of fillint.
	cellsToVisit := cell.Neighbors().FilterByPossible(numToApply).FilterByNumPossibilities(2)

	//Now that we know which cells will be affected and what their next number will be,
	//set the number in the given cell and then recurse downward down each branch.
	cell.SetNumber(numToApply)

	generationDetails.affectedCells[cell.ref()] = true
	generationDetails.filledNumbers[cell.ref()] = numToApply

	for _, cellToVisit := range cellsToVisit {

		possibilities := cellToVisit.Possibilities()

		if len(possibilities) != 1 {
			panic("Expected the cell to have one possibility")
		}

		forcedNum := possibilities[0]

		//Each branch modifies the grid, so create a new copy
		newGrid := cellToVisit.grid.Copy()
		cellToVisit = cellToVisit.InGrid(newGrid)

		//Recurse downward
		chainSearcher(i-1, cellToVisit, forcedNum, accumulator)

	}

}
<commit_msg>TESTS FAIL. Slightly better debugging ot figure out what's breakign<commit_after>package sudoku

import (
	"fmt"
	"log"
	"strconv"
)

type forcingChainsTechnique struct {
	*basicSolveTechnique
}

func (self *forcingChainsTechnique) HumanLikelihood() float64 {
	//TODO: figure out what the baseDifficulty should be
	return self.difficultyHelper(200.0)
}

func (self *forcingChainsTechnique) Description(step *SolveStep) string {
	//TODO: implement this
	return "ERROR: NOT IMPLEMENTED"
}

func (self *forcingChainsTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {
	//TODO: test that this will find multiple if they exist.
	//TODO: Implement this.

	getter := grid.queue().DefaultGetter()

	_MAX_IMPLICATION_STEPS := 6

	for {

		//Check if it's time to stop.
		select {
		case <-done:
			return
		default:
		}

		candidate := getter.GetSmallerThan(3)

		if candidate == nil {
			break
		}

		candidateCell := candidate.(*Cell)

		if len(candidateCell.Possibilities()) != 2 {
			//We found one with 1 possibility, which isn't interesting for us--nakedSingle should do that one.
			continue
		}

		firstPossibilityNum := candidateCell.Possibilities()[0]
		secondPossibilityNum := candidateCell.Possibilities()[1]

		firstGrid := grid.Copy()
		secondGrid := grid.Copy()

		//Check that the neighbor isn't just already having a single possibility, because then this technique is overkill.

		firstAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)
		secondAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(firstGrid),
			firstPossibilityNum,
			firstAccumulator)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(secondGrid),
			secondPossibilityNum,
			secondAccumulator)

		//TODO:Check if the sets overlap.

		doPrint := candidateCell.Row() == 1 && candidateCell.Col() == 0

		//For these debugging purposes, only print out the candidateCell we know to be interesting in the test case.
		if doPrint {
			log.Println(firstAccumulator)
			log.Println(secondAccumulator)
		}

		//See if either branch, at some generation, has the same cell forced to the same number in either generation.

		//accumulate forward, so the last generation has ALL cells affected in any generation on that branch
		firstAccumulator.accumulateGenerations()
		secondAccumulator.accumulateGenerations()

		//Check for any overlap at the last generation
		firstAffectedCells := firstAccumulator[0].filledNumbers
		secondAffectedCells := secondAccumulator[0].filledNumbers

		for key, val := range firstAffectedCells {
			if num, ok := secondAffectedCells[key]; ok {
				//Found cell overlap! ... is the forced number the same?
				if val == num {
					//Yup, seems like we've found a cell that is forced to the same value on either branch.
					step := &SolveStep{self,
						CellSlice{key.Cell(grid)},
						IntSlice{val},
						CellSlice{candidateCell},
						candidateCell.Possibilities(),
					}

					if doPrint {
						log.Println(step)
					}

					if step.IsUseful(grid) {
						select {
						case results <- step:
						case <-done:
							return
						}
					}
				}
			}
		}

		//TODO: do we really need the cellSet? (if we remove it, add a note to remove it from cellslice.go)
		//TODO: figure out a way to only compute a generation if required on each branch (don't compute all the way to _MAX_IMPLICATIONS to start)

	}
}

type chainSearcherGenerationDetails struct {
	affectedCells cellSet
	filledNumbers map[cellRef]int
}

func (c chainSearcherGenerationDetails) String() string {
	result := "Begin map\n"
	for cell, num := range c.filledNumbers {
		result += "\t" + cell.String() + " : " + strconv.Itoa(num) + "\n"
	}
	result += "End map\n"
	return result
}

type chainSearcherAccumulator []*chainSearcherGenerationDetails

func (c chainSearcherAccumulator) String() string {
	result := "Accumulator[\n"
	for _, rec := range c {
		result += fmt.Sprintf("%s\n", rec)
	}
	result += "]\n"
	return result
}

//accumulateGenerations goes through each generation (youngest to newest)
//and squaches older generation maps into each generation, so each
//generation's map represents the totality of all cells seen at that point.
func (c chainSearcherAccumulator) accumulateGenerations() {
	for i := len(c) - 2; i >= 0; i-- {
		lastGeneration := c[i+1].filledNumbers
		currentGeneration := c[i].filledNumbers
		for key, val := range lastGeneration {
			currentGeneration[key] = val
		}
	}
}

func makeChainSeacherAccumulator(size int) chainSearcherAccumulator {
	result := make(chainSearcherAccumulator, size)
	for i := 0; i < size; i++ {
		result[i] = &chainSearcherGenerationDetails{
			affectedCells: make(cellSet),
			filledNumbers: make(map[cellRef]int),
		}
	}
	return result
}

func chainSearcher(i int, cell *Cell, numToApply int, accumulator chainSearcherAccumulator) {
	if i <= 0 || cell == nil {
		//Base case
		return
	}

	if i-1 >= len(accumulator) {
		panic("The accumulator provided was not big enough for the i provided.")
	}

	generationDetails := accumulator[i-1]

	//Find the nextCells that WILL have their numbers forced by the cell we're thinking of fillint.
	cellsToVisit := cell.Neighbors().FilterByPossible(numToApply).FilterByNumPossibilities(2)

	//Now that we know which cells will be affected and what their next number will be,
	//set the number in the given cell and then recurse downward down each branch.
	cell.SetNumber(numToApply)

	generationDetails.affectedCells[cell.ref()] = true
	generationDetails.filledNumbers[cell.ref()] = numToApply

	for _, cellToVisit := range cellsToVisit {

		possibilities := cellToVisit.Possibilities()

		if len(possibilities) != 1 {
			panic("Expected the cell to have one possibility")
		}

		forcedNum := possibilities[0]

		//Each branch modifies the grid, so create a new copy
		newGrid := cellToVisit.grid.Copy()
		cellToVisit = cellToVisit.InGrid(newGrid)

		//Recurse downward
		chainSearcher(i-1, cellToVisit, forcedNum, accumulator)

	}

}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"path/filepath"
	"runtime/debug"

	flag "github.com/spf13/pflag"

	"fmt"

	"github.com/prasannavl/go-gluons/http/diag"
	"github.com/prasannavl/go-gluons/http/httpservice"
	"github.com/prasannavl/go-gluons/http/redirector"

	"github.com/prasannavl/go-gluons/appx"
	"github.com/prasannavl/go-gluons/http/app-http/app"
	"github.com/prasannavl/go-gluons/log"
	"github.com/prasannavl/go-gluons/logconfig"
)

type EnvFlags struct {
	Addr           string
	LogFile        string
	LogDisabled    bool
	Verbosity      int
	DisplayVersion bool
	DiagAddr       string
	LogHumanize    bool
	Insecure       bool
	RedirectorAddr string
	UseSelfSigned  bool
	Hosts          []string
	WebRoot        string
	CertCacheDir   string
}

func initFlags(env *EnvFlags) {
	flag.BoolVar(&env.DisplayVersion, "version", false, "display the version and exit")
	flag.CountVarP(&env.Verbosity, "verbose", "v", "verbosity level")
	flag.StringVarP(&env.Addr, "address", "a", "localhost:8000", "the 'host:port' for the service to listen on")
	flag.StringVar(&env.DiagAddr, "dapi-address", "localhost:9090", "the 'host:port' for diagnostics api")
	flag.StringVar(&env.LogFile, "log", "", "the log file destination")
	flag.BoolVar(&env.LogDisabled, "no-log", false, "disable the logger")
	flag.BoolVarP(&env.LogHumanize, "log-humanize", "h", false, "humanize log messages")
	flag.BoolVar(&env.Insecure, "insecure", false, "disable tls")
	flag.BoolVar(&env.UseSelfSigned, "self-signed", false, "use randomly generated self signed certificate for tls")
	flag.StringVar(&env.RedirectorAddr, "redirector", "", "a redirector address as 'host:port' to enable")
	flag.StringArrayVar(&env.Hosts, "hosts", nil, "'host:port' items to enable hosts filter")
	flag.StringVar(&env.WebRoot, "root", "", "web root path")
	flag.StringVar(&env.CertCacheDir, "cert-dir", "", "the auto-tls certificate cache dir")

	flag.Usage = func() {
		printPackageHeader(false)
		fmt.Printf("Usage: [opts]\n\nOptions:\r\n")
		flag.PrintDefaults()
		fmt.Println()
	}
}

func initLogging(env *EnvFlags) logconfig.LogInitResult {
	logInitResult := logconfig.LogInitResult{}
	if !env.LogDisabled {
		logOpts := logconfig.DefaultOptions()
		if !env.LogHumanize {
			logOpts.Humanize = logconfig.Humanize.False
		}
		if env.LogFile != "" {
			logOpts.LogFile = env.LogFile
		}
		logOpts.VerbosityLevel = env.Verbosity
		logconfig.Init(&logOpts, &logInitResult)
	}
	return logInitResult
}

func printPackageHeader(versionOnly bool) {
	if versionOnly {
		fmt.Printf("%s", app.Version)
	} else {
		fmt.Printf("%s\t%s\r\n", app.Package, app.Version)
	}
}

func main() {
	appx.InitTerm()

	env := EnvFlags{}
	initFlags(&env)

	flag.Parse()

	defer func() {
		if err := recover(); err != nil {
			log.Errorv(err)
			debug.PrintStack()
		}
	}()

	if env.DisplayVersion {
		printPackageHeader(true)
		return
	}

	logInitResult := initLogging(&env)
	log.Infof("listen-address: %s", env.Addr)

	if env.DiagAddr != "" {
		s1 := diag.CreateWithConfigure(env.DiagAddr,
			diag.SetupIndexNotFound,
			diag.LogLevelSwitcher(nil))
		go s1.Run()
	}

	if env.RedirectorAddr != "" {
		s2 := redirector.Create(env.RedirectorAddr, env.Addr)
		go s2.Run()
	}

	opts := httpservice.HandlerServiceOpts{
		Addr:     env.Addr,
		Logger:   logInitResult.Logger,
		WebRoot:  filepath.Clean(env.WebRoot),
		Hosts:    env.Hosts,
		CacheDir: env.CertCacheDir,
	}

	if env.Insecure {
		opts.Insecure = true
	}

	service, err := app.CreateService(&opts)

	if err != nil {
		log.Errorf("failed to create service: %v", err)
		return
	}

	appx.CreateShutdownHandler(func() {
		_ = service.Stop(0)
	}, appx.ShutdownSignals...)

	err = service.Run()
	if err != http.ErrServerClosed {
		log.Errorf("service: %v", err)
	}

	log.Info("exit")
}
<commit_msg>fix: respect self signed arg<commit_after>package main

import (
	"net/http"
	"path/filepath"
	"runtime/debug"

	flag "github.com/spf13/pflag"

	"fmt"

	"github.com/prasannavl/go-gluons/http/diag"
	"github.com/prasannavl/go-gluons/http/httpservice"
	"github.com/prasannavl/go-gluons/http/redirector"

	"github.com/prasannavl/go-gluons/appx"
	"github.com/prasannavl/go-gluons/http/app-http/app"
	"github.com/prasannavl/go-gluons/log"
	"github.com/prasannavl/go-gluons/logconfig"
)

type EnvFlags struct {
	Addr           string
	LogFile        string
	LogDisabled    bool
	Verbosity      int
	DisplayVersion bool
	DiagAddr       string
	LogHumanize    bool
	Insecure       bool
	RedirectorAddr string
	UseSelfSigned  bool
	Hosts          []string
	WebRoot        string
	CertCacheDir   string
}

func initFlags(env *EnvFlags) {
	flag.BoolVar(&env.DisplayVersion, "version", false, "display the version and exit")
	flag.CountVarP(&env.Verbosity, "verbose", "v", "verbosity level")
	flag.StringVarP(&env.Addr, "address", "a", "localhost:8000", "the 'host:port' for the service to listen on")
	flag.StringVar(&env.DiagAddr, "dapi-address", "localhost:9090", "the 'host:port' for diagnostics api")
	flag.StringVar(&env.LogFile, "log", "", "the log file destination")
	flag.BoolVar(&env.LogDisabled, "no-log", false, "disable the logger")
	flag.BoolVarP(&env.LogHumanize, "log-humanize", "h", false, "humanize log messages")
	flag.BoolVar(&env.Insecure, "insecure", false, "disable tls")
	flag.BoolVar(&env.UseSelfSigned, "self-signed", false, "use randomly generated self signed certificate for tls")
	flag.StringVar(&env.RedirectorAddr, "redirector", "", "a redirector address as 'host:port' to enable")
	flag.StringArrayVar(&env.Hosts, "hosts", nil, "'host:port' items to enable hosts filter")
	flag.StringVar(&env.WebRoot, "root", "", "web root path")
	flag.StringVar(&env.CertCacheDir, "cert-dir", "", "the auto-tls certificate cache dir")

	flag.Usage = func() {
		printPackageHeader(false)
		fmt.Printf("Usage: [opts]\n\nOptions:\r\n")
		flag.PrintDefaults()
		fmt.Println()
	}
}

func initLogging(env *EnvFlags) logconfig.LogInitResult {
	logInitResult := logconfig.LogInitResult{}
	if !env.LogDisabled {
		logOpts := logconfig.DefaultOptions()
		if !env.LogHumanize {
			logOpts.Humanize = logconfig.Humanize.False
		}
		if env.LogFile != "" {
			logOpts.LogFile = env.LogFile
		}
		logOpts.VerbosityLevel = env.Verbosity
		logconfig.Init(&logOpts, &logInitResult)
	}
	return logInitResult
}

func printPackageHeader(versionOnly bool) {
	if versionOnly {
		fmt.Printf("%s", app.Version)
	} else {
		fmt.Printf("%s\t%s\r\n", app.Package, app.Version)
	}
}

func main() {
	appx.InitTerm()

	env := EnvFlags{}
	initFlags(&env)

	flag.Parse()

	defer func() {
		if err := recover(); err != nil {
			log.Errorv(err)
			debug.PrintStack()
		}
	}()

	if env.DisplayVersion {
		printPackageHeader(true)
		return
	}

	logInitResult := initLogging(&env)
	log.Infof("listen-address: %s", env.Addr)

	if env.DiagAddr != "" {
		s1 := diag.CreateWithConfigure(env.DiagAddr,
			diag.SetupIndexNotFound,
			diag.LogLevelSwitcher(nil))
		go s1.Run()
	}

	if env.RedirectorAddr != "" {
		s2 := redirector.Create(env.RedirectorAddr, env.Addr)
		go s2.Run()
	}

	opts := httpservice.HandlerServiceOpts{
		Addr:          env.Addr,
		Logger:        logInitResult.Logger,
		WebRoot:       filepath.Clean(env.WebRoot),
		Hosts:         env.Hosts,
		CacheDir:      env.CertCacheDir,
		UseSelfSigned: env.UseSelfSigned,
	}

	if env.Insecure {
		opts.Insecure = true
	}

	service, err := app.CreateService(&opts)

	if err != nil {
		log.Errorf("failed to create service: %v", err)
		return
	}

	appx.CreateShutdownHandler(func() {
		_ = service.Stop(0)
	}, appx.ShutdownSignals...)

	err = service.Run()
	if err != http.ErrServerClosed {
		log.Errorf("service: %v", err)
	}

	log.Info("exit")
}
<|endoftext|>


<commit_before>package packetio

import (
	"io"
	"net"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestBuffer(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()
	packet := make([]byte, 4)

	// Write once
	n, err := buffer.Write([]byte{0, 1})
	assert.NoError(err)
	assert.Equal(2, n)

	// Read once
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{0, 1}, packet[:n])

	// Read deadline
	err = buffer.SetReadDeadline(time.Unix(0, 1))
	assert.NoError(err)
	n, err = buffer.Read(packet)
	if e, ok := err.(net.Error); !ok || !e.Timeout() {
		t.Errorf("Unexpected error: %v", err)
	}
	assert.Equal(0, n)

	// Reset deadline
	err = buffer.SetReadDeadline(time.Time{})
	assert.NoError(err)

	// Write twice
	n, err = buffer.Write([]byte{2, 3, 4})
	assert.NoError(err)
	assert.Equal(3, n)

	n, err = buffer.Write([]byte{5, 6, 7})
	assert.NoError(err)
	assert.Equal(3, n)

	// Read twice
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(3, n)
	assert.Equal([]byte{2, 3, 4}, packet[:n])

	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(3, n)
	assert.Equal([]byte{5, 6, 7}, packet[:n])

	// Write once prior to close.
	_, err = buffer.Write([]byte{3})
	assert.NoError(err)

	// Close
	err = buffer.Close()
	assert.NoError(err)

	// Future writes will error
	_, err = buffer.Write([]byte{4})
	assert.Error(err)

	// But we can read the remaining data.
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(1, n)
	assert.Equal([]byte{3}, packet[:n])

	// Until EOF
	_, err = buffer.Read(packet)
	assert.Equal(io.EOF, err)
}

func testWraparound(t *testing.T, grow bool) {
	assert := assert.New(t)

	buffer := NewBuffer()
	err := buffer.grow()
	assert.NoError(err)

	buffer.head = len(buffer.data) - 13
	buffer.tail = buffer.head

	p1 := []byte{1, 2, 3}
	p2 := []byte{4, 5, 6}
	p3 := []byte{7, 8, 9}
	p4 := []byte{10, 11, 12}

	_, err = buffer.Write(p1)
	assert.NoError(err)
	_, err = buffer.Write(p2)
	assert.NoError(err)
	_, err = buffer.Write(p3)
	assert.NoError(err)

	p := make([]byte, 10)

	n, err := buffer.Read(p)
	assert.NoError(err)
	assert.Equal(p1, p[:n])

	if grow {
		err = buffer.grow()
		assert.NoError(err)
	}

	n, err = buffer.Read(p)
	assert.NoError(err)
	assert.Equal(p2, p[:n])

	_, err = buffer.Write(p4)
	assert.NoError(err)

	n, err = buffer.Read(p)
	assert.NoError(err)
	assert.Equal(p3, p[:n])
	n, err = buffer.Read(p)
	assert.NoError(err)
	assert.Equal(p4, p[:n])

	if !grow {
		assert.Equal(len(buffer.data), minSize)
	} else {
		assert.Equal(len(buffer.data), 2*minSize)
	}
}

func TestBufferWraparound(t *testing.T) {
	testWraparound(t, false)
}

func TestBufferWraparoundGrow(t *testing.T) {
	testWraparound(t, true)
}

func TestBufferAsync(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()

	// Start up a goroutine to start a blocking read.
	done := make(chan struct{})
	go func() {
		packet := make([]byte, 4)

		n, err := buffer.Read(packet)
		assert.NoError(err)
		assert.Equal(2, n)
		assert.Equal([]byte{0, 1}, packet[:n])

		_, err = buffer.Read(packet)
		assert.Equal(io.EOF, err)

		close(done)
	}()

	// Wait for the reader to start reading.
	time.Sleep(time.Millisecond)

	// Write once
	n, err := buffer.Write([]byte{0, 1})
	assert.NoError(err)
	assert.Equal(2, n)

	// Wait for the reader to start reading again.
	time.Sleep(time.Millisecond)

	// Close will unblock the reader.
	err = buffer.Close()
	assert.NoError(err)

	<-done
}

func TestBufferLimitCount(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()
	buffer.SetLimitCount(2)

	assert.Equal(0, buffer.Count())

	// Write twice
	n, err := buffer.Write([]byte{0, 1})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(1, buffer.Count())

	n, err = buffer.Write([]byte{2, 3})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(2, buffer.Count())

	// Over capacity
	_, err = buffer.Write([]byte{4, 5})
	assert.Equal(ErrFull, err)
	assert.Equal(2, buffer.Count())

	// Read once
	packet := make([]byte, 4)
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{0, 1}, packet[:n])
	assert.Equal(1, buffer.Count())

	// Write once
	n, err = buffer.Write([]byte{6, 7})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(2, buffer.Count())

	// Over capacity
	_, err = buffer.Write([]byte{8, 9})
	assert.Equal(ErrFull, err)
	assert.Equal(2, buffer.Count())

	// Read twice
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{2, 3}, packet[:n])
	assert.Equal(1, buffer.Count())

	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{6, 7}, packet[:n])
	assert.Equal(0, buffer.Count())

	// Nothing left.
	err = buffer.Close()
	assert.NoError(err)
}

func TestBufferLimitSize(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()
	buffer.SetLimitSize(11)

	assert.Equal(0, buffer.Size())

	// Write twice
	n, err := buffer.Write([]byte{0, 1})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(4, buffer.Size())

	n, err = buffer.Write([]byte{2, 3})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(8, buffer.Size())

	// Over capacity
	_, err = buffer.Write([]byte{4, 5})
	assert.Equal(ErrFull, err)
	assert.Equal(8, buffer.Size())

	// Cheeky write at exact size.
	n, err = buffer.Write([]byte{6})
	assert.NoError(err)
	assert.Equal(1, n)
	assert.Equal(11, buffer.Size())

	// Read once
	packet := make([]byte, 4)
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{0, 1}, packet[:n])
	assert.Equal(7, buffer.Size())

	// Write once
	n, err = buffer.Write([]byte{7, 8})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(11, buffer.Size())

	// Over capacity
	_, err = buffer.Write([]byte{9, 10})
	assert.Equal(ErrFull, err)
	assert.Equal(11, buffer.Size())

	// Read everything
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{2, 3}, packet[:n])
	assert.Equal(7, buffer.Size())

	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(1, n)
	assert.Equal([]byte{6}, packet[:n])
	assert.Equal(4, buffer.Size())

	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{7, 8}, packet[:n])
	assert.Equal(0, buffer.Size())

	// Nothing left.
	err = buffer.Close()
	assert.NoError(err)
}

func TestBufferMisc(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()

	// Write once
	n, err := buffer.Write([]byte{0, 1, 2, 3})
	assert.NoError(err)
	assert.Equal(4, n)

	// Try to read with a short buffer
	packet := make([]byte, 3)
	_, err = buffer.Read(packet)
	assert.Equal(io.ErrShortBuffer, err)

	// Close
	err = buffer.Close()
	assert.NoError(err)

	// Make sure you can Close twice
	err = buffer.Close()
	assert.NoError(err)
}

func benchmarkBuffer(b *testing.B, size int64) {
	buffer := NewBuffer()
	b.SetBytes(size)

	done := make(chan struct{})
	go func() {
		packet := make([]byte, size)

		for {
			_, err := buffer.Read(packet)
			if err == io.EOF {
				break
			} else if err != nil {
				b.Error(err)
				break
			}
		}

		close(done)
	}()

	packet := make([]byte, size)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		var err error
		for {
			_, err = buffer.Write(packet)
			if err != ErrFull {
				break
			}
			time.Sleep(time.Microsecond)
		}
		if err != nil {
			b.Fatal(err)
		}
	}

	err := buffer.Close()
	if err != nil {
		b.Fatal(err)
	}

	<-done
}

func BenchmarkBuffer14(b *testing.B) {
	benchmarkBuffer(b, 14)
}

func BenchmarkBuffer140(b *testing.B) {
	benchmarkBuffer(b, 140)
}

func BenchmarkBuffer1400(b *testing.B) {
	benchmarkBuffer(b, 1400)
}
<commit_msg>Add test for buffer allocation counts<commit_after>package packetio

import (
	"io"
	"net"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestBuffer(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()
	packet := make([]byte, 4)

	// Write once
	n, err := buffer.Write([]byte{0, 1})
	assert.NoError(err)
	assert.Equal(2, n)

	// Read once
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{0, 1}, packet[:n])

	// Read deadline
	err = buffer.SetReadDeadline(time.Unix(0, 1))
	assert.NoError(err)
	n, err = buffer.Read(packet)
	if e, ok := err.(net.Error); !ok || !e.Timeout() {
		t.Errorf("Unexpected error: %v", err)
	}
	assert.Equal(0, n)

	// Reset deadline
	err = buffer.SetReadDeadline(time.Time{})
	assert.NoError(err)

	// Write twice
	n, err = buffer.Write([]byte{2, 3, 4})
	assert.NoError(err)
	assert.Equal(3, n)

	n, err = buffer.Write([]byte{5, 6, 7})
	assert.NoError(err)
	assert.Equal(3, n)

	// Read twice
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(3, n)
	assert.Equal([]byte{2, 3, 4}, packet[:n])

	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(3, n)
	assert.Equal([]byte{5, 6, 7}, packet[:n])

	// Write once prior to close.
	_, err = buffer.Write([]byte{3})
	assert.NoError(err)

	// Close
	err = buffer.Close()
	assert.NoError(err)

	// Future writes will error
	_, err = buffer.Write([]byte{4})
	assert.Error(err)

	// But we can read the remaining data.
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(1, n)
	assert.Equal([]byte{3}, packet[:n])

	// Until EOF
	_, err = buffer.Read(packet)
	assert.Equal(io.EOF, err)
}

func testWraparound(t *testing.T, grow bool) {
	assert := assert.New(t)

	buffer := NewBuffer()
	err := buffer.grow()
	assert.NoError(err)

	buffer.head = len(buffer.data) - 13
	buffer.tail = buffer.head

	p1 := []byte{1, 2, 3}
	p2 := []byte{4, 5, 6}
	p3 := []byte{7, 8, 9}
	p4 := []byte{10, 11, 12}

	_, err = buffer.Write(p1)
	assert.NoError(err)
	_, err = buffer.Write(p2)
	assert.NoError(err)
	_, err = buffer.Write(p3)
	assert.NoError(err)

	p := make([]byte, 10)

	n, err := buffer.Read(p)
	assert.NoError(err)
	assert.Equal(p1, p[:n])

	if grow {
		err = buffer.grow()
		assert.NoError(err)
	}

	n, err = buffer.Read(p)
	assert.NoError(err)
	assert.Equal(p2, p[:n])

	_, err = buffer.Write(p4)
	assert.NoError(err)

	n, err = buffer.Read(p)
	assert.NoError(err)
	assert.Equal(p3, p[:n])
	n, err = buffer.Read(p)
	assert.NoError(err)
	assert.Equal(p4, p[:n])

	if !grow {
		assert.Equal(len(buffer.data), minSize)
	} else {
		assert.Equal(len(buffer.data), 2*minSize)
	}
}

func TestBufferWraparound(t *testing.T) {
	testWraparound(t, false)
}

func TestBufferWraparoundGrow(t *testing.T) {
	testWraparound(t, true)
}

func TestBufferAsync(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()

	// Start up a goroutine to start a blocking read.
	done := make(chan struct{})
	go func() {
		packet := make([]byte, 4)

		n, err := buffer.Read(packet)
		assert.NoError(err)
		assert.Equal(2, n)
		assert.Equal([]byte{0, 1}, packet[:n])

		_, err = buffer.Read(packet)
		assert.Equal(io.EOF, err)

		close(done)
	}()

	// Wait for the reader to start reading.
	time.Sleep(time.Millisecond)

	// Write once
	n, err := buffer.Write([]byte{0, 1})
	assert.NoError(err)
	assert.Equal(2, n)

	// Wait for the reader to start reading again.
	time.Sleep(time.Millisecond)

	// Close will unblock the reader.
	err = buffer.Close()
	assert.NoError(err)

	<-done
}

func TestBufferLimitCount(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()
	buffer.SetLimitCount(2)

	assert.Equal(0, buffer.Count())

	// Write twice
	n, err := buffer.Write([]byte{0, 1})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(1, buffer.Count())

	n, err = buffer.Write([]byte{2, 3})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(2, buffer.Count())

	// Over capacity
	_, err = buffer.Write([]byte{4, 5})
	assert.Equal(ErrFull, err)
	assert.Equal(2, buffer.Count())

	// Read once
	packet := make([]byte, 4)
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{0, 1}, packet[:n])
	assert.Equal(1, buffer.Count())

	// Write once
	n, err = buffer.Write([]byte{6, 7})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(2, buffer.Count())

	// Over capacity
	_, err = buffer.Write([]byte{8, 9})
	assert.Equal(ErrFull, err)
	assert.Equal(2, buffer.Count())

	// Read twice
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{2, 3}, packet[:n])
	assert.Equal(1, buffer.Count())

	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{6, 7}, packet[:n])
	assert.Equal(0, buffer.Count())

	// Nothing left.
	err = buffer.Close()
	assert.NoError(err)
}

func TestBufferLimitSize(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()
	buffer.SetLimitSize(11)

	assert.Equal(0, buffer.Size())

	// Write twice
	n, err := buffer.Write([]byte{0, 1})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(4, buffer.Size())

	n, err = buffer.Write([]byte{2, 3})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(8, buffer.Size())

	// Over capacity
	_, err = buffer.Write([]byte{4, 5})
	assert.Equal(ErrFull, err)
	assert.Equal(8, buffer.Size())

	// Cheeky write at exact size.
	n, err = buffer.Write([]byte{6})
	assert.NoError(err)
	assert.Equal(1, n)
	assert.Equal(11, buffer.Size())

	// Read once
	packet := make([]byte, 4)
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{0, 1}, packet[:n])
	assert.Equal(7, buffer.Size())

	// Write once
	n, err = buffer.Write([]byte{7, 8})
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal(11, buffer.Size())

	// Over capacity
	_, err = buffer.Write([]byte{9, 10})
	assert.Equal(ErrFull, err)
	assert.Equal(11, buffer.Size())

	// Read everything
	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{2, 3}, packet[:n])
	assert.Equal(7, buffer.Size())

	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(1, n)
	assert.Equal([]byte{6}, packet[:n])
	assert.Equal(4, buffer.Size())

	n, err = buffer.Read(packet)
	assert.NoError(err)
	assert.Equal(2, n)
	assert.Equal([]byte{7, 8}, packet[:n])
	assert.Equal(0, buffer.Size())

	// Nothing left.
	err = buffer.Close()
	assert.NoError(err)
}

func TestBufferMisc(t *testing.T) {
	assert := assert.New(t)

	buffer := NewBuffer()

	// Write once
	n, err := buffer.Write([]byte{0, 1, 2, 3})
	assert.NoError(err)
	assert.Equal(4, n)

	// Try to read with a short buffer
	packet := make([]byte, 3)
	_, err = buffer.Read(packet)
	assert.Equal(io.ErrShortBuffer, err)

	// Close
	err = buffer.Close()
	assert.NoError(err)

	// Make sure you can Close twice
	err = buffer.Close()
	assert.NoError(err)
}

func TestBufferAlloc(t *testing.T) {
	packet := make([]byte, 1024)

	test := func(f func(count int) func(), count int, max float64) func(t *testing.T) {
		return func(t *testing.T) {
			allocs := testing.AllocsPerRun(3, f(count))
			if allocs > max {
				t.Errorf("count=%v, max=%v, got %v",
					count, max, allocs,
				)
			}
		}
	}

	w := func(count int) func() {
		return func() {
			buffer := NewBuffer()
			for i := 0; i < count; i++ {
				_, err := buffer.Write(packet)
				if err != nil {
					t.Errorf("Write: %v", err)
					break
				}
			}
		}
	}

	t.Run("100 writes", test(w, 100, 13))
	t.Run("200 writes", test(w, 200, 17))
	t.Run("400 writes", test(w, 400, 19))
	t.Run("1000 writes", test(w, 1000, 23))

	wr := func(count int) func() {
		return func() {
			buffer := NewBuffer()
			for i := 0; i < count; i++ {
				_, err := buffer.Write(packet)
				if err != nil {
					t.Fatalf("Write: %v", err)
				}
				_, err = buffer.Read(packet)
				if err != nil {
					t.Fatalf("Read: %v", err)
				}
			}
		}
	}

	t.Run("100 writes and reads", test(wr, 100, 7))
	t.Run("1000 writes and reads", test(wr, 1000, 7))
	t.Run("10000 writes and reads", test(wr, 10000, 7))
}

func benchmarkBuffer(b *testing.B, size int64) {
	buffer := NewBuffer()
	b.SetBytes(size)

	done := make(chan struct{})
	go func() {
		packet := make([]byte, size)

		for {
			_, err := buffer.Read(packet)
			if err == io.EOF {
				break
			} else if err != nil {
				b.Error(err)
				break
			}
		}

		close(done)
	}()

	packet := make([]byte, size)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		var err error
		for {
			_, err = buffer.Write(packet)
			if err != ErrFull {
				break
			}
			time.Sleep(time.Microsecond)
		}
		if err != nil {
			b.Fatal(err)
		}
	}

	err := buffer.Close()
	if err != nil {
		b.Fatal(err)
	}

	<-done
}

func BenchmarkBuffer14(b *testing.B) {
	benchmarkBuffer(b, 14)
}

func BenchmarkBuffer140(b *testing.B) {
	benchmarkBuffer(b, 140)
}

func BenchmarkBuffer1400(b *testing.B) {
	benchmarkBuffer(b, 1400)
}
<|endoftext|>


<commit_before>// Copyright 2016-2019 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bpf

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"regexp"
	"time"

	"github.com/cilium/cilium/pkg/controller"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sirupsen/logrus"
)

// MapType is an enumeration for valid BPF map types
type MapType int

// This enumeration must be in sync with enum bpf_prog_type in <linux/bpf.h>
const (
	MapTypeUnspec MapType = iota
	MapTypeHash
	MapTypeArray
	MapTypeProgArray
	MapTypePerfEventArray
	MapTypePerCPUHash
	MapTypePerCPUArray
	MapTypeStackTrace
	MapTypeCgroupArray
	MapTypeLRUHash
	MapTypeLRUPerCPUHash
	MapTypeLPMTrie
	MapTypeArrayOfMaps
	MapTypeHashOfMaps
	MapTypeDevMap
	MapTypeSockMap
	MapTypeCPUMap
	MapTypeXSKMap
	MapTypeSockHash
	// MapTypeMaximum is the maximum supported known map type.
	MapTypeMaximum

	// maxSyncErrors is the maximum consecutive errors syncing before the
	// controller bails out
	maxSyncErrors = 512

	// errorResolverSchedulerMinInterval is the minimum interval for the
	// error resolver to be scheduled. This minimum interval ensures not to
	// overschedule if a large number of updates fail in a row.
	errorResolverSchedulerMinInterval = 5 * time.Second

	// errorResolverSchedulerDelay is the delay to update the controller
	// after determination that a run is needed. The delay allows to
	// schedule the resolver after series of updates have failed.
	errorResolverSchedulerDelay = 200 * time.Millisecond
)

var (
	mapControllers = controller.NewManager()

	// supportedMapTypes maps from a MapType to a bool indicating whether
	// the currently running kernel supports the map type.
	supportedMapTypes = make(map[MapType]bool)
)

func (t MapType) String() string {
	switch t {
	case MapTypeHash:
		return "Hash"
	case MapTypeArray:
		return "Array"
	case MapTypeProgArray:
		return "Program array"
	case MapTypePerfEventArray:
		return "Event array"
	case MapTypePerCPUHash:
		return "Per-CPU hash"
	case MapTypePerCPUArray:
		return "Per-CPU array"
	case MapTypeStackTrace:
		return "Stack trace"
	case MapTypeCgroupArray:
		return "Cgroup array"
	case MapTypeLRUHash:
		return "LRU hash"
	case MapTypeLRUPerCPUHash:
		return "LRU per-CPU hash"
	case MapTypeLPMTrie:
		return "Longest prefix match trie"
	case MapTypeArrayOfMaps:
		return "Array of maps"
	case MapTypeHashOfMaps:
		return "Hash of maps"
	case MapTypeDevMap:
		return "Device Map"
	case MapTypeSockMap:
		return "Socket Map"
	case MapTypeCPUMap:
		return "CPU Redirect Map"
	case MapTypeSockHash:
		return "Socket Hash"
	}

	return "Unknown"
}

func (t MapType) allowsPreallocation() bool {
	if t == MapTypeLPMTrie {
		return false
	}
	return true
}

func (t MapType) requiresPreallocation() bool {
	switch t {
	case MapTypeHash, MapTypePerCPUHash, MapTypeLPMTrie, MapTypeHashOfMaps:
		return false
	}
	return true
}

// DesiredAction is the action to be performed on the BPF map
type DesiredAction int

const (
	// OK indicates that to further action is required and the entry is in
	// sync
	OK DesiredAction = iota

	// Insert indicates that the entry needs to be created or updated
	Insert

	// Delete indicates that the entry needs to be deleted
	Delete
)

func (d DesiredAction) String() string {
	switch d {
	case OK:
		return "sync"
	case Insert:
		return "to-be-inserted"
	case Delete:
		return "to-be-deleted"
	default:
		return "unknown"
	}
}

// mapTypeToFeatureString maps a MapType into a string defined by run_probes.sh
func mapTypeToFeatureString(mt MapType) string {
	var featureString string
	switch mt {
	case MapTypeLPMTrie:
		featureString = fmt.Sprintf("#define HAVE_LPM_MAP_TYPE")
	case MapTypeLRUHash:
		featureString = fmt.Sprintf("#define HAVE_LRU_MAP_TYPE")
	default:
		break
	}
	return featureString
}

// ReadFeatureProbes reads the bpf_features.h file at the specified path (as
// generated by bpf/run_probes.sh), and stores the results of the kernel
// feature probing.
func ReadFeatureProbes(filename string) {
	f, err := os.Open(filename)
	if err != nil {
		// Should not happen; the caller ensured that the file exists
		log.WithFields(logrus.Fields{
			logfields.Path: filename,
		}).WithError(err).Fatal("Failed to read feature probes")
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		for mapType := MapTypeHash; mapType < MapTypeMaximum; mapType++ {
			featureString := mapTypeToFeatureString(mapType)
			if featureString != "" &&
				bytes.Compare(scanner.Bytes(), []byte(featureString)) == 0 {
				log.Debugf("Detected support for map type %s", mapType.String())
				supportedMapTypes[mapType] = true
			}
		}
	}

	for mapType := MapTypeHash; mapType < MapTypeMaximum; mapType++ {
		if mapTypeToFeatureString(mapType) == "" {
			log.Debugf("Skipping support detection for map type %s", mapType.String())
		} else if _, probed := supportedMapTypes[mapType]; !probed {
			log.Debugf("Detected no support for map type %s", mapType.String())
			supportedMapTypes[mapType] = false
		}
	}
}

// GetMapType determines whether the specified map type is supported by the
// kernel (as determined by ReadFeatureProbes()), and if the map type is not
// supported, returns a more primitive map type that may be used to implement
// the map on older implementations. Otherwise, returns the specified map type.
func GetMapType(t MapType) MapType {
	switch t {
	case MapTypeLPMTrie:
		fallthrough
	case MapTypeLRUHash:
		if !supportedMapTypes[t] {
			return MapTypeHash
		}
	}
	return t
}

var commonNameRegexps = []*regexp.Regexp{
	regexp.MustCompile(`^(cilium_)(.+)_reserved_[0-9]+$`),
	regexp.MustCompile(`^(cilium_)(.+)_netdev_ns_[0-9]+$`),
	regexp.MustCompile(`^(cilium_)(.+)_overlay_[0-9]+$`),
	regexp.MustCompile(`^(cilium_)(.+)_[0-9]+$`),
	regexp.MustCompile(`^(cilium_)(.+)+$`),
}

func extractCommonName(name string) string {
	for _, r := range commonNameRegexps {
		if replaced := r.ReplaceAllString(name, `$2`); replaced != name {
			return replaced
		}
	}

	return name
}
<commit_msg>fix staticchecker warnings for bpf<commit_after>// Copyright 2016-2019 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bpf

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"regexp"
	"time"

	"github.com/cilium/cilium/pkg/controller"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sirupsen/logrus"
)

// MapType is an enumeration for valid BPF map types
type MapType int

// This enumeration must be in sync with enum bpf_prog_type in <linux/bpf.h>
const (
	MapTypeUnspec MapType = iota
	MapTypeHash
	MapTypeArray
	MapTypeProgArray
	MapTypePerfEventArray
	MapTypePerCPUHash
	MapTypePerCPUArray
	MapTypeStackTrace
	MapTypeCgroupArray
	MapTypeLRUHash
	MapTypeLRUPerCPUHash
	MapTypeLPMTrie
	MapTypeArrayOfMaps
	MapTypeHashOfMaps
	MapTypeDevMap
	MapTypeSockMap
	MapTypeCPUMap
	MapTypeXSKMap
	MapTypeSockHash
	// MapTypeMaximum is the maximum supported known map type.
	MapTypeMaximum

	// maxSyncErrors is the maximum consecutive errors syncing before the
	// controller bails out
	maxSyncErrors = 512

	// errorResolverSchedulerMinInterval is the minimum interval for the
	// error resolver to be scheduled. This minimum interval ensures not to
	// overschedule if a large number of updates fail in a row.
	errorResolverSchedulerMinInterval = 5 * time.Second

	// errorResolverSchedulerDelay is the delay to update the controller
	// after determination that a run is needed. The delay allows to
	// schedule the resolver after series of updates have failed.
	errorResolverSchedulerDelay = 200 * time.Millisecond
)

var (
	mapControllers = controller.NewManager()

	// supportedMapTypes maps from a MapType to a bool indicating whether
	// the currently running kernel supports the map type.
	supportedMapTypes = make(map[MapType]bool)
)

func (t MapType) String() string {
	switch t {
	case MapTypeHash:
		return "Hash"
	case MapTypeArray:
		return "Array"
	case MapTypeProgArray:
		return "Program array"
	case MapTypePerfEventArray:
		return "Event array"
	case MapTypePerCPUHash:
		return "Per-CPU hash"
	case MapTypePerCPUArray:
		return "Per-CPU array"
	case MapTypeStackTrace:
		return "Stack trace"
	case MapTypeCgroupArray:
		return "Cgroup array"
	case MapTypeLRUHash:
		return "LRU hash"
	case MapTypeLRUPerCPUHash:
		return "LRU per-CPU hash"
	case MapTypeLPMTrie:
		return "Longest prefix match trie"
	case MapTypeArrayOfMaps:
		return "Array of maps"
	case MapTypeHashOfMaps:
		return "Hash of maps"
	case MapTypeDevMap:
		return "Device Map"
	case MapTypeSockMap:
		return "Socket Map"
	case MapTypeCPUMap:
		return "CPU Redirect Map"
	case MapTypeSockHash:
		return "Socket Hash"
	}

	return "Unknown"
}

func (t MapType) allowsPreallocation() bool {
	return t != MapTypeLPMTrie
}

func (t MapType) requiresPreallocation() bool {
	switch t {
	case MapTypeHash, MapTypePerCPUHash, MapTypeLPMTrie, MapTypeHashOfMaps:
		return false
	}
	return true
}

// DesiredAction is the action to be performed on the BPF map
type DesiredAction int

const (
	// OK indicates that to further action is required and the entry is in
	// sync
	OK DesiredAction = iota

	// Insert indicates that the entry needs to be created or updated
	Insert

	// Delete indicates that the entry needs to be deleted
	Delete
)

func (d DesiredAction) String() string {
	switch d {
	case OK:
		return "sync"
	case Insert:
		return "to-be-inserted"
	case Delete:
		return "to-be-deleted"
	default:
		return "unknown"
	}
}

// mapTypeToFeatureString maps a MapType into a string defined by run_probes.sh
func mapTypeToFeatureString(mt MapType) string {
	var featureString string
	switch mt {
	case MapTypeLPMTrie:
		featureString = fmt.Sprintf("#define HAVE_LPM_MAP_TYPE")
	case MapTypeLRUHash:
		featureString = fmt.Sprintf("#define HAVE_LRU_MAP_TYPE")
	default:
		break
	}
	return featureString
}

// ReadFeatureProbes reads the bpf_features.h file at the specified path (as
// generated by bpf/run_probes.sh), and stores the results of the kernel
// feature probing.
func ReadFeatureProbes(filename string) {
	f, err := os.Open(filename)
	if err != nil {
		// Should not happen; the caller ensured that the file exists
		log.WithFields(logrus.Fields{
			logfields.Path: filename,
		}).WithError(err).Fatal("Failed to read feature probes")
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		for mapType := MapTypeHash; mapType < MapTypeMaximum; mapType++ {
			featureString := mapTypeToFeatureString(mapType)
			if featureString != "" &&
				bytes.Equal(scanner.Bytes(), []byte(featureString)) {
				log.Debugf("Detected support for map type %s", mapType.String())
				supportedMapTypes[mapType] = true
			}
		}
	}

	for mapType := MapTypeHash; mapType < MapTypeMaximum; mapType++ {
		if mapTypeToFeatureString(mapType) == "" {
			log.Debugf("Skipping support detection for map type %s", mapType.String())
		} else if _, probed := supportedMapTypes[mapType]; !probed {
			log.Debugf("Detected no support for map type %s", mapType.String())
			supportedMapTypes[mapType] = false
		}
	}
}

// GetMapType determines whether the specified map type is supported by the
// kernel (as determined by ReadFeatureProbes()), and if the map type is not
// supported, returns a more primitive map type that may be used to implement
// the map on older implementations. Otherwise, returns the specified map type.
func GetMapType(t MapType) MapType {
	switch t {
	case MapTypeLPMTrie:
		fallthrough
	case MapTypeLRUHash:
		if !supportedMapTypes[t] {
			return MapTypeHash
		}
	}
	return t
}

var commonNameRegexps = []*regexp.Regexp{
	regexp.MustCompile(`^(cilium_)(.+)_reserved_[0-9]+$`),
	regexp.MustCompile(`^(cilium_)(.+)_netdev_ns_[0-9]+$`),
	regexp.MustCompile(`^(cilium_)(.+)_overlay_[0-9]+$`),
	regexp.MustCompile(`^(cilium_)(.+)_[0-9]+$`),
	regexp.MustCompile(`^(cilium_)(.+)+$`),
}

func extractCommonName(name string) string {
	for _, r := range commonNameRegexps {
		if replaced := r.ReplaceAllString(name, `$2`); replaced != name {
			return replaced
		}
	}

	return name
}
<|endoftext|>