<commit_before>// Package ripe provides ASN and IP information
package ripe

import (
	"encoding/json"
	"fmt"
	"github.com/olekukonko/tablewriter"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strings"
	"sync"

	"github.com/mehrdadrad/mylg/data"
)

const (
	// Ripe API URL
	RIPEAPI = "https://stat.ripe.net"
	// Ripe prefix path
	RIPEPrefixURL = "/data/prefix-overview/data.json?max_related=50&resource="
	// Ripe ASN path
	RIPEASNURL = "/data/as-overview/data.json?resource=AS"
	// Ripe Geo path
	RIPEGeoURL = "/data/geoloc/data.json?resource=AS"
)

// ASN represents ASN information
type ASN struct {
	Number  string
	Data    map[string]interface{}
	GeoData map[string]interface{}
}

// Prefix represents prefix information
type Prefix struct {
	Resource string
	Data     map[string]interface{}
}

// location represents location information
type location struct {
	City    string `json:"city"`
	Country string `json:"country"`
}

// Set sets the resource value
func (p *Prefix) Set(r string) {
	p.Resource = r
}

// GetData gets prefix information from RIPE NCC
func (p *Prefix) GetData() bool {
	if len(p.Resource) < 6 {
		println("error: prefix invalid")
		return false
	}
	resp, err := http.Get(RIPEAPI + RIPEPrefixURL + p.Resource)
	if err != nil {
		println(err.Error())
		return false
	}
	if resp.StatusCode != 200 {
		println("error: check your prefix")
		return false
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal(body, &p.Data)
	return true
}

// PrettyPrint print ASN information (holder)
func (p *Prefix) PrettyPrint() {
	data, ok := p.Data["data"].(map[string]interface{})
	if ok {
		println("prefix:", data["resource"].(string))
		asns := data["asns"].([]interface{})
		for _, h := range asns {
			println("holder:", h.(map[string]interface{})["holder"].(string))
		}
	}
}

// Set ASN
func (a *ASN) Set(r string) {
	a.Number = r
}

// GetData gets ASN information from RIPE NCC
func (a *ASN) GetData() bool {
	var (
		wg        sync.WaitGroup
		rOV, rGeo bool
	)

	wg.Add(1)
	go func() {
		defer wg.Done()
		rOV = a.GetOVData()
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		rGeo = a.GetGeoData()
	}()
	wg.Wait()
	return rOV || rGeo
}

// GetOVData gets ASN overview information from RIPE NCC
func (a *ASN) GetOVData() bool {
	if len(a.Number) < 2 {
		println("error: AS number invalid")
		return false
	}
	resp, err := http.Get(RIPEAPI + RIPEASNURL + a.Number)
	if err != nil {
		println(err.Error())
		return false
	}
	if resp.StatusCode != 200 {
		return false
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal(body, &a.Data)
	return true
}

// GetGeoData gets Geo information from RIPE NCC
func (a *ASN) GetGeoData() bool {
	if len(a.Number) < 2 {
		println("error: AS number invalid")
		return false
	}
	resp, err := http.Get(RIPEAPI + RIPEGeoURL + a.Number)
	if err != nil {
		println(err.Error())
		return false
	}
	if resp.StatusCode != 200 {
		println("error: check your AS number")
		return false
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal(body, &a.GeoData)
	return true
}

// PrettyPrint print ASN information (holder)
func (a *ASN) PrettyPrint() {
	var cols = make(map[string]float64)
	overviewData, ok := a.Data["data"].(map[string]interface{})
	if ok {
		println(string(overviewData["holder"].(string)))
	}
	geoLocData, ok := a.GeoData["data"].(map[string]interface{})
	if !ok {
		return
	}
	locs := geoLocData["locations"].([]interface{})
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Location", "Covered %"})
	for _, loc := range locs {
		geoInfo := loc.(map[string]interface{})
		cols[geoInfo["country"].(string)] = geoInfo["covered_percentage"].(float64)
	}
	for name, percent := range cols {
		uc := strings.Split(name, "-")
		if country, ok := data.Country[uc[0]]; ok {
			name = country
		}
		if len(uc) == 2 {
			name = fmt.Sprintf("%s - %s", name, uc[1])
		}
		table.Append([]string{name, fmt.Sprintf("%.2f", percent)})
	}
	table.Render()
}

// IsASN checks if the key is a number
func IsASN(key string) bool {
	m, err := regexp.MatchString(`^\d+$`, key)
	if err != nil {
		return false
	}
	return m
}
<commit_msg>added sort func to peering coverage information<commit_after>// Package ripe provides ASN and IP information
package ripe

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"

	"github.com/mehrdadrad/mylg/data"
	"github.com/olekukonko/tablewriter"
)

const (
	// Ripe API URL
	RIPEAPI = "https://stat.ripe.net"
	// Ripe prefix path
	RIPEPrefixURL = "/data/prefix-overview/data.json?max_related=50&resource="
	// Ripe ASN path
	RIPEASNURL = "/data/as-overview/data.json?resource=AS"
	// Ripe Geo path
	RIPEGeoURL = "/data/geoloc/data.json?resource=AS"
)

// ASN represents ASN information
type ASN struct {
	Number  string
	Data    map[string]interface{}
	GeoData map[string]interface{}
}

// Prefix represents prefix information
type Prefix struct {
	Resource string
	Data     map[string]interface{}
}

// kv represents key/value(float64) in sort func
type kv struct {
	key   string
	value float64
}

// location represents location information
type location struct {
	City    string `json:"city"`
	Country string `json:"country"`
}

// Set sets the resource value
func (p *Prefix) Set(r string) {
	p.Resource = r
}

// GetData gets prefix information from RIPE NCC
func (p *Prefix) GetData() bool {
	if len(p.Resource) < 6 {
		println("error: prefix invalid")
		return false
	}
	resp, err := http.Get(RIPEAPI + RIPEPrefixURL + p.Resource)
	if err != nil {
		println(err.Error())
		return false
	}
	if resp.StatusCode != 200 {
		println("error: check your prefix")
		return false
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal(body, &p.Data)
	return true
}

// PrettyPrint print ASN information (holder)
func (p *Prefix) PrettyPrint() {
	data, ok := p.Data["data"].(map[string]interface{})
	if ok {
		println("prefix:", data["resource"].(string))
		asns := data["asns"].([]interface{})
		for _, h := range asns {
			println("holder:", h.(map[string]interface{})["holder"].(string))
		}
	}
}

// Set ASN
func (a *ASN) Set(r string) {
	a.Number = r
}

// GetData gets ASN information from RIPE NCC
func (a *ASN) GetData() bool {
	var (
		wg        sync.WaitGroup
		rOV, rGeo bool
	)

	wg.Add(1)
	go func() {
		defer wg.Done()
		rOV = a.GetOVData()
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		rGeo = a.GetGeoData()
	}()
	wg.Wait()
	return rOV || rGeo
}

// GetOVData gets ASN overview information from RIPE NCC
func (a *ASN) GetOVData() bool {
	if len(a.Number) < 2 {
		println("error: AS number invalid")
		return false
	}
	resp, err := http.Get(RIPEAPI + RIPEASNURL + a.Number)
	if err != nil {
		println(err.Error())
		return false
	}
	if resp.StatusCode != 200 {
		return false
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal(body, &a.Data)
	return true
}

// GetGeoData gets Geo information from RIPE NCC
func (a *ASN) GetGeoData() bool {
	if len(a.Number) < 2 {
		println("error: AS number invalid")
		return false
	}
	resp, err := http.Get(RIPEAPI + RIPEGeoURL + a.Number)
	if err != nil {
		println(err.Error())
		return false
	}
	if resp.StatusCode != 200 {
		println("error: check your AS number")
		return false
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal(body, &a.GeoData)
	return true
}

// PrettyPrint print ASN information (holder)
func (a *ASN) PrettyPrint() {
	var cols = make(map[string]float64)
	overviewData, ok := a.Data["data"].(map[string]interface{})
	if ok {
		println(string(overviewData["holder"].(string)))
	}
	geoLocData, ok := a.GeoData["data"].(map[string]interface{})
	if !ok {
		return
	}
	locs := geoLocData["locations"].([]interface{})
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Location", "Covered %"})
	for _, loc := range locs {
		geoInfo := loc.(map[string]interface{})
		cols[geoInfo["country"].(string)] = geoInfo["covered_percentage"].(float64)
	}
	for _, v := range sortMapFloat(cols) {
		name := v.key
		percent := v.value
		uc := strings.Split(name, "-")
		if country, ok := data.Country[uc[0]]; ok {
			name = country
		}
		if len(uc) == 2 {
			name = fmt.Sprintf("%s - %s", name, uc[1])
		}
		table.Append([]string{name, fmt.Sprintf("%.2f", percent)})
	}
	table.Render()
}

// IsASN checks if the key is a number
func IsASN(key string) bool {
	m, err := regexp.MatchString(`^\d+$`, key)
	if err != nil {
		return false
	}
	return m
}

// sortMapFloat sorts map[string]float64 w/ value
func sortMapFloat(m map[string]float64) []kv {
	n := map[float64][]string{}
	var (
		a []float64
		r []kv
	)
	for k, v := range m {
		n[v] = append(n[v], k)
	}
	for k := range n {
		a = append(a, k)
	}
	sort.Sort(sort.Reverse(sort.Float64Slice(a)))
	for _, k := range a {
		for _, s := range n[k] {
			r = append(r, kv{s, k})
		}
	}
	return r
}
<|endoftext|>


<commit_before>package risk

import (
	"encoding/json"
	"time"
)

const (
	// GetScoresEnpoint location of the getscores endpoint
	GetScoresEnpoint = "v1/score/getScores"

	// EcosystemScope constant with name of scope for ecosystem
	EcosystemScope = "ecosystem"

	// SupplyChainScope constant with name of scope for supply chain
	SupplyChainScope = "supply_chain"

	// TechnologyScope constant with name of scope for technology
	TechnologyScope = "technology"
)

type Metrics struct {
	ID      string   `json:"id"`
	Metrics []Metric `json:"metrics"`
}

type Metric struct {
	Name         string           `json:"name"`
	Bindings     []ScoreBinding   `json:"bindings"`
	Severity     string           `json:"severity"`
	SeverityRank int              `json:"severity_rank"`
	Value        *json.RawMessage `json:"value"`
	Type         string           `json:"type"`
}

// ScoreBinding a mapping from metric to which scope it falls into.
type ScoreBinding struct {
	Metric    string `json:"metric"`
	Scope     string `json:"scope"`
	Category  string `json:"category"`
	Attribute string `json:"attribute"`
	Source    string `json:"source"`
}

// Scores top level struct for modeling the score tree
type Scores struct {
	Name   string  `json:"name"`
	Value  float64 `json:"value"`
	Scopes []Scope `json:"scopes"`
}

// Scope second tier struct for modeling the score tree
type Scope struct {
	Name       string     `json:"name"`
	Value      float64    `json:"value"`
	Categories []Category `json:"-"`
}

// GetScope returns a Scope value based on the name supplied
func (s *Scores) GetScope(name string) *Scope {
	var scope *Scope
	for _, sco := range s.Scopes {
		if sco.Name == name {
			scope = &sco
			break
		}
	}

	if scope == nil {
		scope = &Scope{Name: name}
	}
	return scope
}

// Category third tier struct for modeling the score tree
type Category struct {
	Name       string      `json:"name"`
	Value      float64     `json:"value"`
	Attributes []Attribute `json:"-"`
}

// Attribute leaf tier struct for modeling the score tree
type Attribute struct {
	Name  string  `json:"name"`
	Value float64 `json:"value"`
}

type RiskTag struct {
	Name        string `json:"name"`
	Description string `json:"description"`
}

type MetricMetadata struct {
	Name           string    `json:"name"`
	InternalName   string    `json:"internal_name"`
	Definition     string    `json:"definition"`
	Scopes         []string  `json:"scopes"`
	RiskTags       []RiskTag `json:"risk_tags"`
	RelatedMetrics []string  `json:"related_metrics"`
	GraphYN        bool      `json:"graph_yn"`
}

// MetricPoint defines the data needed for points on a single risk point
type MetricPoint struct {
	Name   string `json:"name" xml:"name"`
	Points int    `json:"points" xml:"points"`
}

// MetricPoints defines the data needed for points on a single risk point
type MetricPoints struct {
	Metrics     []MetricPoint `json:"metrics" xml:"metrics"`
	ProcessedAt time.Time     `json:"processed_at" xml:"processed_at"`
}
<commit_msg>component summary structs<commit_after>package risk

import (
	"encoding/json"
	"time"
)

const (
	// GetScoresEnpoint location of the getscores endpoint
	GetScoresEnpoint = "v1/score/getScores"

	// EcosystemScope constant with name of scope for ecosystem
	EcosystemScope = "ecosystem"

	// SupplyChainScope constant with name of scope for supply chain
	SupplyChainScope = "supply_chain"

	// TechnologyScope constant with name of scope for technology
	TechnologyScope = "technology"
)

type Metrics struct {
	ID      string   `json:"id"`
	Metrics []Metric `json:"metrics"`
}

type Metric struct {
	Name         string           `json:"name"`
	Bindings     []ScoreBinding   `json:"bindings"`
	Severity     string           `json:"severity"`
	SeverityRank int              `json:"severity_rank"`
	Value        *json.RawMessage `json:"value"`
	Type         string           `json:"type"`
}

// ScoreBinding a mapping from metric to which scope it falls into.
type ScoreBinding struct {
	Metric    string `json:"metric"`
	Scope     string `json:"scope"`
	Category  string `json:"category"`
	Attribute string `json:"attribute"`
	Source    string `json:"source"`
}

// Scores top level struct for modeling the score tree
type Scores struct {
	Name   string  `json:"name"`
	Value  float64 `json:"value"`
	Scopes []Scope `json:"scopes"`
}

// Scope second tier struct for modeling the score tree
type Scope struct {
	Name       string     `json:"name"`
	Value      float64    `json:"value"`
	Categories []Category `json:"-"`
}

// GetScope returns a Scope value based on the name supplied
func (s *Scores) GetScope(name string) *Scope {
	var scope *Scope
	for _, sco := range s.Scopes {
		if sco.Name == name {
			scope = &sco
			break
		}
	}

	if scope == nil {
		scope = &Scope{Name: name}
	}
	return scope
}

// Category third tier struct for modeling the score tree
type Category struct {
	Name       string      `json:"name"`
	Value      float64     `json:"value"`
	Attributes []Attribute `json:"-"`
}

// Attribute leaf tier struct for modeling the score tree
type Attribute struct {
	Name  string  `json:"name"`
	Value float64 `json:"value"`
}

type RiskTag struct {
	Name        string `json:"name"`
	Description string `json:"description"`
}

type MetricMetadata struct {
	Name           string    `json:"name"`
	InternalName   string    `json:"internal_name"`
	Definition     string    `json:"definition"`
	Scopes         []string  `json:"scopes"`
	RiskTags       []RiskTag `json:"risk_tags"`
	RelatedMetrics []string  `json:"related_metrics"`
	GraphYN        bool      `json:"graph_yn"`
}

// MetricPoint defines the data needed for points on a single risk point
type MetricPoint struct {
	Name   string `json:"name" xml:"name"`
	Points int    `json:"points" xml:"points"`
}

// MetricPoints defines the data needed for points on a single risk point
type MetricPoints struct {
	Metrics     []MetricPoint `json:"metrics" xml:"metrics"`
	ProcessedAt time.Time     `json:"processed_at" xml:"processed_at"`
}

type ComponentOverview struct {
	ID               string             `json:"id"`
	Name             string             `json:"name"`
	Org              string             `json:"org"`
	Version          string             `json:"version"`
	Sources          []ComponentSource  `json:"sources"`
	Scores           Scores             `json:"score"`
	ComponentSummary string             `json:"summary"`
	RiskTags         []ComponentRiskTag `json:"risk_tags"`
}

type ComponentRiskTag struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Severity    string `json:"severity"`
}

type ComponentSource struct {
	Type   string   `json:"type"`
	Source []string `json:"source"`
	ID     string   `json:"id"`
}
<|endoftext|>


<commit_before>package rethinkgo

import (
	test "launchpad.net/gocheck"
)

type object struct {
	Id    int64  `rethinkdb:"id"`
	Name  string `rethinkdb:"name"`
	Attrs []attr
}

type attr struct {
	Name  string
	Value interface{}
}

func (s *RethinkSuite) TestResultScanLiteral(c *test.C) {
	row := Expr(5).RunRow(conn)

	var response interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, 5)
}

func (s *RethinkSuite) TestResultScanSlice(c *test.C) {
	row := Expr(List{1, 2, 3, 4, 5}).RunRow(conn)

	var response interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, List{1, 2, 3, 4, 5})
}

func (s *RethinkSuite) TestResultScanMap(c *test.C) {
	row := Expr(Obj{
		"id":   2,
		"name": "Object 1",
	}).RunRow(conn)

	var response map[string]interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, Obj{
		"id":   2,
		"name": "Object 1",
	})
}

func (s *RethinkSuite) TestResultScanMapIntoInterface(c *test.C) {
	row := Expr(Obj{
		"id":   2,
		"name": "Object 1",
	}).RunRow(conn)

	var response interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, Obj{
		"id":   2,
		"name": "Object 1",
	})
}

func (s *RethinkSuite) TestResultScanMapNested(c *test.C) {
	row := Expr(Obj{
		"id":   2,
		"name": "Object 1",
		"attr": List{Obj{
			"name":  "attr 1",
			"value": "value 1",
		}},
	}).RunRow(conn)

	var response interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, Obj{
		"id":   2,
		"name": "Object 1",
		"attr": List{Obj{
			"name":  "attr 1",
			"value": "value 1",
		}},
	})
}

func (s *RethinkSuite) TestResultScanStruct(c *test.C) {
	row := Expr(Obj{
		"id":   2,
		"name": "Object 1",
		"Attrs": List{Obj{
			"Name":  "attr 1",
			"Value": "value 1",
		}},
	}).RunRow(conn)

	var response object
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, test.DeepEquals, object{
		Id:   2,
		Name: "Object 1",
		Attrs: []attr{attr{
			Name:  "attr 1",
			Value: "value 1",
		}},
	})
}

func (s *RethinkSuite) TestResultAtomString(c *test.C) {
	row := Expr("a").RunRow(conn)

	var response string
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, test.Equals, "a")
}

func (s *RethinkSuite) TestResultAtomArray(c *test.C) {
	row := Expr(List{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}).RunRow(conn)

	var response List
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, test.DeepEquals, List{1, 2, 3, 4, 5, 6, 7, 8, 9, 0})
}
<commit_msg>Fixed test checking wrong types<commit_after>package rethinkgo

import (
	test "launchpad.net/gocheck"
)

type object struct {
	Id    int64  `rethinkdb:"id"`
	Name  string `rethinkdb:"name"`
	Attrs []attr
}

type attr struct {
	Name  string
	Value interface{}
}

func (s *RethinkSuite) TestResultScanLiteral(c *test.C) {
	row := Expr(5).RunRow(conn)

	var response interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, 5)
}

func (s *RethinkSuite) TestResultScanSlice(c *test.C) {
	row := Expr(List{1, 2, 3, 4, 5}).RunRow(conn)

	var response interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, List{1, 2, 3, 4, 5})
}

func (s *RethinkSuite) TestResultScanMap(c *test.C) {
	row := Expr(Obj{
		"id":   2,
		"name": "Object 1",
	}).RunRow(conn)

	var response map[string]interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, Obj{
		"id":   2,
		"name": "Object 1",
	})
}

func (s *RethinkSuite) TestResultScanMapIntoInterface(c *test.C) {
	row := Expr(Obj{
		"id":   2,
		"name": "Object 1",
	}).RunRow(conn)

	var response interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, Obj{
		"id":   2,
		"name": "Object 1",
	})
}

func (s *RethinkSuite) TestResultScanMapNested(c *test.C) {
	row := Expr(Obj{
		"id":   2,
		"name": "Object 1",
		"attr": List{Obj{
			"name":  "attr 1",
			"value": "value 1",
		}},
	}).RunRow(conn)

	var response interface{}
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, JsonEquals, Obj{
		"id":   2,
		"name": "Object 1",
		"attr": List{Obj{
			"name":  "attr 1",
			"value": "value 1",
		}},
	})
}

func (s *RethinkSuite) TestResultScanStruct(c *test.C) {
	row := Expr(Obj{
		"id":   2,
		"name": "Object 1",
		"Attrs": List{Obj{
			"Name":  "attr 1",
			"Value": "value 1",
		}},
	}).RunRow(conn)

	var response object
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, test.DeepEquals, object{
		Id:   2,
		Name: "Object 1",
		Attrs: []attr{attr{
			Name:  "attr 1",
			Value: "value 1",
		}},
	})
}

func (s *RethinkSuite) TestResultAtomString(c *test.C) {
	row := Expr("a").RunRow(conn)

	var response string
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, test.Equals, "a")
}

func (s *RethinkSuite) TestResultAtomArray(c *test.C) {
	row := Expr(List{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}).RunRow(conn)

	var response []int
	err := row.Scan(&response)
	c.Assert(err, test.IsNil)
	c.Assert(response, test.DeepEquals, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0})
}
<|endoftext|>


<commit_before>/*
RpcClient for Go RPC Servers
Copyright (C) 2012-2014 ITsysCOM GmbH

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package rpcclient

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	//"log/syslog"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"
	"reflect"
	"strings"
	"sync"
	"syscall"
	"time"
)

const (
	JSON_RPC       = "json"
	JSON_HTTP      = "http_jsonrpc"
	GOB_RPC        = "gob"
	INTERNAL_RPC   = "internal"
	POOL_FIRST     = "first"
	POOL_RANDOM    = "random"
	POOL_NEXT      = "next"
	POOL_BROADCAST = "broadcast"
)

var (
	ErrReqUnsynchronized       = errors.New("REQ_UNSYNCHRONIZED")
	ErrUnsupporteServiceMethod = errors.New("UNSUPPORTED_SERVICE_METHOD")
	ErrWrongArgsType           = errors.New("WRONG_ARGS_TYPE")
	ErrWrongReplyType          = errors.New("WRONG_REPLY_TYPE")
	//logger                     *syslog.Writer
)

/*
func init() {
	logger, _ = syslog.New(syslog.LOG_INFO, "RPCClient") // If we need to report anything to syslog
}
*/

// successive Fibonacci numbers.
func Fib() func() time.Duration {
	a, b := 0, 1
	return func() time.Duration {
		a, b = b, a+b
		return time.Duration(a) * time.Second
	}
}

func NewRpcClient(transport, addr string, connectAttempts, reconnects int, codec string, internalConn RpcClientConnection) (*RpcClient, error) {
	var err error
	rpcClient := &RpcClient{transport: transport, address: addr, reconnects: reconnects, codec: codec, connection: internalConn, connMux: new(sync.Mutex)}
	delay := Fib()
	for i := 0; i < connectAttempts; i++ {
		err = rpcClient.connect()
		if err == nil { //Connected so no need to reiterate
			break
		}
		time.Sleep(delay())
	}
	if err != nil {
		return nil, err
	}
	return rpcClient, nil
}

type RpcClient struct {
	transport  string
	address    string
	reconnects int
	codec      string // JSON_RPC or GOB_RPC
	connection RpcClientConnection
	connMux    *sync.Mutex
}

func (self *RpcClient) connect() (err error) {
	self.connMux.Lock()
	defer self.connMux.Unlock()
	switch self.codec {
	case JSON_RPC:
		self.connection, err = jsonrpc.Dial(self.transport, self.address)
	case JSON_HTTP:
		self.connection = &HttpJsonRpcClient{httpClient: new(http.Client), url: self.address}
	case INTERNAL_RPC:
		return nil // connection should be set on init
	default:
		self.connection, err = rpc.Dial(self.transport, self.address)
	}
	return
}

func (self *RpcClient) reconnect() (err error) {
	if self.codec == JSON_HTTP { // http client has automatic reconnects in place
		return self.connect()
	}
	i := 0
	delay := Fib()
	for {
		if self.reconnects != -1 && i >= self.reconnects { // Maximum reconnects reached, -1 for infinite reconnects
			break
		}
		if err = self.connect(); err == nil { // No error on connect, succcess
			return nil
		}
		time.Sleep(delay()) // Cound not reconnect, retry
	}
	return errors.New("RECONNECT_FAIL")
}

func (self *RpcClient) Call(serviceMethod string, args interface{}, reply interface{}) error {
	err := self.connection.Call(serviceMethod, args, reply)
	if isNetworkError(err) && self.reconnects != 0 {
		if errReconnect := self.reconnect(); errReconnect != nil {
			return err
		} else { // Run command after reconnect
			return self.connection.Call(serviceMethod, args, reply)
		}
	}
	return err
}

// Connection used in RpcClient, as interface so we can combine the rpc.RpcClient with http one or websocket
type RpcClientConnection interface {
	Call(string, interface{}, interface{}) error
}

// Response received for
type JsonRpcResponse struct {
	Id     uint64
	Result *json.RawMessage
	Error  interface{}
}

type HttpJsonRpcClient struct {
	httpClient *http.Client
	id         uint64
	url        string
}

func (self *HttpJsonRpcClient) Call(serviceMethod string, args interface{}, reply interface{}) error {
	self.id += 1
	id := self.id
	data, err := json.Marshal(map[string]interface{}{
		"method": serviceMethod,
		"id":     self.id,
		"params": [1]interface{}{args},
	})
	if err != nil {
		return err
	}
	resp, err := self.httpClient.Post(self.url, "application/json", ioutil.NopCloser(strings.NewReader(string(data)))) // Closer so we automatically have close after response
	if err != nil {
		return err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var jsonRsp JsonRpcResponse
	err = json.Unmarshal(body, &jsonRsp)
	if err != nil {
		return err
	}
	if jsonRsp.Id != id {
		return ErrReqUnsynchronized
	}
	if jsonRsp.Error != nil || jsonRsp.Result == nil {
		x, ok := jsonRsp.Error.(string)
		if !ok {
			return fmt.Errorf("invalid error %v", jsonRsp.Error)
		}
		if x == "" {
			x = "unspecified error"
		}
		return errors.New(x)
	}
	return json.Unmarshal(*jsonRsp.Result, reply)
}

type RpcClientPool struct {
	transmissionType string
	connections      []RpcClientConnection
	counter          int
}

func NewRpcClientPool(transmissionType string) *RpcClientPool {
	return &RpcClientPool{transmissionType: transmissionType}
}

func (pool *RpcClientPool) AddClient(rcc RpcClientConnection) {
	if rcc != nil {
		pool.connections = append(pool.connections, rcc)
	}
}

func (pool *RpcClientPool) Call(serviceMethod string, args interface{}, reply interface{}) (err error) {
	switch pool.transmissionType {
	case POOL_BROADCAST:
		replyChan := make(chan *rpcReplyError, len(pool.connections))
		for _, rc := range pool.connections {
			go func(conn RpcClientConnection) {
				// make a new pointer of the same type
				rpl := reflect.New(reflect.TypeOf(reflect.ValueOf(reply).Elem().Interface()))
				err := conn.Call(serviceMethod, args, rpl.Interface())
				if !isNetworkError(err) {
					replyChan <- &rpcReplyError{reply: rpl.Interface(), err: err}
				}
			}(rc)
		}
		//get first response
		re := <-replyChan
		// put received value in the orig reply
		reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(re.reply).Elem())
		return re.err
	case POOL_FIRST:
		for _, rc := range pool.connections {
			err = rc.Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	case POOL_NEXT:
		ln := len(pool.connections)
		rrIndexes := roundIndex(int(math.Mod(float64(pool.counter), float64(ln))), ln)
		pool.counter++
		for _, index := range rrIndexes {
			err = pool.connections[index].Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	case POOL_RANDOM:
		rand.Seed(time.Now().UnixNano())
		randomIndex := rand.Perm(len(pool.connections))
		for _, index := range randomIndex {
			err = pool.connections[index].Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	}
	return
}

type rpcReplyError struct {
	reply interface{}
	err   error
}

// generates round robin indexes for a slice of length max
// starting from index start
func roundIndex(start, max int) []int {
	if start < 0 {
		start = 0
	}
	result := make([]int, max)
	for i := 0; i < max; i++ {
		if start+i < max {
			result[i] = start + i
		} else {
			result[i] = int(math.Abs(float64(max - (start + i))))
		}
	}
	return result
}

func isNetworkError(err error) bool {
	if err == nil {
		return false
	}
	if operr, ok := err.(*net.OpError); ok && strings.HasSuffix(operr.Err.Error(), syscall.ECONNRESET.Error()) { // connection reset
		return true
	}
	return (err == rpc.ErrShutdown ||
		err == ErrReqUnsynchronized)
}
<commit_msg>Reconnects fix, nil interface when error on connection<commit_after>/*
RpcClient for Go RPC Servers
Copyright (C) 2012-2014 ITsysCOM GmbH

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package rpcclient

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	//"log/syslog"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"
	"reflect"
	"strings"
	"sync"
	"syscall"
	"time"
)

const (
	JSON_RPC       = "json"
	JSON_HTTP      = "http_jsonrpc"
	GOB_RPC        = "gob"
	INTERNAL_RPC   = "internal"
	POOL_FIRST     = "first"
	POOL_RANDOM    = "random"
	POOL_NEXT      = "next"
	POOL_BROADCAST = "broadcast"
)

var (
	ErrReqUnsynchronized       = errors.New("REQ_UNSYNCHRONIZED")
	ErrUnsupporteServiceMethod = errors.New("UNSUPPORTED_SERVICE_METHOD")
	ErrWrongArgsType           = errors.New("WRONG_ARGS_TYPE")
	ErrWrongReplyType          = errors.New("WRONG_REPLY_TYPE")
	ErrDisconnected            = errors.New("DISCONNECTED")
	//logger                     *syslog.Writer
)

func init() {
	//logger, _ = syslog.New(syslog.LOG_INFO, "RPCClient") // If we need to report anything to syslog
}

// successive Fibonacci numbers.
func Fib() func() time.Duration {
	a, b := 0, 1
	return func() time.Duration {
		a, b = b, a+b
		return time.Duration(a) * time.Second
	}
}

func NewRpcClient(transport, addr string, connectAttempts, reconnects int, codec string, internalConn RpcClientConnection) (*RpcClient, error) {
	var err error
	rpcClient := &RpcClient{transport: transport, address: addr, reconnects: reconnects, codec: codec, connection: internalConn, connMux: new(sync.Mutex)}
	delay := Fib()
	for i := 0; i < connectAttempts; i++ {
		err = rpcClient.connect()
		if err == nil { //Connected so no need to reiterate
			break
		}
		time.Sleep(delay())
	}
	if err != nil {
		return nil, err
	}
	return rpcClient, nil
}

type RpcClient struct {
	transport  string
	address    string
	reconnects int
	codec      string // JSON_RPC or GOB_RPC
	connection RpcClientConnection
	connMux    *sync.Mutex
}

func (self *RpcClient) connect() (err error) {
	self.connMux.Lock()
	defer self.connMux.Unlock()
	switch self.codec {
	case JSON_RPC:
		self.connection, err = jsonrpc.Dial(self.transport, self.address)
	case JSON_HTTP:
		self.connection = &HttpJsonRpcClient{httpClient: new(http.Client), url: self.address}
	case INTERNAL_RPC:
		return nil // connection should be set on init
	default:
		self.connection, err = rpc.Dial(self.transport, self.address)
	}
	if err != nil {
		self.connection = nil // So we don't wrap nil into the interface
	}
	return
}

func (self *RpcClient) reconnect() (err error) {
	if self.codec == JSON_HTTP { // http client has automatic reconnects in place
		return self.connect()
	}
	i := 0
	delay := Fib()
	for {
		if self.reconnects != -1 && i >= self.reconnects { // Maximum reconnects reached, -1 for infinite reconnects
			break
		}
		if err = self.connect(); err == nil { // No error on connect, succcess
			return nil
		}
		i++
		time.Sleep(delay()) // Cound not reconnect, retry
	}
	return errors.New("RECONNECT_FAIL")
}

func (self *RpcClient) Call(serviceMethod string, args interface{}, reply interface{}) (err error) {
	if self.connection == nil {
		err = ErrDisconnected
	} else {
		err = self.connection.Call(serviceMethod, args, reply)
	}
	if isNetworkError(err) && self.reconnects != 0 {
		if errReconnect := self.reconnect(); errReconnect != nil {
			return err
		} else { // Run command after reconnect
			return self.connection.Call(serviceMethod, args, reply)
		}
	}
	return err
}

// Connection used in RpcClient, as interface so we can combine the rpc.RpcClient with http one or websocket
type RpcClientConnection interface {
	Call(string, interface{}, interface{}) error
}

// Response received for
type JsonRpcResponse struct {
	Id     uint64
	Result *json.RawMessage
	Error  interface{}
}

type HttpJsonRpcClient struct {
	httpClient *http.Client
	id         uint64
	url        string
}

func (self *HttpJsonRpcClient) Call(serviceMethod string, args interface{}, reply interface{}) error {
	self.id += 1
	id := self.id
	data, err := json.Marshal(map[string]interface{}{
		"method": serviceMethod,
		"id":     self.id,
		"params": [1]interface{}{args},
	})
	if err != nil {
		return err
	}
	resp, err := self.httpClient.Post(self.url, "application/json", ioutil.NopCloser(strings.NewReader(string(data)))) // Closer so we automatically have close after response
	if err != nil {
		return err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var jsonRsp JsonRpcResponse
	err = json.Unmarshal(body, &jsonRsp)
	if err != nil {
		return err
	}
	if jsonRsp.Id != id {
		return ErrReqUnsynchronized
	}
	if jsonRsp.Error != nil || jsonRsp.Result == nil {
		x, ok := jsonRsp.Error.(string)
		if !ok {
			return fmt.Errorf("invalid error %v", jsonRsp.Error)
		}
		if x == "" {
			x = "unspecified error"
		}
		return errors.New(x)
	}
	return json.Unmarshal(*jsonRsp.Result, reply)
}

type RpcClientPool struct {
	transmissionType string
	connections      []RpcClientConnection
	counter          int
}

func NewRpcClientPool(transmissionType string) *RpcClientPool {
	return &RpcClientPool{transmissionType: transmissionType}
}

func (pool *RpcClientPool) AddClient(rcc RpcClientConnection) {
	if rcc != nil {
		pool.connections = append(pool.connections, rcc)
	}
}

func (pool *RpcClientPool) Call(serviceMethod string, args interface{}, reply interface{}) (err error) {
	switch pool.transmissionType {
	case POOL_BROADCAST:
		replyChan := make(chan *rpcReplyError, len(pool.connections))
		for _, rc := range pool.connections {
			go func(conn RpcClientConnection) {
				// make a new pointer of the same type
				rpl := reflect.New(reflect.TypeOf(reflect.ValueOf(reply).Elem().Interface()))
				err := conn.Call(serviceMethod, args, rpl.Interface())
				if !isNetworkError(err) {
					replyChan <- &rpcReplyError{reply: rpl.Interface(), err: err}
				}
			}(rc)
		}
		//get first response
		re := <-replyChan
		// put received value in the orig reply
		reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(re.reply).Elem())
		return re.err
	case POOL_FIRST:
		for _, rc := range pool.connections {
			err = rc.Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	case POOL_NEXT:
		ln := len(pool.connections)
		rrIndexes := roundIndex(int(math.Mod(float64(pool.counter), float64(ln))), ln)
		pool.counter++
		for _, index := range rrIndexes {
			err = pool.connections[index].Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	case POOL_RANDOM:
		rand.Seed(time.Now().UnixNano())
		randomIndex := rand.Perm(len(pool.connections))
		for _, index := range randomIndex {
			err = pool.connections[index].Call(serviceMethod, args, reply)
			if isNetworkError(err) {
				continue
			}
			return
		}
	}
	return
}

type rpcReplyError struct {
	reply interface{}
	err   error
}

// generates round robin indexes for a slice of length max
// starting from index start
func roundIndex(start, max int) []int {
	if start < 0 {
		start = 0
	}
	result := make([]int, max)
	for i := 0; i < max; i++ {
		if start+i < max {
			result[i] = start + i
		} else {
			result[i] = int(math.Abs(float64(max - (start + i))))
		}
	}
	return result
}

func isNetworkError(err error) bool {
	if err == nil {
		return false
	}
	if operr, ok := err.(*net.OpError); ok && strings.HasSuffix(operr.Err.Error(), syscall.ECONNRESET.Error()) { // connection reset
		return true
	}
	return (err == rpc.ErrShutdown ||
		err == ErrReqUnsynchronized || err == ErrDisconnected)
}
<|endoftext|>


<commit_before>package rpio

import (
	"os"
	"testing"
	"time"
)

func TestMain(m *testing.M) {
	println("Note: bcm pins 2 and 3 has to be directly connected")
	if err := Open(); err != nil {
		panic(err)
	}
	defer Close()
	os.Exit(m.Run())
}

func TestEvent(t *testing.T) {
	src := Pin(3)
	src.Mode(Output)

	pin := Pin(2)
	pin.Mode(Input)
	pin.PullDown()

	t.Run("rising edge", func(t *testing.T) {
		pin.Detect(RiseEdge)
		src.Low()

		for i := 0; ; i++ {
			src.High()

			time.Sleep(time.Second / 10)
			if pin.EdgeDetected() {
				t.Log("edge rised")
			} else {
				t.Errorf("Rise event should be detected")
			}
			if i == 5 {
				break
			}

			src.Low()
		}

		time.Sleep(time.Second / 10)
		if pin.EdgeDetected() {
			t.Error("Rise should not be detected, no change since last call")
		}
		pin.Detect(NoEdge)
		src.High()
		if pin.EdgeDetected() {
			t.Error("Rise should not be detected, events disabled")
		}

	})

	t.Run("falling edge", func(t *testing.T) {
		pin.Detect(FallEdge)
		src.High()

		for i := 0; ; i++ {
			src.Low()

			time.Sleep(time.Second / 10)
			if pin.EdgeDetected() {
				t.Log("edge fallen")
			} else {
				t.Errorf("Fall event should be detected")
			}

			if i == 5 {
				break
			}

			src.High()
		}
		time.Sleep(time.Second / 10)
		if pin.EdgeDetected() {
			t.Error("Fall should not be detected, no change since last call")
		}
		pin.Detect(NoEdge)
		src.Low()
		if pin.EdgeDetected() {
			t.Error("Fall should not be detected, events disabled")
		}
	})

	t.Run("both edges", func(t *testing.T) {
		pin.Detect(AnyEdge)
		src.Low()

		for i := 0; i < 5; i++ {
			src.High()

			if pin.EdgeDetected() {
				t.Log("edge detected")
			} else {
				t.Errorf("Rise event shoud be detected")
			}

			src.Low()

			if pin.EdgeDetected() {
				t.Log("edge detected")
			} else {
				t.Errorf("Fall edge should be detected")
			}
		}

		pin.Detect(NoEdge)
		src.High()
		src.Low()

		if pin.EdgeDetected() {
			t.Errorf("No edge should be detected, events disabled")
		}

	})

}

func BenchmarkToggle(b *testing.B) {
	src := Pin(3)
	src.Mode(Output)
	src.Low()

	pin := Pin(2)
	pin.Mode(Input)
	pin.PullDown()

	b.Run("old", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			oldToggle(src)
		}
	})

	b.Run("current", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			TogglePin(src)
		}
	})

}

func BenchmarkWrite(b *testing.B) {
	src := Pin(3)
	src.Mode(Output)
	src.Low()

	pin := Pin(2)
	pin.Mode(Input)
	pin.PullDown()

	b.Run("old", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			if i%2 == 0 {
				oldWrite(src, High)
			} else {
				oldWrite(src, Low)
			}
		}
	})

	b.Run("current", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			if i%2 == 0 {
				WritePin(src, High)
			} else {
				WritePin(src, Low)
			}
		}
	})

}

func oldToggle(pin Pin) {
	switch ReadPin(pin) {
	case Low:
		oldWrite(pin, High)
	case High:
		oldWrite(pin, Low)
	}
}

func oldWrite(pin Pin, state State) {
	p := uint8(pin)

	setReg := p/32 + 7
	clearReg := p/32 + 10

	memlock.Lock()
	defer memlock.Unlock()

	if state == Low {
		gpioMem[clearReg] = 1 << (p & 31)
	} else {
		gpioMem[setReg] = 1 << (p & 31)
	}
}
<commit_msg>Update benchmark<commit_after>package rpio

import (
	"os"
	"testing"
	"time"
)

func TestMain(m *testing.M) {
	println("Note: bcm pins 2 and 3 has to be directly connected")
	if err := Open(); err != nil {
		panic(err)
	}
	defer Close()
	os.Exit(m.Run())
}

func TestEvent(t *testing.T) {
	src := Pin(3)
	src.Mode(Output)

	pin := Pin(2)
	pin.Mode(Input)
	pin.PullDown()

	t.Run("rising edge", func(t *testing.T) {
		pin.Detect(RiseEdge)
		src.Low()

		for i := 0; ; i++ {
			src.High()

			time.Sleep(time.Second / 10)
			if pin.EdgeDetected() {
				t.Log("edge rised")
			} else {
				t.Errorf("Rise event should be detected")
			}
			if i == 5 {
				break
			}

			src.Low()
		}

		time.Sleep(time.Second / 10)
		if pin.EdgeDetected() {
			t.Error("Rise should not be detected, no change since last call")
		}
		pin.Detect(NoEdge)
		src.High()
		if pin.EdgeDetected() {
			t.Error("Rise should not be detected, events disabled")
		}

	})

	t.Run("falling edge", func(t *testing.T) {
		pin.Detect(FallEdge)
		src.High()

		for i := 0; ; i++ {
			src.Low()

			time.Sleep(time.Second / 10)
			if pin.EdgeDetected() {
				t.Log("edge fallen")
			} else {
				t.Errorf("Fall event should be detected")
			}

			if i == 5 {
				break
			}

			src.High()
		}
		time.Sleep(time.Second / 10)
		if pin.EdgeDetected() {
			t.Error("Fall should not be detected, no change since last call")
		}
		pin.Detect(NoEdge)
		src.Low()
		if pin.EdgeDetected() {
			t.Error("Fall should not be detected, events disabled")
		}
	})

	t.Run("both edges", func(t *testing.T) {
		pin.Detect(AnyEdge)
		src.Low()

		for i := 0; i < 5; i++ {
			src.High()

			if pin.EdgeDetected() {
				t.Log("edge detected")
			} else {
				t.Errorf("Rise event shoud be detected")
			}

			src.Low()

			if pin.EdgeDetected() {
				t.Log("edge detected")
			} else {
				t.Errorf("Fall edge should be detected")
			}
		}

		pin.Detect(NoEdge)
		src.High()
		src.Low()

		if pin.EdgeDetected() {
			t.Errorf("No edge should be detected, events disabled")
		}

	})

}

func BenchmarkGpio(b *testing.B) {
	src := Pin(3)
	src.Mode(Output)
	src.Low()

	pin := Pin(2)
	pin.Mode(Input)
	pin.PullDown()

	oldWrite := func(pin Pin, state State) {
		p := uint8(pin)

		setReg := p/32 + 7
		clearReg := p/32 + 10

		memlock.Lock()
		defer memlock.Unlock()

		if state == Low {
			gpioMem[clearReg] = 1 << (p & 31)
		} else {
			gpioMem[setReg] = 1 << (p & 31)
		}
	}

	oldToggle := func(pin Pin) {
		switch ReadPin(pin) {
		case Low:
			oldWrite(pin, High)
		case High:
			oldWrite(pin, Low)
		}
	}

	b.Run("write", func(b *testing.B) {
		b.Run("old", func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				if i%2 == 0 {
					oldWrite(src, High)
				} else {
					oldWrite(src, Low)
				}
			}
		})

		b.Run("new", func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				if i%2 == 0 {
					WritePin(src, High)
				} else {
					WritePin(src, Low)
				}
			}
		})
	})

	b.Run("toggle", func(b *testing.B) {
		b.Run("old", func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				oldToggle(src)
			}
		})

		b.Run("new", func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				TogglePin(src)
			}
		})
	})

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
	"testing"

	cv "github.com/smartystreets/goconvey/convey"
)

func TestRuid(t *testing.T) {

	ruidGen := NewRuidGen("put unique location string here")
	cv.Convey("Given we generate two Ruids() very quickly", t, func() {
		cv.Convey("Then they should be unique, and start with 'ruid_v'", func() {
			r1 := ruidGen.Ruid()
			r2 := ruidGen.Ruid()
			fmt.Printf("\n r1 = '%s'\n", r1)
			fmt.Printf("\n r2 = '%s'\n", r2)
			cv.So(strings.HasPrefix(r1, `ruid_v`), cv.ShouldEqual, true)
			cv.So(strings.HasPrefix(r2, `ruid_v`), cv.ShouldEqual, true)
			cv.So(r1, cv.ShouldNotEqual, r2)
		})

		cv.Convey("And two Huids() they should be unique, and start with 'huid_v'", func() {
			h1 := ruidGen.Huid()
			h2 := ruidGen.Huid()
			fmt.Printf("\n h1 = '%s'\n", h1)
			fmt.Printf("\n h2 = '%s'\n", h2)
			cv.So(strings.HasPrefix(h1, `huid_v`), cv.ShouldEqual, true)
			cv.So(strings.HasPrefix(h2, `huid_v`), cv.ShouldEqual, true)
			cv.So(h1, cv.ShouldNotEqual, h2)
		})
	})
}

func BenchmarkRuid(b *testing.B) {

	myExternalIP := "my example location"
	ruidGen := NewRuidGen(myExternalIP)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ruidGen.Ruid()
	}
}

func BenchmarkHuid(b *testing.B) {

	myExternalIP := "my example location"
	ruidGen := NewRuidGen(myExternalIP)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ruidGen.Huid()
	}
}
<commit_msg>bench++<commit_after>package main

import (
	"fmt"
	"strings"
	"testing"

	cv "github.com/smartystreets/goconvey/convey"
	"github.com/twinj/uuid"
)

func TestRuid(t *testing.T) {

	ruidGen := NewRuidGen("put unique location string here")
	cv.Convey("Given we generate two Ruids() very quickly", t, func() {
		cv.Convey("Then they should be unique, and start with 'ruid_v'", func() {
			r1 := ruidGen.Ruid()
			r2 := ruidGen.Ruid()
			fmt.Printf("\n r1 = '%s'\n", r1)
			fmt.Printf("\n r2 = '%s'\n", r2)
			cv.So(strings.HasPrefix(r1, `ruid_v`), cv.ShouldEqual, true)
			cv.So(strings.HasPrefix(r2, `ruid_v`), cv.ShouldEqual, true)
			cv.So(r1, cv.ShouldNotEqual, r2)
		})

		cv.Convey("And two Huids() they should be unique, and start with 'huid_v'", func() {
			h1 := ruidGen.Huid()
			h2 := ruidGen.Huid()
			fmt.Printf("\n h1 = '%s'\n", h1)
			fmt.Printf("\n h2 = '%s'\n", h2)
			cv.So(strings.HasPrefix(h1, `huid_v`), cv.ShouldEqual, true)
			cv.So(strings.HasPrefix(h2, `huid_v`), cv.ShouldEqual, true)
			cv.So(h1, cv.ShouldNotEqual, h2)
		})
	})
}

func BenchmarkRuid(b *testing.B) {

	myExternalIP := "my example location"
	ruidGen := NewRuidGen(myExternalIP)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ruidGen.Ruid()
	}
}

func BenchmarkHuid(b *testing.B) {

	myExternalIP := "my example location"
	ruidGen := NewRuidGen(myExternalIP)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ruidGen.Huid()
	}
}

func BenchmarkUUID4(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		uuid.NewV4()
	}
}
<|endoftext|>


<commit_before>package transfer

import (
	"errors"
	"fmt"
	"hash"
	"io"
	"os"
	"path/filepath"

	"github.com/github/git-lfs/errutil"
	"github.com/github/git-lfs/httputil"
	"github.com/github/git-lfs/localstorage"
	"github.com/github/git-lfs/tools"
	"github.com/rubyist/tracerx"
)

const (
	HttpRangeAdapterName = "http-range"
)

// Download adapter that can resume downloads using HTTP Range headers
type httpRangeAdapter struct {
	*adapterBase
}

func (a *httpRangeAdapter) ClearTempStorage() error {
	return os.RemoveAll(a.tempDir())
}

func (a *httpRangeAdapter) tempDir() string {
	// Must be dedicated to this adapter as deleted by ClearTempStorage
	// Also make local to this repo not global, and separate to localstorage temp,
	// which gets cleared at the end of every invocation
	d := filepath.Join(localstorage.Objects().RootDir, "incomplete")
	if err := os.MkdirAll(d, 0755); err != nil {
		return os.TempDir()
	}
	return d
}

func (a *httpRangeAdapter) DoTransfer(t *Transfer, cb TransferProgressCallback, authOkFunc func()) error {

	f, fromByte, hashSoFar, err := a.checkResumeDownload(t)
	if err != nil {
		return err
	}
	return a.download(t, cb, authOkFunc, f, fromByte, hashSoFar)
}

// Checks to see if a download can be resumed, and if so returns a non-nil locked file, byte start and hash
func (a *httpRangeAdapter) checkResumeDownload(t *Transfer) (outFile *os.File, fromByte int64, hashSoFar hash.Hash, e error) {
	// lock the file by opening it for read/write, rather than checking Stat() etc
	// which could be subject to race conditions by other processes
	f, err := os.OpenFile(a.downloadFilename(t), os.O_RDWR, 0644)

	if err != nil {
		// Create a new file instead, must not already exist or error (permissions / race condition)
		newfile, err := os.OpenFile(a.downloadFilename(t), os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644)
		return newfile, 0, nil, err
	}

	// Successfully opened an existing file at this point
	// Read any existing data into hash then return file handle at end
	hash := tools.NewLfsContentHash()
	n, err := io.Copy(hash, f)
	if err != nil {
		f.Close()
		return nil, 0, nil, err
	}
	tracerx.Printf("http-range: Attempting to resume download of %q from byte %d", t.Object.Oid, n)
	return f, n, hash, nil

}

// Create or open a download file for resuming
func (a *httpRangeAdapter) downloadFilename(t *Transfer) string {
	// Not a temp file since we will be resuming it
	return filepath.Join(a.tempDir(), t.Object.Oid+".tmp")
}

// download starts or resumes and download. Always closes dlFile if non-nil
func (a *httpRangeAdapter) download(t *Transfer, cb TransferProgressCallback, authOkFunc func(), dlFile *os.File, fromByte int64, hash hash.Hash) error {

	if dlFile != nil {
		// ensure we always close dlFile. Note that this does not conflict with the
		// early close below, as close is idempotent.
		defer dlFile.Close()
	}

	rel, ok := t.Object.Rel("download")
	if !ok {
		return errors.New("Object not found on the server.")
	}

	req, err := httputil.NewHttpRequest("GET", rel.Href, rel.Header)
	if err != nil {
		return err
	}

	if fromByte > 0 {
		if dlFile == nil || hash == nil {
			return fmt.Errorf("Cannot restart %v from %d without a file & hash", t.Object.Oid, fromByte)
		}
		// We could just use a start byte, but since we know the length be specific
		req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", fromByte, t.Object.Size))
	}

	res, err := httputil.DoHttpRequest(req, true)
	if err != nil {
		// Special-case status code 416 () - fall back
		if fromByte > 0 && dlFile != nil && res.StatusCode == 416 {
			tracerx.Printf("http-range: server rejected resume download request for %q from byte %d; re-downloading from start", t.Object.Oid, fromByte)
			dlFile.Close()
			os.Remove(dlFile.Name())
			return a.download(t, cb, authOkFunc, nil, 0, nil)
		}
		return errutil.NewRetriableError(err)
	}
	httputil.LogTransfer("lfs.data.download", res)
	defer res.Body.Close()

	// Range request must return 206 to confirm
	if fromByte > 0 {
		if res.StatusCode == 206 {
			// Successful range request
			tracerx.Printf("http-range: server accepted resume download request: %q from byte %d", t.Object.Oid, fromByte)
			// Advance progress callback; must split into max int sizes though
			const maxInt = int(^uint(0) >> 1)
			for read := int64(0); read < fromByte; {
				remainder := fromByte - read
				if remainder > int64(maxInt) {
					read += int64(maxInt)
					cb(t.Name, t.Object.Size, read, maxInt)
				} else {
					read += remainder
					cb(t.Name, t.Object.Size, read, int(remainder))
				}

			}
		} else {
			// Abort resume, perform regular download
			tracerx.Printf("http-range: server rejected resume download request for %q from byte %d; re-downloading from start", t.Object.Oid, fromByte)
			dlFile.Close()
			os.Remove(dlFile.Name())
			return a.download(t, cb, authOkFunc, nil, 0, nil)
		}
	}

	// Signal auth OK on success response, before starting download to free up
	// other workers immediately
	if authOkFunc != nil {
		authOkFunc()
	}

	var hasher *tools.HashingReader
	if fromByte > 0 && hash != nil {
		// pre-load hashing reader with previous content
		hasher = tools.NewHashingReaderPreloadHash(res.Body, hash)
	} else {
		hasher = tools.NewHashingReader(res.Body)
	}

	if dlFile == nil {
		// New file start
		dlFile, err = os.OpenFile(a.downloadFilename(t), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
		if err != nil {
			return err
		}
		defer dlFile.Close()
	}
	dlfilename := dlFile.Name()
	// Wrap callback to give name context
	ccb := func(totalSize int64, readSoFar int64, readSinceLast int) error {
		if cb != nil {
			return cb(t.Name, totalSize, readSoFar+fromByte, readSinceLast)
		}
		return nil
	}
	written, err := tools.CopyWithCallback(dlFile, hasher, res.ContentLength, ccb)
	if err != nil {
		return fmt.Errorf("cannot write data to tempfile %q: %v", dlfilename, err)
	}
	if err := dlFile.Close(); err != nil {
		return fmt.Errorf("can't close tempfile %q: %v", dlfilename, err)
	}

	if actual := hasher.Hash(); actual != t.Object.Oid {
		return fmt.Errorf("Expected OID %s, got %s after %d bytes written", t.Object.Oid, actual, written)
	}

	// Notice that on failure we do not delete the partially downloaded file.
	// Instead we will resume next time
	tracerx.Printf("http-range: successfully downloaded bytes %d to %d for %q ", fromByte, t.Object.Size, t.Object.Oid)

	return tools.RenameFileCopyPermissions(dlfilename, t.Path)

}

func init() {
	newfunc := func(name string, dir Direction) TransferAdapter {
		switch dir {
		case Download:
			hd := &httpRangeAdapter{newAdapterBase(name, dir, nil)}
			// self implements impl
			hd.transferImpl = hd
			return hd
		case Upload:
			panic("Should never ask a HTTP Range adapter to upload")
		}
		return nil
	}
	RegisterNewTransferAdapterFunc(HttpRangeAdapterName, Download, newfunc)
}
<commit_msg>Validate Content-Range on 206 range response & diagnose failure details<commit_after>package transfer

import (
	"errors"
	"fmt"
	"hash"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strconv"

	"github.com/github/git-lfs/errutil"
	"github.com/github/git-lfs/httputil"
	"github.com/github/git-lfs/localstorage"
	"github.com/github/git-lfs/tools"
	"github.com/rubyist/tracerx"
)

const (
	HttpRangeAdapterName = "http-range"
)

// Download adapter that can resume downloads using HTTP Range headers
type httpRangeAdapter struct {
	*adapterBase
}

func (a *httpRangeAdapter) ClearTempStorage() error {
	return os.RemoveAll(a.tempDir())
}

func (a *httpRangeAdapter) tempDir() string {
	// Must be dedicated to this adapter as deleted by ClearTempStorage
	// Also make local to this repo not global, and separate to localstorage temp,
	// which gets cleared at the end of every invocation
	d := filepath.Join(localstorage.Objects().RootDir, "incomplete")
	if err := os.MkdirAll(d, 0755); err != nil {
		return os.TempDir()
	}
	return d
}

func (a *httpRangeAdapter) DoTransfer(t *Transfer, cb TransferProgressCallback, authOkFunc func()) error {

	f, fromByte, hashSoFar, err := a.checkResumeDownload(t)
	if err != nil {
		return err
	}
	return a.download(t, cb, authOkFunc, f, fromByte, hashSoFar)
}

// Checks to see if a download can be resumed, and if so returns a non-nil locked file, byte start and hash
func (a *httpRangeAdapter) checkResumeDownload(t *Transfer) (outFile *os.File, fromByte int64, hashSoFar hash.Hash, e error) {
	// lock the file by opening it for read/write, rather than checking Stat() etc
	// which could be subject to race conditions by other processes
	f, err := os.OpenFile(a.downloadFilename(t), os.O_RDWR, 0644)

	if err != nil {
		// Create a new file instead, must not already exist or error (permissions / race condition)
		newfile, err := os.OpenFile(a.downloadFilename(t), os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644)
		return newfile, 0, nil, err
	}

	// Successfully opened an existing file at this point
	// Read any existing data into hash then return file handle at end
	hash := tools.NewLfsContentHash()
	n, err := io.Copy(hash, f)
	if err != nil {
		f.Close()
		return nil, 0, nil, err
	}
	tracerx.Printf("http-range: Attempting to resume download of %q from byte %d", t.Object.Oid, n)
	return f, n, hash, nil

}

// Create or open a download file for resuming
func (a *httpRangeAdapter) downloadFilename(t *Transfer) string {
	// Not a temp file since we will be resuming it
	return filepath.Join(a.tempDir(), t.Object.Oid+".tmp")
}

// download starts or resumes and download. Always closes dlFile if non-nil
func (a *httpRangeAdapter) download(t *Transfer, cb TransferProgressCallback, authOkFunc func(), dlFile *os.File, fromByte int64, hash hash.Hash) error {

	if dlFile != nil {
		// ensure we always close dlFile. Note that this does not conflict with the
		// early close below, as close is idempotent.
		defer dlFile.Close()
	}

	rel, ok := t.Object.Rel("download")
	if !ok {
		return errors.New("Object not found on the server.")
	}

	req, err := httputil.NewHttpRequest("GET", rel.Href, rel.Header)
	if err != nil {
		return err
	}

	if fromByte > 0 {
		if dlFile == nil || hash == nil {
			return fmt.Errorf("Cannot restart %v from %d without a file & hash", t.Object.Oid, fromByte)
		}
		// We could just use a start byte, but since we know the length be specific
		req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", fromByte, t.Object.Size))
	}

	res, err := httputil.DoHttpRequest(req, true)
	if err != nil {
		// Special-case status code 416 () - fall back
		if fromByte > 0 && dlFile != nil && res.StatusCode == 416 {
			tracerx.Printf("http-range: server rejected resume download request for %q from byte %d; re-downloading from start", t.Object.Oid, fromByte)
			dlFile.Close()
			os.Remove(dlFile.Name())
			return a.download(t, cb, authOkFunc, nil, 0, nil)
		}
		return errutil.NewRetriableError(err)
	}
	httputil.LogTransfer("lfs.data.download", res)
	defer res.Body.Close()

	// Range request must return 206 & content range to confirm
	if fromByte > 0 {
		rangeRequestOk := false
		var failReason string
		// check 206 and Content-Range, fall back if either not as expected
		if res.StatusCode == 206 {
			// Probably a successful range request, check Content-Range
			if rangeHdr := res.Header.Get("Content-Range"); rangeHdr != "" {
				regex := regexp.MustCompile(`bytes=(\d+)\-.*`)
				match := regex.FindStringSubmatch(rangeHdr)
				if match != nil && len(match) > 1 {
					contentStart, _ := strconv.ParseInt(match[1], 10, 32)
					if contentStart == fromByte {
						rangeRequestOk = true
					} else {
						failReason = fmt.Sprintf("Content-Range start byte incorrect: %s expected %d", match[1], fromByte)
					}
				} else {
					failReason = fmt.Sprintf("badly formatted Content-Range header: %q", rangeHdr)
				}
			} else {
				failReason = "missing Content-Range header in response"
			}
		} else {
			failReason = fmt.Sprintf("expected status code 206, received %d", res.StatusCode)
		}
		if rangeRequestOk {
			tracerx.Printf("http-range: server accepted resume download request: %q from byte %d", t.Object.Oid, fromByte)
			// Advance progress callback; must split into max int sizes though
			const maxInt = int(^uint(0) >> 1)
			for read := int64(0); read < fromByte; {
				remainder := fromByte - read
				if remainder > int64(maxInt) {
					read += int64(maxInt)
					cb(t.Name, t.Object.Size, read, maxInt)
				} else {
					read += remainder
					cb(t.Name, t.Object.Size, read, int(remainder))
				}

			}
		} else {
			// Abort resume, perform regular download
			tracerx.Printf("http-range: failed to resume download for %q from byte %d: %s. Re-downloading from start", t.Object.Oid, fromByte, failReason)
			dlFile.Close()
			os.Remove(dlFile.Name())
			return a.download(t, cb, authOkFunc, nil, 0, nil)
		}
	}

	// Signal auth OK on success response, before starting download to free up
	// other workers immediately
	if authOkFunc != nil {
		authOkFunc()
	}

	var hasher *tools.HashingReader
	if fromByte > 0 && hash != nil {
		// pre-load hashing reader with previous content
		hasher = tools.NewHashingReaderPreloadHash(res.Body, hash)
	} else {
		hasher = tools.NewHashingReader(res.Body)
	}

	if dlFile == nil {
		// New file start
		dlFile, err = os.OpenFile(a.downloadFilename(t), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
		if err != nil {
			return err
		}
		defer dlFile.Close()
	}
	dlfilename := dlFile.Name()
	// Wrap callback to give name context
	ccb := func(totalSize int64, readSoFar int64, readSinceLast int) error {
		if cb != nil {
			return cb(t.Name, totalSize, readSoFar+fromByte, readSinceLast)
		}
		return nil
	}
	written, err := tools.CopyWithCallback(dlFile, hasher, res.ContentLength, ccb)
	if err != nil {
		return fmt.Errorf("cannot write data to tempfile %q: %v", dlfilename, err)
	}
	if err := dlFile.Close(); err != nil {
		return fmt.Errorf("can't close tempfile %q: %v", dlfilename, err)
	}

	if actual := hasher.Hash(); actual != t.Object.Oid {
		return fmt.Errorf("Expected OID %s, got %s after %d bytes written", t.Object.Oid, actual, written)
	}

	// Notice that on failure we do not delete the partially downloaded file.
	// Instead we will resume next time
	tracerx.Printf("http-range: successfully downloaded bytes %d to %d for %q ", fromByte, t.Object.Size, t.Object.Oid)

	return tools.RenameFileCopyPermissions(dlfilename, t.Path)

}

func init() {
	newfunc := func(name string, dir Direction) TransferAdapter {
		switch dir {
		case Download:
			hd := &httpRangeAdapter{newAdapterBase(name, dir, nil)}
			// self implements impl
			hd.transferImpl = hd
			return hd
		case Upload:
			panic("Should never ask a HTTP Range adapter to upload")
		}
		return nil
	}
	RegisterNewTransferAdapterFunc(HttpRangeAdapterName, Download, newfunc)
}
<|endoftext|>


<commit_before>// Copyright (c) 2018-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package txscript

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"testing"

	"github.com/decred/dcrd/wire"
)

var (
	// manyInputsBenchTx is a transaction that contains a lot of inputs which is
	// useful for benchmarking signature hash calculation.
	manyInputsBenchTx wire.MsgTx

	// A mock previous output script to use in the signing benchmark.
	prevOutScript = hexToBytes("a914f5916158e3e2c4551c1796708db8367207ed13bb87")
)

func init() {
	// tx 620f57c92cf05a7f7e7f7d28255d5f7089437bc48e34dcfebf7751d08b7fb8f5
	txHex, err := ioutil.ReadFile("data/many_inputs_tx.hex")
	if err != nil {
		panic(fmt.Sprintf("unable to read benchmark tx file: %v", err))
	}

	txBytes := hexToBytes(string(txHex))
	err = manyInputsBenchTx.Deserialize(bytes.NewReader(txBytes))
	if err != nil {
		panic(err)
	}
}

// BenchmarkCalcSigHash benchmarks how long it takes to calculate the signature
// hashes for all inputs of a transaction with many inputs.
func BenchmarkCalcSigHash(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for j := 0; j < len(manyInputsBenchTx.TxIn); j++ {
			_, err := CalcSignatureHash(prevOutScript, SigHashAll,
				&manyInputsBenchTx, j, nil)
			if err != nil {
				b.Fatalf("failed to calc signature hash: %v", err)
			}
		}
	}
}

// genComplexScript returns a script comprised of half as many opcodes as the
// maximum allowed followed by as many max size data pushes fit without
// exceeding the max allowed script size.
func genComplexScript() ([]byte, error) {
	var scriptLen int
	builder := NewScriptBuilder()
	for i := 0; i < MaxOpsPerScript/2; i++ {
		builder.AddOp(OP_TRUE)
		scriptLen++
	}
	maxData := bytes.Repeat([]byte{0x02}, MaxScriptElementSize)
	for i := 0; i < (MaxScriptSize-scriptLen)/MaxScriptElementSize; i++ {
		builder.AddData(maxData)
	}
	return builder.Script()
}

// BenchmarkScriptParsing benchmarks how long it takes to parse a very large
// script.
func BenchmarkScriptParsing(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	const scriptVersion = 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tokenizer := MakeScriptTokenizer(scriptVersion, script)
		for tokenizer.Next() {
			_ = tokenizer.Opcode()
			_ = tokenizer.Data()
			_ = tokenizer.ByteIndex()
		}
		if err := tokenizer.Err(); err != nil {
			b.Fatalf("failed to parse script: %v", err)
		}
	}
}

// BenchmarkDisasmString benchmarks how long it takes to disassemble a very
// large script.
func BenchmarkDisasmString(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := DisasmString(script)
		if err != nil {
			b.Fatalf("failed to disasm script: %v", err)
		}
	}
}

// BenchmarkIsPayToScriptHash benchmarks how long it takes IsPayToScriptHash to
// analyze a very large script.
func BenchmarkIsPayToScriptHash(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = IsPayToScriptHash(script)
	}
}

// BenchmarkIsMultisigScriptLarge benchmarks how long it takes IsMultisigScript
// to analyze a very large script.
func BenchmarkIsMultisigScriptLarge(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		isMultisig, err := IsMultisigScript(script)
		if err != nil {
			b.Fatalf("unexpected err: %v", err)
		}
		if isMultisig {
			b.Fatalf("script should NOT be reported as mutisig script")
		}
	}
}

// BenchmarkIsMultisigScript benchmarks how long it takes IsMultisigScript to
// analyze a 1-of-2 multisig public key script.
func BenchmarkIsMultisigScript(b *testing.B) {
	multisigShortForm := "1 " +
		"DATA_33 " +
		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
		"DATA_33 " +
		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
		"2 CHECKMULTISIG"
	pkScript := mustParseShortForm(multisigShortForm)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		isMultisig, err := IsMultisigScript(pkScript)
		if err != nil {
			b.Fatalf("unexpected err: %v", err)
		}
		if !isMultisig {
			b.Fatalf("script should be reported as a mutisig script")
		}
	}
}

// BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
// to analyze a very large script.
func BenchmarkIsMultisigSigScriptLarge(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if IsMultisigSigScript(script) {
			b.Fatalf("script should NOT be reported as mutisig sig script")
		}
	}
}

// BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
// to analyze both a 1-of-2 multisig public key script (which should be false)
// and a signature script comprised of a pay-to-script-hash 1-of-2 multisig
// redeem script (which should be true).
func BenchmarkIsMultisigSigScript(b *testing.B) {
	multisigShortForm := "1 " +
		"DATA_33 " +
		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
		"DATA_33 " +
		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
		"2 CHECKMULTISIG"
	pkScript := mustParseShortForm(multisigShortForm)

	sigHex := "0x304402205795c3ab6ba11331eeac757bf1fc9c34bef0c7e1a9c8bd5eebb8" +
		"82f3b79c5838022001e0ab7b4c7662e4522dc5fa479e4b4133fa88c6a53d895dc1d5" +
		"2eddc7bbcf2801 "
	sigScript := mustParseShortForm("DATA_71 " + sigHex + "DATA_71 " +
		multisigShortForm)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if IsMultisigSigScript(pkScript) {
			b.Fatalf("script should NOT be reported as mutisig sig script")
		}
		if !IsMultisigSigScript(sigScript) {
			b.Fatalf("script should be reported as a mutisig sig script")
		}
	}
}

// BenchmarkGetSigOpCount benchmarks how long it takes to count the signature
// operations of a very large script.
func BenchmarkGetSigOpCount(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = GetSigOpCount(script)
	}
}

// BenchmarkIsAnyKindOfScriptHash benchmarks how long it takes to
// isAnyKindOfScriptHash to analyze operations of a very large script.
func BenchmarkIsAnyKindOfScriptHash(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = isAnyKindOfScriptHash(script)
	}
}

// BenchmarkIsPushOnlyScript benchmarks how long it takes IsPushOnlyScript to
// analyze a very large script.
func BenchmarkIsPushOnlyScript(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = IsPushOnlyScript(script)
	}
}
<commit_msg>txscript: Add benchmark for GetPreciseSigOpCount.<commit_after>// Copyright (c) 2018-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package txscript

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"testing"

	"github.com/decred/dcrd/wire"
)

var (
	// manyInputsBenchTx is a transaction that contains a lot of inputs which is
	// useful for benchmarking signature hash calculation.
	manyInputsBenchTx wire.MsgTx

	// A mock previous output script to use in the signing benchmark.
	prevOutScript = hexToBytes("a914f5916158e3e2c4551c1796708db8367207ed13bb87")
)

func init() {
	// tx 620f57c92cf05a7f7e7f7d28255d5f7089437bc48e34dcfebf7751d08b7fb8f5
	txHex, err := ioutil.ReadFile("data/many_inputs_tx.hex")
	if err != nil {
		panic(fmt.Sprintf("unable to read benchmark tx file: %v", err))
	}

	txBytes := hexToBytes(string(txHex))
	err = manyInputsBenchTx.Deserialize(bytes.NewReader(txBytes))
	if err != nil {
		panic(err)
	}
}

// BenchmarkCalcSigHash benchmarks how long it takes to calculate the signature
// hashes for all inputs of a transaction with many inputs.
func BenchmarkCalcSigHash(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for j := 0; j < len(manyInputsBenchTx.TxIn); j++ {
			_, err := CalcSignatureHash(prevOutScript, SigHashAll,
				&manyInputsBenchTx, j, nil)
			if err != nil {
				b.Fatalf("failed to calc signature hash: %v", err)
			}
		}
	}
}

// genComplexScript returns a script comprised of half as many opcodes as the
// maximum allowed followed by as many max size data pushes fit without
// exceeding the max allowed script size.
func genComplexScript() ([]byte, error) {
	var scriptLen int
	builder := NewScriptBuilder()
	for i := 0; i < MaxOpsPerScript/2; i++ {
		builder.AddOp(OP_TRUE)
		scriptLen++
	}
	maxData := bytes.Repeat([]byte{0x02}, MaxScriptElementSize)
	for i := 0; i < (MaxScriptSize-scriptLen)/MaxScriptElementSize; i++ {
		builder.AddData(maxData)
	}
	return builder.Script()
}

// BenchmarkScriptParsing benchmarks how long it takes to parse a very large
// script.
func BenchmarkScriptParsing(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	const scriptVersion = 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tokenizer := MakeScriptTokenizer(scriptVersion, script)
		for tokenizer.Next() {
			_ = tokenizer.Opcode()
			_ = tokenizer.Data()
			_ = tokenizer.ByteIndex()
		}
		if err := tokenizer.Err(); err != nil {
			b.Fatalf("failed to parse script: %v", err)
		}
	}
}

// BenchmarkDisasmString benchmarks how long it takes to disassemble a very
// large script.
func BenchmarkDisasmString(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := DisasmString(script)
		if err != nil {
			b.Fatalf("failed to disasm script: %v", err)
		}
	}
}

// BenchmarkIsPayToScriptHash benchmarks how long it takes IsPayToScriptHash to
// analyze a very large script.
func BenchmarkIsPayToScriptHash(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = IsPayToScriptHash(script)
	}
}

// BenchmarkIsMultisigScriptLarge benchmarks how long it takes IsMultisigScript
// to analyze a very large script.
func BenchmarkIsMultisigScriptLarge(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		isMultisig, err := IsMultisigScript(script)
		if err != nil {
			b.Fatalf("unexpected err: %v", err)
		}
		if isMultisig {
			b.Fatalf("script should NOT be reported as mutisig script")
		}
	}
}

// BenchmarkIsMultisigScript benchmarks how long it takes IsMultisigScript to
// analyze a 1-of-2 multisig public key script.
func BenchmarkIsMultisigScript(b *testing.B) {
	multisigShortForm := "1 " +
		"DATA_33 " +
		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
		"DATA_33 " +
		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
		"2 CHECKMULTISIG"
	pkScript := mustParseShortForm(multisigShortForm)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		isMultisig, err := IsMultisigScript(pkScript)
		if err != nil {
			b.Fatalf("unexpected err: %v", err)
		}
		if !isMultisig {
			b.Fatalf("script should be reported as a mutisig script")
		}
	}
}

// BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
// to analyze a very large script.
func BenchmarkIsMultisigSigScriptLarge(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if IsMultisigSigScript(script) {
			b.Fatalf("script should NOT be reported as mutisig sig script")
		}
	}
}

// BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
// to analyze both a 1-of-2 multisig public key script (which should be false)
// and a signature script comprised of a pay-to-script-hash 1-of-2 multisig
// redeem script (which should be true).
func BenchmarkIsMultisigSigScript(b *testing.B) {
	multisigShortForm := "1 " +
		"DATA_33 " +
		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
		"DATA_33 " +
		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
		"2 CHECKMULTISIG"
	pkScript := mustParseShortForm(multisigShortForm)

	sigHex := "0x304402205795c3ab6ba11331eeac757bf1fc9c34bef0c7e1a9c8bd5eebb8" +
		"82f3b79c5838022001e0ab7b4c7662e4522dc5fa479e4b4133fa88c6a53d895dc1d5" +
		"2eddc7bbcf2801 "
	sigScript := mustParseShortForm("DATA_71 " + sigHex + "DATA_71 " +
		multisigShortForm)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if IsMultisigSigScript(pkScript) {
			b.Fatalf("script should NOT be reported as mutisig sig script")
		}
		if !IsMultisigSigScript(sigScript) {
			b.Fatalf("script should be reported as a mutisig sig script")
		}
	}
}

// BenchmarkGetSigOpCount benchmarks how long it takes to count the signature
// operations of a very large script.
func BenchmarkGetSigOpCount(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = GetSigOpCount(script)
	}
}

// BenchmarkGetPreciseSigOpCount benchmarks how long it takes to count the
// signature operations of a very large script using the more precise counting
// method.
func BenchmarkGetPreciseSigOpCount(b *testing.B) {
	redeemScript, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	// Create a fake pay-to-script-hash to pass the necessary checks and create
	// the signature script accordingly by pushing the generated "redeem" script
	// as the final data push so the benchmark will cover the p2sh path.
	scriptHash := "0x0000000000000000000000000000000000000001"
	pkScript := mustParseShortForm("HASH160 DATA_20 " + scriptHash + " EQUAL")
	sigScript, err := NewScriptBuilder().AddFullData(redeemScript).Script()
	if err != nil {
		b.Fatalf("failed to create signature script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = GetPreciseSigOpCount(sigScript, pkScript, true)
	}
}

// BenchmarkIsAnyKindOfScriptHash benchmarks how long it takes to
// isAnyKindOfScriptHash to analyze operations of a very large script.
func BenchmarkIsAnyKindOfScriptHash(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = isAnyKindOfScriptHash(script)
	}
}

// BenchmarkIsPushOnlyScript benchmarks how long it takes IsPushOnlyScript to
// analyze a very large script.
func BenchmarkIsPushOnlyScript(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = IsPushOnlyScript(script)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"runtime/debug"
	"strings"
	"time"
)

type rCommand struct {
	command   *exec.Cmd
	key       string
	output    string
	startTime int64
	endTime   int64
	err       error
	mainOut   string
	status    string
	crash1    bool
	crash2    bool
	base      string
	block     bool
	comp      chan bool
}

//Scheduler the main task scheduler
type Scheduler struct {
	Log              func(string)
	blockingQueue    chan *rCommand
	nonblockingQueue chan *rCommand
	complete         []*rCommand
}

func (s *Scheduler) getState(key string) string {
	for _, c := range s.complete {
		if c.key == key {
			return fmt.Sprintf("%v -> %v", c.endTime, c.output)
		}
	}

	return "UNKNOWN"
}

// Schedule schedules a task
func (s *Scheduler) Schedule(c *rCommand) string {
	debug.PrintStack()
	fmt.Printf("Scheduling: %v\n", c.command.Path)
	key := fmt.Sprintf("%v", time.Now().UnixNano())
	s.complete = append(s.complete, c)
	c.status = "InQueue"
	if c.block {
		s.blockingQueue <- c
	} else {
		s.nonblockingQueue <- c
	}
	return key
}

func (s *Scheduler) getOutput(key string) (string, error) {
	for _, c := range s.complete {
		if c.key == key {
			return c.output, nil
		}
	}

	return key, fmt.Errorf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) getErrOutput(key string) (string, error) {
	for _, c := range s.complete {
		if c.key == key {
			return c.mainOut, nil
		}
	}

	return "", fmt.Errorf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) wait(key string) {
	for _, c := range s.complete {
		if c.key == key {
			<-c.comp
			return
		}
	}
	fmt.Printf("Wait Failed for %v with %v\n", key, len(s.complete))
}

func (s *Scheduler) getStatus(key string) string {
	for _, val := range s.complete {
		if val.key == key {
			return val.status
		}
	}

	return fmt.Sprintf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) killJob(key string) {
	for _, val := range s.complete {
		if val.key == key {
			if val.command.Process != nil {
				val.command.Process.Kill()
				val.command.Process.Wait()
			}
		}
	}
}

func (s *Scheduler) processBlockingCommands() {
	for c := range s.blockingQueue {
		err := run(c)
		if err != nil {
			fmt.Printf("Command failure: %v", err)
			c.endTime = time.Now().Unix()
		}
	}
}

func (s *Scheduler) processNonblockingCommands() {
	for c := range s.nonblockingQueue {
		err := run(c)
		if err != nil {
			fmt.Printf("Command failure: %v", err)
			c.endTime = time.Now().Unix()
		}
	}
}

func run(c *rCommand) error {
	c.status = "Running"
	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	gpath := home + "/gobuild"
	c.command.Path = strings.Replace(c.command.Path, "$GOPATH", gpath, -1)
	for i := range c.command.Args {
		c.command.Args[i] = strings.Replace(c.command.Args[i], "$GOPATH", gpath, -1)
	}
	path := fmt.Sprintf("GOPATH=" + home + "/gobuild")
	pathbin := fmt.Sprintf("GOBIN=" + home + "/gobuild/bin")
	found := false
	for i, blah := range env {
		if strings.HasPrefix(blah, "GOPATH") {
			env[i] = path
			found = true
		}
		if strings.HasPrefix(blah, "GOBIN") {
			env[i] = pathbin
			found = true
		}
	}
	if !found {
		env = append(env, path)
	}
	c.command.Env = env

	out, err1 := c.command.StderrPipe()
	outr, err2 := c.command.StdoutPipe()

	if c.crash1 || err1 != nil {
		return err1
	}

	if c.crash2 || err2 != nil {
		return err2
	}

	scanner := bufio.NewScanner(out)
	go func() {
		for scanner != nil && scanner.Scan() {
			c.output += scanner.Text()
		}
		out.Close()
	}()

	scanner2 := bufio.NewScanner(outr)
	go func() {
		for scanner2 != nil && scanner2.Scan() {
			c.mainOut += scanner2.Text()
		}
		outr.Close()
	}()

	c.status = "StartCommand"
	err := c.command.Start()
	if err != nil {
		return err
	}
	c.startTime = time.Now().Unix()

	// Monitor the job and report completion
	r := func() {
		c.status = "Entering Wait"
		err := c.command.Wait()
		c.status = "Completed Wait"
		if err != nil {
			c.err = err
		}
		c.endTime = time.Now().Unix()
		c.comp <- true
	}

	if c.block {
		r()
	} else {
		go r()
	}

	return nil
}
<commit_msg>Mark key in command<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"runtime/debug"
	"strings"
	"time"
)

type rCommand struct {
	command   *exec.Cmd
	key       string
	output    string
	startTime int64
	endTime   int64
	err       error
	mainOut   string
	status    string
	crash1    bool
	crash2    bool
	base      string
	block     bool
	comp      chan bool
}

//Scheduler the main task scheduler
type Scheduler struct {
	Log              func(string)
	blockingQueue    chan *rCommand
	nonblockingQueue chan *rCommand
	complete         []*rCommand
}

func (s *Scheduler) getState(key string) string {
	for _, c := range s.complete {
		if c.key == key {
			return fmt.Sprintf("%v -> %v", c.endTime, c.output)
		}
	}

	return "UNKNOWN"
}

// Schedule schedules a task
func (s *Scheduler) Schedule(c *rCommand) string {
	debug.PrintStack()
	fmt.Printf("Scheduling: %v\n", c.command.Path)
	key := fmt.Sprintf("%v", time.Now().UnixNano())
	s.complete = append(s.complete, c)
	c.status = "InQueue"
	c.key = key
	if c.block {
		s.blockingQueue <- c
	} else {
		s.nonblockingQueue <- c
	}
	return key
}

func (s *Scheduler) getOutput(key string) (string, error) {
	for _, c := range s.complete {
		if c.key == key {
			return c.output, nil
		}
	}

	return key, fmt.Errorf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) getErrOutput(key string) (string, error) {
	for _, c := range s.complete {
		if c.key == key {
			return c.mainOut, nil
		}
	}

	return "", fmt.Errorf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) wait(key string) {
	for _, c := range s.complete {
		if c.key == key {
			<-c.comp
			return
		}
	}
	fmt.Printf("Wait Failed for %v with %v\n", key, len(s.complete))
}

func (s *Scheduler) getStatus(key string) string {
	for _, val := range s.complete {
		if val.key == key {
			return val.status
		}
	}

	return fmt.Sprintf("KEY NOT_IN_MAP: %v", key)
}

func (s *Scheduler) killJob(key string) {
	for _, val := range s.complete {
		if val.key == key {
			if val.command.Process != nil {
				val.command.Process.Kill()
				val.command.Process.Wait()
			}
		}
	}
}

func (s *Scheduler) processBlockingCommands() {
	for c := range s.blockingQueue {
		err := run(c)
		if err != nil {
			fmt.Printf("Command failure: %v", err)
			c.endTime = time.Now().Unix()
		}
	}
}

func (s *Scheduler) processNonblockingCommands() {
	for c := range s.nonblockingQueue {
		err := run(c)
		if err != nil {
			fmt.Printf("Command failure: %v", err)
			c.endTime = time.Now().Unix()
		}
	}
}

func run(c *rCommand) error {
	c.status = "Running"
	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	gpath := home + "/gobuild"
	c.command.Path = strings.Replace(c.command.Path, "$GOPATH", gpath, -1)
	for i := range c.command.Args {
		c.command.Args[i] = strings.Replace(c.command.Args[i], "$GOPATH", gpath, -1)
	}
	path := fmt.Sprintf("GOPATH=" + home + "/gobuild")
	pathbin := fmt.Sprintf("GOBIN=" + home + "/gobuild/bin")
	found := false
	for i, blah := range env {
		if strings.HasPrefix(blah, "GOPATH") {
			env[i] = path
			found = true
		}
		if strings.HasPrefix(blah, "GOBIN") {
			env[i] = pathbin
			found = true
		}
	}
	if !found {
		env = append(env, path)
	}
	c.command.Env = env

	out, err1 := c.command.StderrPipe()
	outr, err2 := c.command.StdoutPipe()

	if c.crash1 || err1 != nil {
		return err1
	}

	if c.crash2 || err2 != nil {
		return err2
	}

	scanner := bufio.NewScanner(out)
	go func() {
		for scanner != nil && scanner.Scan() {
			c.output += scanner.Text()
		}
		out.Close()
	}()

	scanner2 := bufio.NewScanner(outr)
	go func() {
		for scanner2 != nil && scanner2.Scan() {
			c.mainOut += scanner2.Text()
		}
		outr.Close()
	}()

	c.status = "StartCommand"
	err := c.command.Start()
	if err != nil {
		return err
	}
	c.startTime = time.Now().Unix()

	// Monitor the job and report completion
	r := func() {
		c.status = "Entering Wait"
		err := c.command.Wait()
		c.status = "Completed Wait"
		if err != nil {
			c.err = err
		}
		c.endTime = time.Now().Unix()
		c.comp <- true
	}

	if c.block {
		r()
	} else {
		go r()
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten_test

import (
	. "github.com/hajimehoshi/ebiten"
	"image"
	"image/color"
	"image/draw"
	_ "image/png"
	"math"
	"testing"
)

var ebitenImageBin = ""

func openImage(path string) (image.Image, error) {
	file, err := readFile(path)
	if err != nil {
		return nil, err
	}

	img, _, err := image.Decode(file)
	if err != nil {
		return nil, err
	}
	return img, nil
}

func openEbitenImage(path string) (*Image, image.Image, error) {
	img, err := openImage(path)
	if err != nil {
		return nil, nil, err
	}

	eimg, err := NewImageFromImage(img, FilterNearest)
	if err != nil {
		return nil, nil, err
	}
	return eimg, img, nil
}

func diff(x, y uint8) uint8 {
	if x <= y {
		return y - x
	}
	return x - y
}

func TestImagePixels(t *testing.T) {
	img0, img, err := openEbitenImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}

	if got := img0.Bounds().Size(); got != img.Bounds().Size() {
		t.Errorf("img size: got %d; want %d", got, img.Bounds().Size())
	}

	for j := 0; j < img0.Bounds().Size().Y; j++ {
		for i := 0; i < img0.Bounds().Size().X; i++ {
			got := img0.At(i, j)
			want := color.RGBAModel.Convert(img.At(i, j))
			if got != want {
				t.Errorf("img0 At(%d, %d): got %#v; want %#v", i, j, got, want)
			}
		}
	}
}

func TestImageComposition(t *testing.T) {
	img2Color := color.NRGBA{0x24, 0x3f, 0x6a, 0x88}
	img3Color := color.NRGBA{0x85, 0xa3, 0x08, 0xd3}

	img1, _, err := openEbitenImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}

	w, h := img1.Bounds().Size().X, img1.Bounds().Size().Y

	img2, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}

	img3, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}

	img2.Fill(img2Color)
	img3.Fill(img3Color)
	img_12_3, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}
	img2.DrawImage(img1, nil)
	img3.DrawImage(img2, nil)
	img_12_3.DrawImage(img3, nil)

	img2.Fill(img2Color)
	img3.Fill(img3Color)
	img_1_23, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}
	img3.DrawImage(img2, nil)
	img3.DrawImage(img1, nil)
	img_1_23.DrawImage(img3, nil)

	for j := 0; j < h; j++ {
		for i := 0; i < w; i++ {
			c1 := img_12_3.At(i, j).(color.RGBA)
			c2 := img_1_23.At(i, j).(color.RGBA)
			if 1 < diff(c1.R, c2.R) || 1 < diff(c1.G, c2.G) || 1 < diff(c1.B, c2.B) || 1 < diff(c1.A, c2.A) {
				t.Errorf("img_12_3.At(%d, %d) = %#v; img_1_23.At(%[1]d, %[2]d) = %#[4]v", i, j, c1, c2)
			}
			if c1.A == 0 {
				t.Fatalf("img_12_3.At(%d, %d).A = 0; nothing is rendered?", i, j)
			}
			if c2.A == 0 {
				t.Fatalf("img_1_23.At(%d, %d).A = 0; nothing is rendered?", i, j)
			}
		}
	}
}

func TestImageSelf(t *testing.T) {
	img, _, err := openEbitenImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}
	if err := img.DrawImage(img, nil); err == nil {
		t.Fatalf("img.DrawImage(img, nil) doesn't return error; an error should be returned")
	}
}

func TestImageDotByDotInversion(t *testing.T) {
	img0, _, err := openEbitenImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}
	w, h := img0.Size()
	img1, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}
	op := &DrawImageOptions{}
	op.GeoM.Rotate(math.Pi)
	op.GeoM.Translate(float64(w), float64(h))
	img1.DrawImage(img0, op)

	for j := 0; j < h; j++ {
		for i := 0; i < w; i++ {
			c0 := img0.At(i, j).(color.RGBA)
			c1 := img1.At(w-i-1, h-j-1).(color.RGBA)
			if c0 != c1 {
				t.Errorf("img0.At(%[1]d, %[2]d) should equal to img1.At(%[3]d, %[4]d) but not: %[5]v vs %[6]v", i, j, w-i-1, h-j-1, c0, c1)
			}
		}
	}
}

func TestReplacePixels(t *testing.T) {
	origImg, err := openImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}
	// Convert to RGBA
	img := image.NewRGBA(origImg.Bounds())
	draw.Draw(img, img.Bounds(), origImg, image.ZP, draw.Src)

	size := img.Bounds().Size()
	img0, err := NewImage(size.X, size.Y, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}

	img0.ReplacePixels(img.Pix)
	for j := 0; j < img0.Bounds().Size().Y; j++ {
		for i := 0; i < img0.Bounds().Size().X; i++ {
			got := img0.At(i, j)
			want := img.At(i, j)
			if got != want {
				t.Errorf("img0 At(%d, %d): got %#v; want %#v", i, j, got, want)
			}
		}
	}

	p := make([]uint8, 4*size.X*size.Y)
	for i, _ := range p {
		p[i] = 0x80
	}
	img0.ReplacePixels(p)
	for j := 0; j < img0.Bounds().Size().Y; j++ {
		for i := 0; i < img0.Bounds().Size().X; i++ {
			got := img0.At(i, j)
			want := color.RGBA{p[4*i], p[4*i+1], p[4*i+2], p[4*i+3]}
			if got != want {
				t.Errorf("img0 At(%d, %d): got %#v; want %#v", i, j, got, want)
			}
		}
	}
}

// TODO: Add more tests (e.g. DrawImage with color matrix)
<commit_msg>image: Add TestImageDispose<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten_test

import (
	. "github.com/hajimehoshi/ebiten"
	"image"
	"image/color"
	"image/draw"
	_ "image/png"
	"math"
	"testing"
)

var ebitenImageBin = ""

func openImage(path string) (image.Image, error) {
	file, err := readFile(path)
	if err != nil {
		return nil, err
	}

	img, _, err := image.Decode(file)
	if err != nil {
		return nil, err
	}
	return img, nil
}

func openEbitenImage(path string) (*Image, image.Image, error) {
	img, err := openImage(path)
	if err != nil {
		return nil, nil, err
	}

	eimg, err := NewImageFromImage(img, FilterNearest)
	if err != nil {
		return nil, nil, err
	}
	return eimg, img, nil
}

func diff(x, y uint8) uint8 {
	if x <= y {
		return y - x
	}
	return x - y
}

func TestImagePixels(t *testing.T) {
	img0, img, err := openEbitenImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}

	if got := img0.Bounds().Size(); got != img.Bounds().Size() {
		t.Errorf("img size: got %d; want %d", got, img.Bounds().Size())
	}

	for j := 0; j < img0.Bounds().Size().Y; j++ {
		for i := 0; i < img0.Bounds().Size().X; i++ {
			got := img0.At(i, j)
			want := color.RGBAModel.Convert(img.At(i, j))
			if got != want {
				t.Errorf("img0 At(%d, %d): got %#v; want %#v", i, j, got, want)
			}
		}
	}
}

func TestImageComposition(t *testing.T) {
	img2Color := color.NRGBA{0x24, 0x3f, 0x6a, 0x88}
	img3Color := color.NRGBA{0x85, 0xa3, 0x08, 0xd3}

	img1, _, err := openEbitenImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}

	w, h := img1.Bounds().Size().X, img1.Bounds().Size().Y

	img2, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}

	img3, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}

	img2.Fill(img2Color)
	img3.Fill(img3Color)
	img_12_3, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}
	img2.DrawImage(img1, nil)
	img3.DrawImage(img2, nil)
	img_12_3.DrawImage(img3, nil)

	img2.Fill(img2Color)
	img3.Fill(img3Color)
	img_1_23, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}
	img3.DrawImage(img2, nil)
	img3.DrawImage(img1, nil)
	img_1_23.DrawImage(img3, nil)

	for j := 0; j < h; j++ {
		for i := 0; i < w; i++ {
			c1 := img_12_3.At(i, j).(color.RGBA)
			c2 := img_1_23.At(i, j).(color.RGBA)
			if 1 < diff(c1.R, c2.R) || 1 < diff(c1.G, c2.G) || 1 < diff(c1.B, c2.B) || 1 < diff(c1.A, c2.A) {
				t.Errorf("img_12_3.At(%d, %d) = %#v; img_1_23.At(%[1]d, %[2]d) = %#[4]v", i, j, c1, c2)
			}
			if c1.A == 0 {
				t.Fatalf("img_12_3.At(%d, %d).A = 0; nothing is rendered?", i, j)
			}
			if c2.A == 0 {
				t.Fatalf("img_1_23.At(%d, %d).A = 0; nothing is rendered?", i, j)
			}
		}
	}
}

func TestImageSelf(t *testing.T) {
	img, _, err := openEbitenImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}
	if err := img.DrawImage(img, nil); err == nil {
		t.Fatalf("img.DrawImage(img, nil) doesn't return error; an error should be returned")
	}
}

func TestImageDotByDotInversion(t *testing.T) {
	img0, _, err := openEbitenImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}
	w, h := img0.Size()
	img1, err := NewImage(w, h, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}
	op := &DrawImageOptions{}
	op.GeoM.Rotate(math.Pi)
	op.GeoM.Translate(float64(w), float64(h))
	img1.DrawImage(img0, op)

	for j := 0; j < h; j++ {
		for i := 0; i < w; i++ {
			c0 := img0.At(i, j).(color.RGBA)
			c1 := img1.At(w-i-1, h-j-1).(color.RGBA)
			if c0 != c1 {
				t.Errorf("img0.At(%[1]d, %[2]d) should equal to img1.At(%[3]d, %[4]d) but not: %[5]v vs %[6]v", i, j, w-i-1, h-j-1, c0, c1)
			}
		}
	}
}

func TestReplacePixels(t *testing.T) {
	origImg, err := openImage("testdata/ebiten.png")
	if err != nil {
		t.Fatal(err)
		return
	}
	// Convert to RGBA
	img := image.NewRGBA(origImg.Bounds())
	draw.Draw(img, img.Bounds(), origImg, image.ZP, draw.Src)

	size := img.Bounds().Size()
	img0, err := NewImage(size.X, size.Y, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}

	img0.ReplacePixels(img.Pix)
	for j := 0; j < img0.Bounds().Size().Y; j++ {
		for i := 0; i < img0.Bounds().Size().X; i++ {
			got := img0.At(i, j)
			want := img.At(i, j)
			if got != want {
				t.Errorf("img0 At(%d, %d): got %#v; want %#v", i, j, got, want)
			}
		}
	}

	p := make([]uint8, 4*size.X*size.Y)
	for i, _ := range p {
		p[i] = 0x80
	}
	img0.ReplacePixels(p)
	for j := 0; j < img0.Bounds().Size().Y; j++ {
		for i := 0; i < img0.Bounds().Size().X; i++ {
			got := img0.At(i, j)
			want := color.RGBA{p[4*i], p[4*i+1], p[4*i+2], p[4*i+3]}
			if got != want {
				t.Errorf("img0 At(%d, %d): got %#v; want %#v", i, j, got, want)
			}
		}
	}
}

func TestImageDispose(t *testing.T) {
	img, err := NewImage(16, 16, FilterNearest)
	if err != nil {
		t.Fatal(err)
		return
	}
	if err := img.Dispose(); err != nil {
		t.Errorf("img.Dipose() returns error: %v", err)
	}
}

// TODO: Add more tests (e.g. DrawImage with color matrix)
<|endoftext|>