<commit_before>package geometry

import (
	"errors"
	"math"
)

/* Thanks to David Calhoun for providing round and toFixed!
http://stackoverflow.com/questions/18390266/how-can-we-truncate-float64-type-to-a-particular-precision-in-golang
*/
func round(num float64) int {
	return int(num + math.Copysign(0.5, num))
}

func toFixed(num float64, precision int) float64 {
	output := math.Pow(10, float64(precision))
	return float64(round(num*output)) / output
}

// Vector : Any sized dimension representation of a point of vector space.
type Vector []float64

// VectorEquality – Trying to implement a quick version of checking for Vector equality
type VectorEquality interface {
	Equal(VectorEquality) bool
}

// Equal method implements an Equality comparison between vectors.
func (v Vector) Equal(u VectorEquality) bool {
	if len(v) != len(u.(Vector)) {
		return false
	}
	for i := 0; i < len(v); i++ {
		if v[i] != u.(Vector)[i] {
			return false
		}
	}
	return true
}

/*
ColRGB stores a standard 8-bit per channel Red Green Blue colour
representation. Part of pkg geometry colour lives in a form of
vector space.
*/
type ColRGB struct {
	red   byte
	green byte
	blue  byte
}

// just providing a conventional element naming system for
// 2D and 3D vectors.
// this 'aliasing' is just local to this file.
const (
	x = iota
	y
	z
)

// VecAddition – performs vector addition between two vectors, v and u
// s.t. v + u = [v₁+u₁ , v₂+u₂, ⠂⠂⠂ , vᵢ₋₁+uᵢ₋₁ , vᵢ+uᵢ ]
// on an i-th dimension vector.
func VecAddition(v Vector, u Vector) (Vector, error) {
	if len(v) != len(u) {
		return nil, errors.New("vector dimensions do not coincide")
	}
	var vPlusU Vector
	for i := 0; i < len(v); i++ {
		vPlusU = append(vPlusU, (v[i] + u[i])) //	add an element to the new vector which is the sum of element i from v and u
	}
	return vPlusU, nil
}

// VecScalarMultiply - performs scalar multiplication on a vector v,
// s.t. scalar * v = [scalar*e1, scalar*e2, scalar*e3]
func VecScalarMultiply(v Vector, scalar float64) (Vector, error) {
	if len(v) == 0 {
		return nil, errors.New("v is an empty vector")
	}
	var sm Vector
	for i := range v {
		sm = append(sm, (v[i] * scalar))
	}
	return sm, nil
}

// DotProduct returns the sum of the product of elements of
// two i-dimension vectors, u and v, as a scalar
// s.t. v•v = (v₁u₁ + v₂u₂ + ⠂⠂⠂ + vᵢ₋₁uᵢ₋₁ + vᵢuᵢ)
func DotProduct(v Vector, u Vector) (float64, error) {
	if len(v) != len(u) {
		return 0, errors.New("vector dimensions do not coincide")
	}
	var f float64
	for i := 0; i < len(v); i++ {
		f += (v[i] * u[i])
	}
	return f, nil
}

// CrossProduct produces a new Vector orthogonal to both v and u.
// Only supported for 3D vectors.
func CrossProduct(v Vector, u Vector) (Vector, error) {
	if len(v) != 3 || len(u) != 3 {
		return nil, errors.New("vector dimension != 3")
	}
	var cp Vector
	cp = append(cp, (v[y]*u[z] - v[z]*u[y]))
	cp = append(cp, (v[z]*u[x] - v[x]*u[z]))
	cp = append(cp, (v[x]*u[y] - v[y]*u[x]))
	return cp, nil
}

// AngleFromOrigin calculates the angle of a given vector from the origin
// relative to the x-axis of 𝐄 (the model environment)
func AngleFromOrigin(v Vector) (float64, error) {
	if len(v) != 2 {
		return 0, errors.New("vector dimension != 2")
	}
	return toFixed(math.Atan2(v[x], v[y]), 5), nil
}

// RelativeAngle – does what it says on the box.
// Only implemented in 2D currently, or, as a comparitive rotation
// on the z-axis in 3D.
// (Not restricted, however, as one could have higher dimensionalites
// of vector and still test for relative angle, by passing a slice which indicates which axis of rotation to be performed. TO BE PROPERLY IMPLEMETED LATER!)
func RelativeAngle(v Vector, u Vector) (float64, error) {
	if len(v) == 0 || len(u) == 0 {
		return 0, errors.New("v or u is an empty vector")
	}
	if len(v) != len(u) {
		return 0, errors.New("vector dimensions do not coincide")
	}
	det := (v[x] * u[y]) - (v[y] * u[x])
	dot, err := DotProduct(v, u)
	if err != nil {
		return 0, err
	}
	angle := math.Atan2(det, dot)
	return toFixed(angle, 5), nil
}

// UnitAngle will map any floating-point value to its angle on a unit circle.
func UnitAngle(angle float64) float64 {
	twoPi := math.Pi * 2
	return toFixed((angle - (twoPi * math.Floor(angle/twoPi))), 5)
}

// Magnitude does the classic calculation for length of a vector
// (or, distance from origin)
func Magnitude(v Vector) (float64, error) {
	if len(v) == 0 {
		return 0, errors.New("v is an empty vector")
	}
	var ǁvǁsq float64
	for i := 0; i < len(v); i++ {
		ǁvǁsq += v[i] * v[i]
	}
	return toFixed(math.Sqrt(ǁvǁsq), 5), nil
}

// VectorDistance calculates the distance between two positions
func VectorDistance(v Vector, u Vector) (float64, error) {
	if len(v) != len(u) {
		return 0, errors.New("vector dimensions do not coincide")
	}
	vd := Vector{}
	for i := 0; i < len(v); i++ {
		diff := (v[i] - u[i])
		vd = append(vd, diff)
	}
	return Magnitude(vd)
}

// Normalise returns the normalised Vector of v
// – it's what you might call a direction vector,
// as opposed to a position vector.
// NOTE: this is not the same as a 'norm'/'normal' which is the vector
// orthogonal to a plane or surface.
func Normalise(v Vector) (Vector, error) {
	if len(v) == 0 {
		return nil, errors.New("v is an empty vector")
	}
	var norm Vector
	var ǁvǁ float64
	var err error
	for i := 0; i < len(v); i++ {
		ǁvǁ, err = Magnitude(v)
		if err != nil {
			return nil, err
		}
		norm = append(norm, toFixed(v[i]/ǁvǁ, 5))
	}
	return norm, nil
}

// TranslatePositionToSector2D : translates the co-ordinates of a 2D vector to sector indices location (2D Version)
func TranslatePositionToSector2D(ed float64, n int, v Vector) (int, int) {
	fn := float64(n)
	col := int(((v[x] + ed) / (2 * ed)) * fn)
	row := int(((-1 * (v[y] - ed)) / (2 * ed)) * fn)
	return row, col
}

/*ColourDistance quantifies the value difference between two ColRGB structs,
returning a floating-point ratio from 0.0 to 1.0.
Multiply the returned value by100 for a percentage.
NOTE: this is a distinct concept from the distance between them as 3D vectors,
as there would be 2 other ColRGB for any ColRGB with an identical magnitude.
e.g. [255 0 0] [0 255 0] [0 0 255] will all have the same magnitude, but are
pure Red, pure Blue, pure Green respectively! */
func ColourDistance(c1 ColRGB, c2 ColRGB) float64 {
	red := float64(c1.red-c2.red) / 255
	green := float64(c1.green-c2.green) / 255
	blue := float64(c1.blue-c2.blue) / 255
	return toFixed(((red + blue + green) / 3.0), 3) // returns to 3 d.p. only
}
<commit_msg>Update geometry.go<commit_after>package geometry

import (
	"errors"
	"math"
)

// I don't count as a contributor?

/* Thanks to David Calhoun for providing round and toFixed!
http://stackoverflow.com/questions/18390266/how-can-we-truncate-float64-type-to-a-particular-precision-in-golang
*/
func round(num float64) int {
	return int(num + math.Copysign(0.5, num))
}

func toFixed(num float64, precision int) float64 {
	output := math.Pow(10, float64(precision))
	return float64(round(num*output)) / output
}

// Vector : Any sized dimension representation of a point of vector space.
type Vector []float64

// VectorEquality – Trying to implement a quick version of checking for Vector equality
type VectorEquality interface {
	Equal(VectorEquality) bool
}

// Equal method implements an Equality comparison between vectors.
func (v Vector) Equal(u VectorEquality) bool {
	if len(v) != len(u.(Vector)) {
		return false
	}
	for i := 0; i < len(v); i++ {
		if v[i] != u.(Vector)[i] {
			return false
		}
	}
	return true
}

/*
ColRGB stores a standard 8-bit per channel Red Green Blue colour
representation. Part of pkg geometry colour lives in a form of
vector space.
*/
type ColRGB struct {
	red   byte
	green byte
	blue  byte
}

// just providing a conventional element naming system for
// 2D and 3D vectors.
// this 'aliasing' is just local to this file.
const (
	x = iota
	y
	z
)

// VecAddition – performs vector addition between two vectors, v and u
// s.t. v + u = [v₁+u₁ , v₂+u₂, ⠂⠂⠂ , vᵢ₋₁+uᵢ₋₁ , vᵢ+uᵢ ]
// on an i-th dimension vector.
func VecAddition(v Vector, u Vector) (Vector, error) {
	if len(v) != len(u) {
		return nil, errors.New("vector dimensions do not coincide")
	}
	var vPlusU Vector
	for i := 0; i < len(v); i++ {
		vPlusU = append(vPlusU, (v[i] + u[i])) //	add an element to the new vector which is the sum of element i from v and u
	}
	return vPlusU, nil
}

// VecScalarMultiply - performs scalar multiplication on a vector v,
// s.t. scalar * v = [scalar*e1, scalar*e2, scalar*e3]
func VecScalarMultiply(v Vector, scalar float64) (Vector, error) {
	if len(v) == 0 {
		return nil, errors.New("v is an empty vector")
	}
	var sm Vector
	for i := range v {
		sm = append(sm, (v[i] * scalar))
	}
	return sm, nil
}

// DotProduct returns the sum of the product of elements of
// two i-dimension vectors, u and v, as a scalar
// s.t. v•v = (v₁u₁ + v₂u₂ + ⠂⠂⠂ + vᵢ₋₁uᵢ₋₁ + vᵢuᵢ)
func DotProduct(v Vector, u Vector) (float64, error) {
	if len(v) != len(u) {
		return 0, errors.New("vector dimensions do not coincide")
	}
	var f float64
	for i := 0; i < len(v); i++ {
		f += (v[i] * u[i])
	}
	return f, nil
}

// CrossProduct produces a new Vector orthogonal to both v and u.
// Only supported for 3D vectors.
func CrossProduct(v Vector, u Vector) (Vector, error) {
	if len(v) != 3 || len(u) != 3 {
		return nil, errors.New("vector dimension != 3")
	}
	var cp Vector
	cp = append(cp, (v[y]*u[z] - v[z]*u[y]))
	cp = append(cp, (v[z]*u[x] - v[x]*u[z]))
	cp = append(cp, (v[x]*u[y] - v[y]*u[x]))
	return cp, nil
}

// AngleFromOrigin calculates the angle of a given vector from the origin
// relative to the x-axis of 𝐄 (the model environment)
func AngleFromOrigin(v Vector) (float64, error) {
	if len(v) != 2 {
		return 0, errors.New("vector dimension != 2")
	}
	return toFixed(math.Atan2(v[x], v[y]), 5), nil
}

// RelativeAngle – does what it says on the box.
// Only implemented in 2D currently, or, as a comparitive rotation
// on the z-axis in 3D.
// (Not restricted, however, as one could have higher dimensionalites
// of vector and still test for relative angle, by passing a slice which indicates which axis of rotation to be performed. TO BE PROPERLY IMPLEMETED LATER!)
func RelativeAngle(v Vector, u Vector) (float64, error) {
	if len(v) == 0 || len(u) == 0 {
		return 0, errors.New("v or u is an empty vector")
	}
	if len(v) != len(u) {
		return 0, errors.New("vector dimensions do not coincide")
	}
	det := (v[x] * u[y]) - (v[y] * u[x])
	dot, err := DotProduct(v, u)
	if err != nil {
		return 0, err
	}
	angle := math.Atan2(det, dot)
	return toFixed(angle, 5), nil
}

// UnitAngle will map any floating-point value to its angle on a unit circle.
func UnitAngle(angle float64) float64 {
	twoPi := math.Pi * 2
	return toFixed((angle - (twoPi * math.Floor(angle/twoPi))), 5)
}

// Magnitude does the classic calculation for length of a vector
// (or, distance from origin)
func Magnitude(v Vector) (float64, error) {
	if len(v) == 0 {
		return 0, errors.New("v is an empty vector")
	}
	var ǁvǁsq float64
	for i := 0; i < len(v); i++ {
		ǁvǁsq += v[i] * v[i]
	}
	return toFixed(math.Sqrt(ǁvǁsq), 5), nil
}

// VectorDistance calculates the distance between two positions
func VectorDistance(v Vector, u Vector) (float64, error) {
	if len(v) != len(u) {
		return 0, errors.New("vector dimensions do not coincide")
	}
	vd := Vector{}
	for i := 0; i < len(v); i++ {
		diff := (v[i] - u[i])
		vd = append(vd, diff)
	}
	return Magnitude(vd)
}

// Normalise returns the normalised Vector of v
// – it's what you might call a direction vector,
// as opposed to a position vector.
// NOTE: this is not the same as a 'norm'/'normal' which is the vector
// orthogonal to a plane or surface.
func Normalise(v Vector) (Vector, error) {
	if len(v) == 0 {
		return nil, errors.New("v is an empty vector")
	}
	var norm Vector
	var ǁvǁ float64
	var err error
	for i := 0; i < len(v); i++ {
		ǁvǁ, err = Magnitude(v)
		if err != nil {
			return nil, err
		}
		norm = append(norm, toFixed(v[i]/ǁvǁ, 5))
	}
	return norm, nil
}

// TranslatePositionToSector2D : translates the co-ordinates of a 2D vector to sector indices location (2D Version)
func TranslatePositionToSector2D(ed float64, n int, v Vector) (int, int) {
	fn := float64(n)
	col := int(((v[x] + ed) / (2 * ed)) * fn)
	row := int(((-1 * (v[y] - ed)) / (2 * ed)) * fn)
	return row, col
}

/*ColourDistance quantifies the value difference between two ColRGB structs,
returning a floating-point ratio from 0.0 to 1.0.
Multiply the returned value by100 for a percentage.
NOTE: this is a distinct concept from the distance between them as 3D vectors,
as there would be 2 other ColRGB for any ColRGB with an identical magnitude.
e.g. [255 0 0] [0 255 0] [0 0 255] will all have the same magnitude, but are
pure Red, pure Blue, pure Green respectively! */
func ColourDistance(c1 ColRGB, c2 ColRGB) float64 {
	red := float64(c1.red-c2.red) / 255
	green := float64(c1.green-c2.green) / 255
	blue := float64(c1.blue-c2.blue) / 255
	return toFixed(((red + blue + green) / 3.0), 3) // returns to 3 d.p. only
}
<|endoftext|>


<commit_before>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

// +build linux

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/circonus-labs/circonus-agent/plugins/linux/io/event_harness"
	"github.com/circonus-labs/circonusllhist"
)

var MAX_AGE = 310.0 // 5m10s
var devlist = map[string]string{}

var inserts = map[string]float64{}
var issues = map[string]float64{}
var max_whence = float64(0)
var latencies = map[string]interface{}{}
var cleantables = time.Ticker{}

func trackLatency(dev, typ string, duration float64) {
	var latency map[string]*circonusllhist.Histogram
	if latency_gen, ok := latencies[typ]; ok {
		latency = latency_gen.(map[string]*circonusllhist.Histogram)
	} else {
		latency = make(map[string]*circonusllhist.Histogram)
		latencies[typ] = latency
	}
	hist, ok2 := latency[dev]
	if !ok2 {
		latency[dev] = circonusllhist.NewNoLocks()
		hist = latency[dev]
	}
	hist.RecordValue(duration)
}

func dumpHistAndClear() {
	tmp := map[string]interface{}{}
	for typ, v := range latencies {
		latency := v.(map[string]*circonusllhist.Histogram)
		for dev, hist := range latency {
			if ss := hist.DecStrings(); len(ss) > 0 {
				hist.Reset()
				val := make(map[string]interface{})
				val["_type"] = "n"
				val["_value"] = ss
				tmp[typ+"|ST[device:"+dev+",units=seconds]"] = val
			}
		}
	}
	if buff, err := json.Marshal(tmp); err == nil {
		fmt.Printf("%s\n\n", string(buff))
	}
}

func handleLogLine(line string) {
	parts := strings.Fields(line)
	if len(parts) >= 11 && devlist[parts[5]] != "" {
		whence, err := strconv.ParseFloat(strings.Replace(parts[3], ":", "", 1), 64)
		if err != nil {
			return
		}
		if whence > max_whence {
			max_whence = whence
		}
		op, dev := strings.Replace(parts[4], ":", "", 1), devlist[parts[5]]
		if whence == 0 {
			return
		}
		select {
		case <-cleantables.C:
			for k, v := range inserts {
				if max_whence-v > MAX_AGE {
					delete(inserts, k)
				}
			}
			for k, v := range issues {
				if max_whence-v > MAX_AGE {
					delete(inserts, k)
				}
			}
		default:
		}

		switch {
		case op == "block_rq_insert":
			sec, nsec := parts[9], parts[11]
			opkey := strings.Join([]string{dev, sec, nsec}, ",")
			inserts[opkey] = whence
		case op == "block_rq_issue":
			sec, nsec := parts[9], parts[11]
			opkey := strings.Join([]string{dev, sec, nsec}, ",")
			if start, ok := inserts[opkey]; ok {
				issues[opkey] = whence
				// Q2D - time request spent in queue - io_latency`queue_time
				trackLatency(dev, "queue_time", whence-start)
			}
		case op == "block_rq_complete":
			sec, nsec := parts[8], parts[10]
			opkey := strings.Join([]string{dev, sec, nsec}, ",")
			if start, ok := inserts[opkey]; ok {
				// Q2C - total request handling time - io_latency`total_time
				trackLatency(dev, "total_time", whence-start)
				delete(inserts, opkey)
			}
			if start, ok := issues[opkey]; ok {
				// D2C - time for device to handle request - io_latency`device_time
				trackLatency(dev, "device_time", whence-start)
				delete(issues, opkey)
			}
		default:
		}
	}
}

func refreshDevs() {
	dir, err := os.Open("/dev/")
	if err != nil {
		return
	}
	files, err := dir.Readdir(0)
	if err != nil {
		return
	}
	for _, file := range files {
		if stat := file.Sys().(*syscall.Stat_t); file.Mode()&os.ModeDevice != 0 && file.Mode()&os.ModeCharDevice == 0 && stat != nil {
			major, minor := stat.Rdev/256, stat.Rdev%256
			key := fmt.Sprintf("%d,%d", major, minor)
			if _, ok := devlist[key]; !ok {
				devlist[key] = file.Name()
			}
		}
	}
}
func cleanupTables() {
	for k, v := range inserts {
		if max_whence-v > MAX_AGE {
			delete(inserts, k)
		}
	}
	for k, v := range issues {
		if max_whence-v > MAX_AGE {
			delete(inserts, k)
		}
	}
}
func main() {
	refreshDevs()

	h, err := event_harness.HarnessMain("circ_blk",
		[][]string{[]string{"events/block/block_rq_issue/enable", "1\n"},
			[]string{"events/block/block_rq_insert/enable", "1\n"},
			[]string{"events/block/block_rq_complete/enable", "1\n"}},
		handleLogLine)
	if err != nil {
		log.Fatalf("Cannot start tracing.\n%s\n", err)
	}

	go func() {
		for {
			time.Sleep(10 * time.Second)
			h.Tasks <- cleanupTables
			h.Tasks <- dumpHistAndClear
		}
	}()

	err = <-h.Done
	if err != nil {
		log.Fatalf("runtime error: %s\n", err)
	}
}
<commit_msg>fix: typo in tag : not =<commit_after>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

// +build linux

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/circonus-labs/circonus-agent/plugins/linux/io/event_harness"
	"github.com/circonus-labs/circonusllhist"
)

var MAX_AGE = 310.0 // 5m10s
var devlist = map[string]string{}

var inserts = map[string]float64{}
var issues = map[string]float64{}
var max_whence = float64(0)
var latencies = map[string]interface{}{}
var cleantables = time.Ticker{}

func trackLatency(dev, typ string, duration float64) {
	var latency map[string]*circonusllhist.Histogram
	if latency_gen, ok := latencies[typ]; ok {
		latency = latency_gen.(map[string]*circonusllhist.Histogram)
	} else {
		latency = make(map[string]*circonusllhist.Histogram)
		latencies[typ] = latency
	}
	hist, ok2 := latency[dev]
	if !ok2 {
		latency[dev] = circonusllhist.NewNoLocks()
		hist = latency[dev]
	}
	hist.RecordValue(duration)
}

func dumpHistAndClear() {
	tmp := map[string]interface{}{}
	for typ, v := range latencies {
		latency := v.(map[string]*circonusllhist.Histogram)
		for dev, hist := range latency {
			if ss := hist.DecStrings(); len(ss) > 0 {
				hist.Reset()
				val := make(map[string]interface{})
				val["_type"] = "n"
				val["_value"] = ss
				tmp[typ+"|ST[device:"+dev+",units:seconds]"] = val
			}
		}
	}
	if buff, err := json.Marshal(tmp); err == nil {
		fmt.Printf("%s\n\n", string(buff))
	}
}

func handleLogLine(line string) {
	parts := strings.Fields(line)
	if len(parts) >= 11 && devlist[parts[5]] != "" {
		whence, err := strconv.ParseFloat(strings.Replace(parts[3], ":", "", 1), 64)
		if err != nil {
			return
		}
		if whence > max_whence {
			max_whence = whence
		}
		op, dev := strings.Replace(parts[4], ":", "", 1), devlist[parts[5]]
		if whence == 0 {
			return
		}
		select {
		case <-cleantables.C:
			for k, v := range inserts {
				if max_whence-v > MAX_AGE {
					delete(inserts, k)
				}
			}
			for k, v := range issues {
				if max_whence-v > MAX_AGE {
					delete(inserts, k)
				}
			}
		default:
		}

		switch {
		case op == "block_rq_insert":
			sec, nsec := parts[9], parts[11]
			opkey := strings.Join([]string{dev, sec, nsec}, ",")
			inserts[opkey] = whence
		case op == "block_rq_issue":
			sec, nsec := parts[9], parts[11]
			opkey := strings.Join([]string{dev, sec, nsec}, ",")
			if start, ok := inserts[opkey]; ok {
				issues[opkey] = whence
				// Q2D - time request spent in queue - io_latency`queue_time
				trackLatency(dev, "queue_time", whence-start)
			}
		case op == "block_rq_complete":
			sec, nsec := parts[8], parts[10]
			opkey := strings.Join([]string{dev, sec, nsec}, ",")
			if start, ok := inserts[opkey]; ok {
				// Q2C - total request handling time - io_latency`total_time
				trackLatency(dev, "total_time", whence-start)
				delete(inserts, opkey)
			}
			if start, ok := issues[opkey]; ok {
				// D2C - time for device to handle request - io_latency`device_time
				trackLatency(dev, "device_time", whence-start)
				delete(issues, opkey)
			}
		default:
		}
	}
}

func refreshDevs() {
	dir, err := os.Open("/dev/")
	if err != nil {
		return
	}
	files, err := dir.Readdir(0)
	if err != nil {
		return
	}
	for _, file := range files {
		if stat := file.Sys().(*syscall.Stat_t); file.Mode()&os.ModeDevice != 0 && file.Mode()&os.ModeCharDevice == 0 && stat != nil {
			major, minor := stat.Rdev/256, stat.Rdev%256
			key := fmt.Sprintf("%d,%d", major, minor)
			if _, ok := devlist[key]; !ok {
				devlist[key] = file.Name()
			}
		}
	}
}
func cleanupTables() {
	for k, v := range inserts {
		if max_whence-v > MAX_AGE {
			delete(inserts, k)
		}
	}
	for k, v := range issues {
		if max_whence-v > MAX_AGE {
			delete(inserts, k)
		}
	}
}
func main() {
	refreshDevs()

	h, err := event_harness.HarnessMain("circ_blk",
		[][]string{[]string{"events/block/block_rq_issue/enable", "1\n"},
			[]string{"events/block/block_rq_insert/enable", "1\n"},
			[]string{"events/block/block_rq_complete/enable", "1\n"}},
		handleLogLine)
	if err != nil {
		log.Fatalf("Cannot start tracing.\n%s\n", err)
	}

	go func() {
		for {
			time.Sleep(10 * time.Second)
			h.Tasks <- cleanupTables
			h.Tasks <- dumpHistAndClear
		}
	}()

	err = <-h.Done
	if err != nil {
		log.Fatalf("runtime error: %s\n", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package pull

import (
	"fmt"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/git"
	"code.gitea.io/gitea/modules/log"
)

// Update updates pull request with base branch.
func Update(pull *models.PullRequest, doer *models.User, message string) error {
	//use merge functions but switch repo's and branch's
	pr := &models.PullRequest{
		HeadRepoID: pull.BaseRepoID,
		BaseRepoID: pull.HeadRepoID,
		HeadBranch: pull.BaseBranch,
		BaseBranch: pull.HeadBranch,
	}

	if err := pr.LoadHeadRepo(); err != nil {
		log.Error("LoadHeadRepo: %v", err)
		return fmt.Errorf("LoadHeadRepo: %v", err)
	} else if err = pr.LoadBaseRepo(); err != nil {
		log.Error("LoadBaseRepo: %v", err)
		return fmt.Errorf("LoadBaseRepo: %v", err)
	}

	diffCount, err := GetDiverging(pull)
	if err != nil {
		return err
	} else if diffCount.Behind == 0 {
		return fmt.Errorf("HeadBranch of PR %d is up to date", pull.Index)
	}

	_, err = rawMerge(pr, doer, models.MergeStyleMerge, message)

	defer func() {
		go AddTestPullRequestTask(doer, pr.HeadRepo.ID, pr.HeadBranch, false, "", "")
	}()

	return err
}

// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
func IsUserAllowedToUpdate(pull *models.PullRequest, user *models.User) (bool, error) {
	headRepoPerm, err := models.GetUserRepoPermission(pull.HeadRepo, user)
	if err != nil {
		return false, err
	}

	pr := &models.PullRequest{
		HeadRepoID: pull.BaseRepoID,
		BaseRepoID: pull.HeadRepoID,
		HeadBranch: pull.BaseBranch,
		BaseBranch: pull.HeadBranch,
	}
	return IsUserAllowedToMerge(pr, headRepoPerm, user)
}

// GetDiverging determines how many commits a PR is ahead or behind the PR base branch
func GetDiverging(pr *models.PullRequest) (*git.DivergeObject, error) {
	log.Trace("GetDiverging[%d]: compare commits", pr.ID)
	if err := pr.LoadBaseRepo(); err != nil {
		return nil, err
	}
	if err := pr.LoadHeadRepo(); err != nil {
		return nil, err
	}

	tmpRepo, err := createTemporaryRepo(pr)
	if err != nil {
		log.Error("CreateTemporaryPath: %v", err)
		return nil, err
	}
	defer func() {
		if err := models.RemoveTemporaryPath(tmpRepo); err != nil {
			log.Error("Merge: RemoveTemporaryPath: %s", err)
		}
	}()

	diff, err := git.GetDivergingCommits(tmpRepo, "base", "tracking")
	return &diff, err
}
<commit_msg>Check Push permissions on IsUserAllowedToUpdate (#11448)<commit_after>// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package pull

import (
	"fmt"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/git"
	"code.gitea.io/gitea/modules/log"
)

// Update updates pull request with base branch.
func Update(pull *models.PullRequest, doer *models.User, message string) error {
	//use merge functions but switch repo's and branch's
	pr := &models.PullRequest{
		HeadRepoID: pull.BaseRepoID,
		BaseRepoID: pull.HeadRepoID,
		HeadBranch: pull.BaseBranch,
		BaseBranch: pull.HeadBranch,
	}

	if err := pr.LoadHeadRepo(); err != nil {
		log.Error("LoadHeadRepo: %v", err)
		return fmt.Errorf("LoadHeadRepo: %v", err)
	} else if err = pr.LoadBaseRepo(); err != nil {
		log.Error("LoadBaseRepo: %v", err)
		return fmt.Errorf("LoadBaseRepo: %v", err)
	}

	diffCount, err := GetDiverging(pull)
	if err != nil {
		return err
	} else if diffCount.Behind == 0 {
		return fmt.Errorf("HeadBranch of PR %d is up to date", pull.Index)
	}

	_, err = rawMerge(pr, doer, models.MergeStyleMerge, message)

	defer func() {
		go AddTestPullRequestTask(doer, pr.HeadRepo.ID, pr.HeadBranch, false, "", "")
	}()

	return err
}

// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
func IsUserAllowedToUpdate(pull *models.PullRequest, user *models.User) (bool, error) {
	headRepoPerm, err := models.GetUserRepoPermission(pull.HeadRepo, user)
	if err != nil {
		return false, err
	}

	pr := &models.PullRequest{
		HeadRepoID: pull.BaseRepoID,
		BaseRepoID: pull.HeadRepoID,
		HeadBranch: pull.BaseBranch,
		BaseBranch: pull.HeadBranch,
	}

	err = pr.LoadProtectedBranch()
	if err != nil {
		return false, err
	}

	// Update function need push permission
	if pr.ProtectedBranch != nil && !pr.ProtectedBranch.CanUserPush(user.ID) {
		return false, nil
	}

	return IsUserAllowedToMerge(pr, headRepoPerm, user)
}

// GetDiverging determines how many commits a PR is ahead or behind the PR base branch
func GetDiverging(pr *models.PullRequest) (*git.DivergeObject, error) {
	log.Trace("GetDiverging[%d]: compare commits", pr.ID)
	if err := pr.LoadBaseRepo(); err != nil {
		return nil, err
	}
	if err := pr.LoadHeadRepo(); err != nil {
		return nil, err
	}

	tmpRepo, err := createTemporaryRepo(pr)
	if err != nil {
		log.Error("CreateTemporaryPath: %v", err)
		return nil, err
	}
	defer func() {
		if err := models.RemoveTemporaryPath(tmpRepo); err != nil {
			log.Error("Merge: RemoveTemporaryPath: %s", err)
		}
	}()

	diff, err := git.GetDivergingCommits(tmpRepo, "base", "tracking")
	return &diff, err
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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 deletion

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"path"
	"strings"
	"sync"
	"testing"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/metadata"
	restclient "k8s.io/client-go/rest"
	core "k8s.io/client-go/testing"
	api "k8s.io/kubernetes/pkg/apis/core"
)

func TestFinalized(t *testing.T) {
	testNamespace := &v1.Namespace{
		Spec: v1.NamespaceSpec{
			Finalizers: []v1.FinalizerName{"a", "b"},
		},
	}
	if finalized(testNamespace) {
		t.Errorf("Unexpected result, namespace is not finalized")
	}
	testNamespace.Spec.Finalizers = []v1.FinalizerName{}
	if !finalized(testNamespace) {
		t.Errorf("Expected object to be finalized")
	}
}

func TestFinalizeNamespaceFunc(t *testing.T) {
	mockClient := &fake.Clientset{}
	testNamespace := &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name:            "test",
			ResourceVersion: "1",
		},
		Spec: v1.NamespaceSpec{
			Finalizers: []v1.FinalizerName{"kubernetes", "other"},
		},
	}
	d := namespacedResourcesDeleter{
		nsClient:       mockClient.CoreV1().Namespaces(),
		finalizerToken: v1.FinalizerKubernetes,
	}
	d.finalizeNamespace(testNamespace)
	actions := mockClient.Actions()
	if len(actions) != 1 {
		t.Errorf("Expected 1 mock client action, but got %v", len(actions))
	}
	if !actions[0].Matches("create", "namespaces") || actions[0].GetSubresource() != "finalize" {
		t.Errorf("Expected finalize-namespace action %v", actions[0])
	}
	finalizers := actions[0].(core.CreateAction).GetObject().(*v1.Namespace).Spec.Finalizers
	if len(finalizers) != 1 {
		t.Errorf("There should be a single finalizer remaining")
	}
	if "other" != string(finalizers[0]) {
		t.Errorf("Unexpected finalizer value, %v", finalizers[0])
	}
}

func testSyncNamespaceThatIsTerminating(t *testing.T, versions *metav1.APIVersions) {
	now := metav1.Now()
	namespaceName := "test"
	testNamespacePendingFinalize := &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name:              namespaceName,
			ResourceVersion:   "1",
			DeletionTimestamp: &now,
		},
		Spec: v1.NamespaceSpec{
			Finalizers: []v1.FinalizerName{"kubernetes"},
		},
		Status: v1.NamespaceStatus{
			Phase: v1.NamespaceTerminating,
		},
	}
	testNamespaceFinalizeComplete := &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name:              namespaceName,
			ResourceVersion:   "1",
			DeletionTimestamp: &now,
		},
		Spec: v1.NamespaceSpec{},
		Status: v1.NamespaceStatus{
			Phase: v1.NamespaceTerminating,
		},
	}

	// when doing a delete all of content, we will do a GET of a collection, and DELETE of a collection by default
	metadataClientActionSet := sets.NewString()
	resources := testResources()
	groupVersionResources, _ := discovery.GroupVersionResources(resources)
	for groupVersionResource := range groupVersionResources {
		urlPath := path.Join([]string{
			dynamic.LegacyAPIPathResolverFunc(schema.GroupVersionKind{Group: groupVersionResource.Group, Version: groupVersionResource.Version}),
			groupVersionResource.Group,
			groupVersionResource.Version,
			"namespaces",
			namespaceName,
			groupVersionResource.Resource,
		}...)
		metadataClientActionSet.Insert((&fakeAction{method: "GET", path: urlPath}).String())
		metadataClientActionSet.Insert((&fakeAction{method: "DELETE", path: urlPath}).String())
	}

	scenarios := map[string]struct {
		testNamespace           *v1.Namespace
		kubeClientActionSet     sets.String
		metadataClientActionSet sets.String
		gvrError                error
		expectErrorOnDelete     error
	}{
		"pending-finalize": {
			testNamespace: testNamespacePendingFinalize,
			kubeClientActionSet: sets.NewString(
				strings.Join([]string{"get", "namespaces", ""}, "-"),
				strings.Join([]string{"create", "namespaces", "finalize"}, "-"),
				strings.Join([]string{"list", "pods", ""}, "-"),
				strings.Join([]string{"delete", "namespaces", ""}, "-"),
			),
			metadataClientActionSet: metadataClientActionSet,
		},
		"complete-finalize": {
			testNamespace: testNamespaceFinalizeComplete,
			kubeClientActionSet: sets.NewString(
				strings.Join([]string{"get", "namespaces", ""}, "-"),
				strings.Join([]string{"delete", "namespaces", ""}, "-"),
			),
			metadataClientActionSet: sets.NewString(),
		},
		"groupVersionResourceErr": {
			testNamespace: testNamespaceFinalizeComplete,
			kubeClientActionSet: sets.NewString(
				strings.Join([]string{"get", "namespaces", ""}, "-"),
				strings.Join([]string{"delete", "namespaces", ""}, "-"),
			),
			metadataClientActionSet: sets.NewString(),
			gvrError:                fmt.Errorf("test error"),
		},
		"groupVersionResourceErr-finalize": {
			testNamespace: testNamespacePendingFinalize,
			kubeClientActionSet: sets.NewString(
				strings.Join([]string{"get", "namespaces", ""}, "-"),
				strings.Join([]string{"list", "pods", ""}, "-"),
				strings.Join([]string{"update", "namespaces", "status"}, "-"),
			),
			dynamicClientActionSet: dynamicClientActionSet,
			gvrError:               fmt.Errorf("test error"),
			expectErrorOnDelete:    fmt.Errorf("test error"),
		},
	}

	for scenario, testInput := range scenarios {
		testHandler := &fakeActionHandler{statusCode: 200}
		srv, clientConfig := testServerAndClientConfig(testHandler.ServeHTTP)
		defer srv.Close()

		mockClient := fake.NewSimpleClientset(testInput.testNamespace)
		metadataClient, err := metadata.NewForConfig(clientConfig)
		if err != nil {
			t.Fatal(err)
		}

		fn := func() ([]*metav1.APIResourceList, error) {
			return resources, testInput.gvrError
		}
		d := NewNamespacedResourcesDeleter(mockClient.CoreV1().Namespaces(), metadataClient, mockClient.CoreV1(), fn, v1.FinalizerKubernetes, true)
		if err := d.Delete(testInput.testNamespace.Name); !matchErrors(err, testInput.expectErrorOnDelete) {
			t.Errorf("scenario %s - expected error %q when syncing namespace, got %q, %v", scenario, testInput.expectErrorOnDelete, err, testInput.expectErrorOnDelete == err)
		}

		// validate traffic from kube client
		actionSet := sets.NewString()
		for _, action := range mockClient.Actions() {
			actionSet.Insert(strings.Join([]string{action.GetVerb(), action.GetResource().Resource, action.GetSubresource()}, "-"))
		}
		if !actionSet.Equal(testInput.kubeClientActionSet) {
			t.Errorf("scenario %s - mock client expected actions:\n%v\n but got:\n%v\nDifference:\n%v", scenario,
				testInput.kubeClientActionSet, actionSet, testInput.kubeClientActionSet.Difference(actionSet))
		}

		// validate traffic from metadata client
		actionSet = sets.NewString()
		for _, action := range testHandler.actions {
			actionSet.Insert(action.String())
		}
		if !actionSet.Equal(testInput.metadataClientActionSet) {
			t.Errorf("scenario %s - metadata client expected actions:\n%v\n but got:\n%v\nDifference:\n%v", scenario,
				testInput.metadataClientActionSet, actionSet, testInput.metadataClientActionSet.Difference(actionSet))
		}
	}
}

func TestRetryOnConflictError(t *testing.T) {
	mockClient := &fake.Clientset{}
	numTries := 0
	retryOnce := func(namespace *v1.Namespace) (*v1.Namespace, error) {
		numTries++
		if numTries <= 1 {
			return namespace, errors.NewConflict(api.Resource("namespaces"), namespace.Name, fmt.Errorf("ERROR"))
		}
		return namespace, nil
	}
	namespace := &v1.Namespace{}
	d := namespacedResourcesDeleter{
		nsClient: mockClient.CoreV1().Namespaces(),
	}
	_, err := d.retryOnConflictError(namespace, retryOnce)
	if err != nil {
		t.Errorf("Unexpected error %v", err)
	}
	if numTries != 2 {
		t.Errorf("Expected %v, but got %v", 2, numTries)
	}
}

func TestSyncNamespaceThatIsTerminatingNonExperimental(t *testing.T) {
	testSyncNamespaceThatIsTerminating(t, &metav1.APIVersions{})
}

func TestSyncNamespaceThatIsTerminatingV1(t *testing.T) {
	testSyncNamespaceThatIsTerminating(t, &metav1.APIVersions{Versions: []string{"apps/v1"}})
}

func TestSyncNamespaceThatIsActive(t *testing.T) {
	mockClient := &fake.Clientset{}
	testNamespace := &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name:            "test",
			ResourceVersion: "1",
		},
		Spec: v1.NamespaceSpec{
			Finalizers: []v1.FinalizerName{"kubernetes"},
		},
		Status: v1.NamespaceStatus{
			Phase: v1.NamespaceActive,
		},
	}
	fn := func() ([]*metav1.APIResourceList, error) {
		return testResources(), nil
	}
	d := NewNamespacedResourcesDeleter(mockClient.CoreV1().Namespaces(), nil, mockClient.CoreV1(),
		fn, v1.FinalizerKubernetes, true)
	err := d.Delete(testNamespace.Name)
	if err != nil {
		t.Errorf("Unexpected error when synching namespace %v", err)
	}
	if len(mockClient.Actions()) != 1 {
		t.Errorf("Expected only one action from controller, but got: %d %v", len(mockClient.Actions()), mockClient.Actions())
	}
	action := mockClient.Actions()[0]
	if !action.Matches("get", "namespaces") {
		t.Errorf("Expected get namespaces, got: %v", action)
	}
}

// matchError returns true if errors match, false if they don't, compares by error message only for convenience which should be sufficient for these tests
func matchErrors(e1, e2 error) bool {
	if e1 == nil && e2 == nil {
		return true
	}
	if e1 != nil && e2 != nil {
		return e1.Error() == e2.Error()
	}
	return false
}

// testServerAndClientConfig returns a server that listens and a config that can reference it
func testServerAndClientConfig(handler func(http.ResponseWriter, *http.Request)) (*httptest.Server, *restclient.Config) {
	srv := httptest.NewServer(http.HandlerFunc(handler))
	config := &restclient.Config{
		Host: srv.URL,
	}
	return srv, config
}

// fakeAction records information about requests to aid in testing.
type fakeAction struct {
	method string
	path   string
}

// String returns method=path to aid in testing
func (f *fakeAction) String() string {
	return strings.Join([]string{f.method, f.path}, "=")
}

// fakeActionHandler holds a list of fakeActions received
type fakeActionHandler struct {
	// statusCode returned by this handler
	statusCode int

	lock    sync.Mutex
	actions []fakeAction
}

// ServeHTTP logs the action that occurred and always returns the associated status code
func (f *fakeActionHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.actions = append(f.actions, fakeAction{method: request.Method, path: request.URL.Path})
	response.Header().Set("Content-Type", runtime.ContentTypeJSON)
	response.WriteHeader(f.statusCode)
	response.Write([]byte("{\"apiVersion\": \"v1\", \"kind\": \"List\",\"items\":null}"))
}

// testResources returns a mocked up set of resources across different api groups for testing namespace controller.
func testResources() []*metav1.APIResourceList {
	results := []*metav1.APIResourceList{
		{
			GroupVersion: "v1",
			APIResources: []metav1.APIResource{
				{
					Name:       "pods",
					Namespaced: true,
					Kind:       "Pod",
					Verbs:      []string{"get", "list", "delete", "deletecollection", "create", "update"},
				},
				{
					Name:       "services",
					Namespaced: true,
					Kind:       "Service",
					Verbs:      []string{"get", "list", "delete", "deletecollection", "create", "update"},
				},
			},
		},
		{
			GroupVersion: "apps/v1",
			APIResources: []metav1.APIResource{
				{
					Name:       "deployments",
					Namespaced: true,
					Kind:       "Deployment",
					Verbs:      []string{"get", "list", "delete", "deletecollection", "create", "update"},
				},
			},
		},
	}
	return results
}
<commit_msg>Add tests for namespace status conditions<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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 deletion

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"path"
	"strings"
	"sync"
	"testing"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/metadata"
	restclient "k8s.io/client-go/rest"
	core "k8s.io/client-go/testing"
	api "k8s.io/kubernetes/pkg/apis/core"
)

func TestFinalized(t *testing.T) {
	testNamespace := &v1.Namespace{
		Spec: v1.NamespaceSpec{
			Finalizers: []v1.FinalizerName{"a", "b"},
		},
	}
	if finalized(testNamespace) {
		t.Errorf("Unexpected result, namespace is not finalized")
	}
	testNamespace.Spec.Finalizers = []v1.FinalizerName{}
	if !finalized(testNamespace) {
		t.Errorf("Expected object to be finalized")
	}
}

func TestFinalizeNamespaceFunc(t *testing.T) {
	mockClient := &fake.Clientset{}
	testNamespace := &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name:            "test",
			ResourceVersion: "1",
		},
		Spec: v1.NamespaceSpec{
			Finalizers: []v1.FinalizerName{"kubernetes", "other"},
		},
	}
	d := namespacedResourcesDeleter{
		nsClient:       mockClient.CoreV1().Namespaces(),
		finalizerToken: v1.FinalizerKubernetes,
	}
	d.finalizeNamespace(testNamespace)
	actions := mockClient.Actions()
	if len(actions) != 1 {
		t.Errorf("Expected 1 mock client action, but got %v", len(actions))
	}
	if !actions[0].Matches("create", "namespaces") || actions[0].GetSubresource() != "finalize" {
		t.Errorf("Expected finalize-namespace action %v", actions[0])
	}
	finalizers := actions[0].(core.CreateAction).GetObject().(*v1.Namespace).Spec.Finalizers
	if len(finalizers) != 1 {
		t.Errorf("There should be a single finalizer remaining")
	}
	if "other" != string(finalizers[0]) {
		t.Errorf("Unexpected finalizer value, %v", finalizers[0])
	}
}

func testSyncNamespaceThatIsTerminating(t *testing.T, versions *metav1.APIVersions) {
	now := metav1.Now()
	namespaceName := "test"
	testNamespacePendingFinalize := &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name:              namespaceName,
			ResourceVersion:   "1",
			DeletionTimestamp: &now,
		},
		Spec: v1.NamespaceSpec{
			Finalizers: []v1.FinalizerName{"kubernetes"},
		},
		Status: v1.NamespaceStatus{
			Phase: v1.NamespaceTerminating,
		},
	}
	testNamespaceFinalizeComplete := &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name:              namespaceName,
			ResourceVersion:   "1",
			DeletionTimestamp: &now,
		},
		Spec: v1.NamespaceSpec{},
		Status: v1.NamespaceStatus{
			Phase: v1.NamespaceTerminating,
		},
	}

	// when doing a delete all of content, we will do a GET of a collection, and DELETE of a collection by default
	metadataClientActionSet := sets.NewString()
	resources := testResources()
	groupVersionResources, _ := discovery.GroupVersionResources(resources)
	for groupVersionResource := range groupVersionResources {
		urlPath := path.Join([]string{
			dynamic.LegacyAPIPathResolverFunc(schema.GroupVersionKind{Group: groupVersionResource.Group, Version: groupVersionResource.Version}),
			groupVersionResource.Group,
			groupVersionResource.Version,
			"namespaces",
			namespaceName,
			groupVersionResource.Resource,
		}...)
		metadataClientActionSet.Insert((&fakeAction{method: "GET", path: urlPath}).String())
		metadataClientActionSet.Insert((&fakeAction{method: "DELETE", path: urlPath}).String())
	}

	scenarios := map[string]struct {
		testNamespace           *v1.Namespace
		kubeClientActionSet     sets.String
		metadataClientActionSet sets.String
		gvrError                error
		expectErrorOnDelete     error
		expectStatus            *v1.NamespaceStatus
	}{
		"pending-finalize": {
			testNamespace: testNamespacePendingFinalize,
			kubeClientActionSet: sets.NewString(
				strings.Join([]string{"get", "namespaces", ""}, "-"),
				strings.Join([]string{"create", "namespaces", "finalize"}, "-"),
				strings.Join([]string{"list", "pods", ""}, "-"),
				strings.Join([]string{"delete", "namespaces", ""}, "-"),
			),
			metadataClientActionSet: metadataClientActionSet,
		},
		"complete-finalize": {
			testNamespace: testNamespaceFinalizeComplete,
			kubeClientActionSet: sets.NewString(
				strings.Join([]string{"get", "namespaces", ""}, "-"),
				strings.Join([]string{"delete", "namespaces", ""}, "-"),
			),
			metadataClientActionSet: sets.NewString(),
		},
		"groupVersionResourceErr": {
			testNamespace: testNamespaceFinalizeComplete,
			kubeClientActionSet: sets.NewString(
				strings.Join([]string{"get", "namespaces", ""}, "-"),
				strings.Join([]string{"delete", "namespaces", ""}, "-"),
			),
			metadataClientActionSet: sets.NewString(),
			gvrError:                fmt.Errorf("test error"),
		},
		"groupVersionResourceErr-finalize": {
			testNamespace: testNamespacePendingFinalize,
			kubeClientActionSet: sets.NewString(
				strings.Join([]string{"get", "namespaces", ""}, "-"),
				strings.Join([]string{"list", "pods", ""}, "-"),
				strings.Join([]string{"update", "namespaces", "status"}, "-"),
			),
			metadataClientActionSet: metadataClientActionSet,
			gvrError:                fmt.Errorf("test error"),
			expectErrorOnDelete:     fmt.Errorf("test error"),
			expectStatus: &v1.NamespaceStatus{
				Phase: v1.NamespaceTerminating,
				Conditions: []v1.NamespaceCondition{
					{Type: v1.NamespaceDeletionDiscoveryFailure},
				},
			},
		},
	}

	for scenario, testInput := range scenarios {
		testHandler := &fakeActionHandler{statusCode: 200}
		srv, clientConfig := testServerAndClientConfig(testHandler.ServeHTTP)
		defer srv.Close()

		mockClient := fake.NewSimpleClientset(testInput.testNamespace)
		metadataClient, err := metadata.NewForConfig(clientConfig)
		if err != nil {
			t.Fatal(err)
		}

		fn := func() ([]*metav1.APIResourceList, error) {
			return resources, testInput.gvrError
		}
		d := NewNamespacedResourcesDeleter(mockClient.CoreV1().Namespaces(), metadataClient, mockClient.CoreV1(), fn, v1.FinalizerKubernetes, true)
		if err := d.Delete(testInput.testNamespace.Name); !matchErrors(err, testInput.expectErrorOnDelete) {
			t.Errorf("scenario %s - expected error %q when syncing namespace, got %q, %v", scenario, testInput.expectErrorOnDelete, err, testInput.expectErrorOnDelete == err)
		}

		// validate traffic from kube client
		actionSet := sets.NewString()
		for _, action := range mockClient.Actions() {
			actionSet.Insert(strings.Join([]string{action.GetVerb(), action.GetResource().Resource, action.GetSubresource()}, "-"))
		}
		if !actionSet.Equal(testInput.kubeClientActionSet) {
			t.Errorf("scenario %s - mock client expected actions:\n%v\n but got:\n%v\nDifference:\n%v", scenario,
				testInput.kubeClientActionSet, actionSet, testInput.kubeClientActionSet.Difference(actionSet))
		}

		// validate traffic from metadata client
		actionSet = sets.NewString()
		for _, action := range testHandler.actions {
			actionSet.Insert(action.String())
		}
		if !actionSet.Equal(testInput.metadataClientActionSet) {
			t.Errorf("scenario %s - metadata client expected actions:\n%v\n but got:\n%v\nDifference:\n%v", scenario,
				testInput.metadataClientActionSet, actionSet, testInput.metadataClientActionSet.Difference(actionSet))
		}

		// validate status conditions
		if testInput.expectStatus != nil {
			obj, err := mockClient.Tracker().Get(schema.GroupVersionResource{Version: "v1", Resource: "namespaces"}, testInput.testNamespace.Namespace, testInput.testNamespace.Name)
			if err != nil {
				t.Errorf("Unexpected error in getting the namespace: %v", err)
				continue
			}
			ns, ok := obj.(*v1.Namespace)
			if !ok {
				t.Errorf("Expected a namespace but received %v", obj)
				continue
			}
			if ns.Status.Phase != testInput.expectStatus.Phase {
				t.Errorf("Expected namespace status phase %v but received %v", testInput.expectStatus.Phase, ns.Status.Phase)
				continue
			}
			for _, expCondition := range testInput.expectStatus.Conditions {
				nsCondition := getCondition(ns.Status.Conditions, expCondition.Type)
				if nsCondition == nil {
					t.Errorf("Missing namespace status condition %v", expCondition.Type)
					continue
				}
			}
		}
	}
}

func TestRetryOnConflictError(t *testing.T) {
	mockClient := &fake.Clientset{}
	numTries := 0
	retryOnce := func(namespace *v1.Namespace) (*v1.Namespace, error) {
		numTries++
		if numTries <= 1 {
			return namespace, errors.NewConflict(api.Resource("namespaces"), namespace.Name, fmt.Errorf("ERROR"))
		}
		return namespace, nil
	}
	namespace := &v1.Namespace{}
	d := namespacedResourcesDeleter{
		nsClient: mockClient.CoreV1().Namespaces(),
	}
	_, err := d.retryOnConflictError(namespace, retryOnce)
	if err != nil {
		t.Errorf("Unexpected error %v", err)
	}
	if numTries != 2 {
		t.Errorf("Expected %v, but got %v", 2, numTries)
	}
}

func TestSyncNamespaceThatIsTerminatingNonExperimental(t *testing.T) {
	testSyncNamespaceThatIsTerminating(t, &metav1.APIVersions{})
}

func TestSyncNamespaceThatIsTerminatingV1(t *testing.T) {
	testSyncNamespaceThatIsTerminating(t, &metav1.APIVersions{Versions: []string{"apps/v1"}})
}

func TestSyncNamespaceThatIsActive(t *testing.T) {
	mockClient := &fake.Clientset{}
	testNamespace := &v1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name:            "test",
			ResourceVersion: "1",
		},
		Spec: v1.NamespaceSpec{
			Finalizers: []v1.FinalizerName{"kubernetes"},
		},
		Status: v1.NamespaceStatus{
			Phase: v1.NamespaceActive,
		},
	}
	fn := func() ([]*metav1.APIResourceList, error) {
		return testResources(), nil
	}
	d := NewNamespacedResourcesDeleter(mockClient.CoreV1().Namespaces(), nil, mockClient.CoreV1(),
		fn, v1.FinalizerKubernetes, true)
	err := d.Delete(testNamespace.Name)
	if err != nil {
		t.Errorf("Unexpected error when synching namespace %v", err)
	}
	if len(mockClient.Actions()) != 1 {
		t.Errorf("Expected only one action from controller, but got: %d %v", len(mockClient.Actions()), mockClient.Actions())
	}
	action := mockClient.Actions()[0]
	if !action.Matches("get", "namespaces") {
		t.Errorf("Expected get namespaces, got: %v", action)
	}
}

// matchError returns true if errors match, false if they don't, compares by error message only for convenience which should be sufficient for these tests
func matchErrors(e1, e2 error) bool {
	if e1 == nil && e2 == nil {
		return true
	}
	if e1 != nil && e2 != nil {
		return e1.Error() == e2.Error()
	}
	return false
}

// testServerAndClientConfig returns a server that listens and a config that can reference it
func testServerAndClientConfig(handler func(http.ResponseWriter, *http.Request)) (*httptest.Server, *restclient.Config) {
	srv := httptest.NewServer(http.HandlerFunc(handler))
	config := &restclient.Config{
		Host: srv.URL,
	}
	return srv, config
}

// fakeAction records information about requests to aid in testing.
type fakeAction struct {
	method string
	path   string
}

// String returns method=path to aid in testing
func (f *fakeAction) String() string {
	return strings.Join([]string{f.method, f.path}, "=")
}

// fakeActionHandler holds a list of fakeActions received
type fakeActionHandler struct {
	// statusCode returned by this handler
	statusCode int

	lock    sync.Mutex
	actions []fakeAction
}

// ServeHTTP logs the action that occurred and always returns the associated status code
func (f *fakeActionHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.actions = append(f.actions, fakeAction{method: request.Method, path: request.URL.Path})
	response.Header().Set("Content-Type", runtime.ContentTypeJSON)
	response.WriteHeader(f.statusCode)
	response.Write([]byte("{\"apiVersion\": \"v1\", \"kind\": \"List\",\"items\":null}"))
}

// testResources returns a mocked up set of resources across different api groups for testing namespace controller.
func testResources() []*metav1.APIResourceList {
	results := []*metav1.APIResourceList{
		{
			GroupVersion: "v1",
			APIResources: []metav1.APIResource{
				{
					Name:       "pods",
					Namespaced: true,
					Kind:       "Pod",
					Verbs:      []string{"get", "list", "delete", "deletecollection", "create", "update"},
				},
				{
					Name:       "services",
					Namespaced: true,
					Kind:       "Service",
					Verbs:      []string{"get", "list", "delete", "deletecollection", "create", "update"},
				},
			},
		},
		{
			GroupVersion: "apps/v1",
			APIResources: []metav1.APIResource{
				{
					Name:       "deployments",
					Namespaced: true,
					Kind:       "Deployment",
					Verbs:      []string{"get", "list", "delete", "deletecollection", "create", "update"},
				},
			},
		},
	}
	return results
}
<|endoftext|>


<commit_before>package goat

import (
	"bencode"
	"encoding/binary"
	"net"
)

// Tracker announce request
func TrackerAnnounce(query map[string]string, resChan chan []byte) {
	// Validate required parameter input
	required := []string{"info_hash", "peer_id", "ip", "port", "uploaded", "downloaded", "left"}
	for _, r := range required {
		if _, ok := query[r]; !ok {
			TrackerError(resChan, "missing required parameter: "+r)
		}
	}

	// Store announce information

	// Fetch peer information

	// Don't use compact announce by default
	compact := false
	if _, ok := query["compact"]; ok && query["compact"] == "1" {
		compact = true
	}

	// Fake tracker announce response
	resChan <- fakeAnnounceResponse(compact)
}

// Report a bencoded []byte response as specified by input string
func TrackerError(resChan chan []byte, err string) {
	resChan <- bencode.EncDictMap(map[string][]byte{
		"failure reason": bencode.EncString(err),
		"interval":       bencode.EncInt(3600),
		"min interval":   bencode.EncInt(1800),
	})
}

// Generate a fake announce response
func fakeAnnounceResponse(compact bool) []byte {
	res := map[string][]byte{
		// complete, downloaded, incomplete: used to report client download statistics
		// min interval, interval: used to tell clients how often to announce
		"complete":     bencode.EncInt(360),
		"downloaded":   bencode.EncInt(1468),
		"incomplete":   bencode.EncInt(1),
		"interval":     bencode.EncInt(3600),
		"min interval": bencode.EncInt(1800),
	}

	// Send a compact response if requested
	if compact {
		res["peers"] = bencode.EncBytes(compactPeerList())
	} else {
		// peers: list of dictionaries
		res["peers"] = bencode.EncList([][]byte{
			// peer dictionary: contains peer id, ip, and port of a peer
			bencode.EncDictMap(map[string][]byte{
				"peer id": bencode.EncString("ABCDEF0123456789ABCD"),
				"ip":      bencode.EncString("127.0.0.1"),
				"port":    bencode.EncInt(6881),
			}),
			bencode.EncDictMap(map[string][]byte{
				"peer id": bencode.EncString("0123456789ABCDEF0123"),
				"ip":      bencode.EncString("127.0.0.1"),
				"port":    bencode.EncInt(6882),
			}),
		})
	}

	return bencode.EncDictMap(res)
}

// Save for later: Generate a compact peer list in binary format
func compactPeerList() []byte {
	// Empty byte buffer
	buf := []byte("")

	// Add a bunch of fake peers to list
	for i := 0; i < 5; i++ {

		// Compact peers into binary format: ip ip ip ip port port
		ip := [4]byte{}
		binary.BigEndian.PutUint32(ip[:], binary.BigEndian.Uint32(net.ParseIP("255.255.255.255").To4()))
		port := [2]byte{}
		binary.BigEndian.PutUint16(port[:], 8080)

		// Append to end of buffer
		buf = append(buf[:], append(ip[:], port[:]...)...)
	}

	return buf
}
<commit_msg>Add support for numwant parameter<commit_after>package goat

import (
	"bencode"
	"encoding/binary"
	"net"
	"strconv"
)

// Tracker announce request
func TrackerAnnounce(query map[string]string, resChan chan []byte) {
	// Validate required parameter input
	required := []string{"info_hash", "peer_id", "ip", "port", "uploaded", "downloaded", "left"}
	for _, r := range required {
		if _, ok := query[r]; !ok {
			TrackerError(resChan, "missing required parameter: "+r)
		}
	}

	// Store announce information

	// Fetch peer information

	// Check for numwant parameter, return up to that number of peers
	// Default is 50 per protocol
	numwant := 50
	if _, ok := query["numwant"]; ok {
		// Verify numwant is an integer
		num, err := strconv.Atoi(query["numwant"])
		if err == nil {
			numwant = num
		}
	}

	// Don't use compact announce by default
	compact := false
	if _, ok := query["compact"]; ok && query["compact"] == "1" {
		compact = true
	}

	// Fake tracker announce response
	resChan <- fakeAnnounceResponse(compact, numwant)
}

// Report a bencoded []byte response as specified by input string
func TrackerError(resChan chan []byte, err string) {
	resChan <- bencode.EncDictMap(map[string][]byte{
		"failure reason": bencode.EncString(err),
		"interval":       bencode.EncInt(3600),
		"min interval":   bencode.EncInt(1800),
	})
}

// Generate a fake announce response
func fakeAnnounceResponse(compact bool, numwant int) []byte {
	// For now, we completely ignore numwant
	_ = numwant

	res := map[string][]byte{
		// complete, downloaded, incomplete: used to report client download statistics
		// min interval, interval: used to tell clients how often to announce
		"complete":     bencode.EncInt(360),
		"downloaded":   bencode.EncInt(1468),
		"incomplete":   bencode.EncInt(1),
		"interval":     bencode.EncInt(3600),
		"min interval": bencode.EncInt(1800),
	}

	// Send a compact response if requested
	if compact {
		res["peers"] = bencode.EncBytes(compactPeerList())
	} else {
		// peers: list of dictionaries
		res["peers"] = bencode.EncList([][]byte{
			// peer dictionary: contains peer id, ip, and port of a peer
			bencode.EncDictMap(map[string][]byte{
				"peer id": bencode.EncString("ABCDEF0123456789ABCD"),
				"ip":      bencode.EncString("127.0.0.1"),
				"port":    bencode.EncInt(6881),
			}),
			bencode.EncDictMap(map[string][]byte{
				"peer id": bencode.EncString("0123456789ABCDEF0123"),
				"ip":      bencode.EncString("127.0.0.1"),
				"port":    bencode.EncInt(6882),
			}),
		})
	}

	return bencode.EncDictMap(res)
}

// Save for later: Generate a compact peer list in binary format
func compactPeerList() []byte {
	// Empty byte buffer
	buf := []byte("")

	// Add a bunch of fake peers to list
	for i := 0; i < 5; i++ {

		// Compact peers into binary format: ip ip ip ip port port
		ip := [4]byte{}
		binary.BigEndian.PutUint32(ip[:], binary.BigEndian.Uint32(net.ParseIP("255.255.255.255").To4()))
		port := [2]byte{}
		binary.BigEndian.PutUint16(port[:], 8080)

		// Append to end of buffer
		buf = append(buf[:], append(ip[:], port[:]...)...)
	}

	return buf
}
<|endoftext|>


<commit_before>package workloads

import (
	"sync"
	"math"
	"math/rand"
	"strconv"
	"bytes"
	"time"

	"databases"
)

type DefaultWorkload struct {
	Config Config
	DeletedItems int64
}

func (workload *DefaultWorkload) Init(config Config) {
	workload.Config = config
}

// Generate new *unique* key
func (workload *DefaultWorkload) GenerateNewKey(currentRecords int64) string {
	strCurrentRecords := strconv.FormatInt(currentRecords, 10)
	return Hash(strCurrentRecords)
}

// Generate random key from current key space
func (workload *DefaultWorkload) GenerateExistingKey(currentRecords int64) string {
	rand.Seed(time.Now().UnixNano())
	randRecord := workload.DeletedItems + rand.Int63n(currentRecords - workload.DeletedItems)
	strRandRecord := strconv.FormatInt(randRecord, 10)
	return Hash(strRandRecord)
}

// Generate sequential key for removal
func (workload *DefaultWorkload) GenerateKeyForRemoval() string {
	keyForRemoval := strconv.FormatInt(workload.DeletedItems, 10)
	workload.DeletedItems++
	return Hash(keyForRemoval)
}

// Generate value with deterministic indexable fields and arbitrary body
func (workload *DefaultWorkload) GenerateValue(key string, indexableFields, size int) map[string]interface{} {
	// Hex lengh is 32 characters, so only 22 indexable fields are allowed
	if indexableFields >= 20 {
		panic("Too much fields! It must be less than 20")
	}
	// Gererate indexable fields (shifting over key name)
	value := make(map[string]interface{})
	for i := 0; i < indexableFields; i++ {
		fieldName := "field" + strconv.Itoa(i)
		value[fieldName] = fieldName + "-" +key[i:i + 10]
	}
	// Generate value body in order to meet value size specification
	fieldName := "field" + strconv.Itoa(indexableFields)
	var buffer bytes.Buffer
	var bodyHash string = Hash(key)
	iterations := (size - len(fieldName + "-" + key[:10]) * indexableFields) / 32
	for i := 0; i < iterations; i++ {
		buffer.WriteString(bodyHash)
	}
	value[fieldName] = buffer.String()
	return value
}

func (workload *DefaultWorkload) GenerateQuery(indexableFields int, currentRecords int64) (string, string, int) {
	i := rand.Intn(indexableFields)
	fieldName := "field" + strconv.Itoa(i)
	fieldValue := fieldName + "-" + workload.GenerateExistingKey(currentRecords)[i:i + 10]
	limit := 10 + rand.Intn(10)
	return fieldName, fieldValue, limit
}

// Generate slice of shuffled characters (CRUD-Q shorthands)
func (workload *DefaultWorkload) PrepareBatch() []string {
	operations := make([]string, 0, 100)
	randOperations := make([]string, 100, 100)
	for i := 0; i < workload.Config.CreatePercentage; i++ {
		operations = append(operations, "c")
	}
	for i := 0; i < workload.Config.ReadPercentage; i++ {
		operations = append(operations, "r")
	}
	for i := 0; i < workload.Config.UpdatePercentage; i++ {
		operations = append(operations, "u")
	}
	for i := 0; i < workload.Config.DeletePercentage; i++ {
		operations = append(operations, "d")
	}
	for i := 0; i < workload.Config.QueryPercentage; i++ {
		operations = append(operations, "q")
	}
	if len(operations) != 100 {
		panic("Wrong workload configuration: sum of percentages is not equal 100")
	}
	for i, randI := range rand.Perm(100) {
		randOperations[i] = operations[randI]
	}
	return randOperations
}

// Sequentially send 100 requests
func (workload *DefaultWorkload) DoBatch(db databases.Database, state *State) {
	var key string
	var value map[string]interface{}
	var status error
	var batch = workload.PrepareBatch()

	for _, v := range batch {
		// Increase number of passed operarions *before* batch execution in order to normally share key space with
		// other workers
		state.Operations ++

		switch v {
		case "c":
			state.Records ++
			key = workload.GenerateNewKey(state.Records)
			value = workload.GenerateValue(key, workload.Config.IndexableFields, workload.Config.ValueSize)
			status = db.Create(key, value)
		case "r":
			key = workload.GenerateExistingKey(state.Records)
			status = db.Read(key)
		case "u":
			key = workload.GenerateExistingKey(state.Records)
			value = workload.GenerateValue(key, workload.Config.IndexableFields, workload.Config.ValueSize)
			status = db.Update(key, value)
		case "d":
			key = workload.GenerateKeyForRemoval()
			status = db.Delete(key)
		case "q":
			fieldName, fieldValue, limit := workload.GenerateQuery(workload.Config.IndexableFields, state.Records)
			status = db.Query(fieldName, fieldValue, limit)
		}
		if status != nil {
			state.Errors[v] ++
			state.Errors["total"] ++
		}
	}
}

// Continuously run batches of operations
func (workload *DefaultWorkload) RunWorkload(database databases.Database, state *State, wg *sync.WaitGroup) {
	// Calculate target time for batch execution. +Inf if not defined
	targetBatchTime := float64(100) / float64(workload.Config.TargetThroughput)

	for state.Operations < workload.Config.Operations {
		// Send batch of request and measure execution time
		t0 := time.Now()
		workload.DoBatch(database, state)
		t1 := time.Now()

		// Sleep if necessary
		if !math.IsInf(targetBatchTime, 0) {
			actualBatchTime := t1.Sub(t0).Seconds()
			sleepTime := (targetBatchTime - actualBatchTime) * math.Pow(10, 9)
			if sleepTime > 0 {
				time.Sleep(time.Duration(sleepTime) * time.Nanosecond)
			}
		}
	}
	wg.Done()
}
<commit_msg>fix redundant batch operations<commit_after>package workloads

import (
	"sync"
	"math"
	"math/rand"
	"strconv"
	"bytes"
	"time"

	"databases"
)

type DefaultWorkload struct {
	Config Config
	DeletedItems int64
}

func (workload *DefaultWorkload) Init(config Config) {
	workload.Config = config
}

// Generate new *unique* key
func (workload *DefaultWorkload) GenerateNewKey(currentRecords int64) string {
	strCurrentRecords := strconv.FormatInt(currentRecords, 10)
	return Hash(strCurrentRecords)
}

// Generate random key from current key space
func (workload *DefaultWorkload) GenerateExistingKey(currentRecords int64) string {
	rand.Seed(time.Now().UnixNano())
	randRecord := workload.DeletedItems + rand.Int63n(currentRecords - workload.DeletedItems)
	strRandRecord := strconv.FormatInt(randRecord, 10)
	return Hash(strRandRecord)
}

// Generate sequential key for removal
func (workload *DefaultWorkload) GenerateKeyForRemoval() string {
	keyForRemoval := strconv.FormatInt(workload.DeletedItems, 10)
	workload.DeletedItems++
	return Hash(keyForRemoval)
}

// Generate value with deterministic indexable fields and arbitrary body
func (workload *DefaultWorkload) GenerateValue(key string, indexableFields, size int) map[string]interface{} {
	// Hex lengh is 32 characters, so only 22 indexable fields are allowed
	if indexableFields >= 20 {
		panic("Too much fields! It must be less than 20")
	}
	// Gererate indexable fields (shifting over key name)
	value := make(map[string]interface{})
	for i := 0; i < indexableFields; i++ {
		fieldName := "field" + strconv.Itoa(i)
		value[fieldName] = fieldName + "-" +key[i:i + 10]
	}
	// Generate value body in order to meet value size specification
	fieldName := "field" + strconv.Itoa(indexableFields)
	var buffer bytes.Buffer
	var bodyHash string = Hash(key)
	iterations := (size - len(fieldName + "-" + key[:10]) * indexableFields) / 32
	for i := 0; i < iterations; i++ {
		buffer.WriteString(bodyHash)
	}
	value[fieldName] = buffer.String()
	return value
}

func (workload *DefaultWorkload) GenerateQuery(indexableFields int, currentRecords int64) (string, string, int) {
	i := rand.Intn(indexableFields)
	fieldName := "field" + strconv.Itoa(i)
	fieldValue := fieldName + "-" + workload.GenerateExistingKey(currentRecords)[i:i + 10]
	limit := 10 + rand.Intn(10)
	return fieldName, fieldValue, limit
}

// Generate slice of shuffled characters (CRUD-Q shorthands)
func (workload *DefaultWorkload) PrepareBatch() []string {
	operations := make([]string, 0, 100)
	randOperations := make([]string, 100, 100)
	for i := 0; i < workload.Config.CreatePercentage; i++ {
		operations = append(operations, "c")
	}
	for i := 0; i < workload.Config.ReadPercentage; i++ {
		operations = append(operations, "r")
	}
	for i := 0; i < workload.Config.UpdatePercentage; i++ {
		operations = append(operations, "u")
	}
	for i := 0; i < workload.Config.DeletePercentage; i++ {
		operations = append(operations, "d")
	}
	for i := 0; i < workload.Config.QueryPercentage; i++ {
		operations = append(operations, "q")
	}
	if len(operations) != 100 {
		panic("Wrong workload configuration: sum of percentages is not equal 100")
	}
	for i, randI := range rand.Perm(100) {
		randOperations[i] = operations[randI]
	}
	return randOperations
}

// Sequentially send 100 requests
func (workload *DefaultWorkload) DoBatch(db databases.Database, state *State) {
	var key string
	var value map[string]interface{}
	var status error
	var batch = workload.PrepareBatch()

	for _, v := range batch {
		// Increase number of passed operarions *before* batch execution in order to normally share key space with
		// other workers
		if state.Operations < workload.Config.Operations {
			state.Operations ++

			switch v {
			case "c":
				state.Records ++
				key = workload.GenerateNewKey(state.Records)
				value = workload.GenerateValue(key, workload.Config.IndexableFields, workload.Config.ValueSize)
				status = db.Create(key, value)
			case "r":
				key = workload.GenerateExistingKey(state.Records)
				status = db.Read(key)
			case "u":
				key = workload.GenerateExistingKey(state.Records)
				value = workload.GenerateValue(key, workload.Config.IndexableFields, workload.Config.ValueSize)
				status = db.Update(key, value)
			case "d":
				key = workload.GenerateKeyForRemoval()
				status = db.Delete(key)
			case "q":
				fieldName, fieldValue, limit := workload.GenerateQuery(workload.Config.IndexableFields, state.Records)
				status = db.Query(fieldName, fieldValue, limit)
			}
			if status != nil {
				state.Errors[v] ++
				state.Errors["total"] ++
			}
		}
	}
}

// Continuously run batches of operations
func (workload *DefaultWorkload) RunWorkload(database databases.Database, state *State, wg *sync.WaitGroup) {
	// Calculate target time for batch execution. +Inf if not defined
	targetBatchTime := float64(100) / float64(workload.Config.TargetThroughput)

	for state.Operations < workload.Config.Operations {
		// Send batch of request and measure execution time
		t0 := time.Now()
		workload.DoBatch(database, state)
		t1 := time.Now()

		// Sleep if necessary
		if !math.IsInf(targetBatchTime, 0) {
			actualBatchTime := t1.Sub(t0).Seconds()
			sleepTime := (targetBatchTime - actualBatchTime) * math.Pow(10, 9)
			if sleepTime > 0 {
				time.Sleep(time.Duration(sleepTime) * time.Nanosecond)
			}
		}
	}
	wg.Done()
}
<|endoftext|>


<commit_before>package context

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/silenceper/wechat/v2/util"
)

const (
	componentAccessTokenURL = "https://api.weixin.qq.com/cgi-bin/component/api_component_token"
	getPreCodeURL           = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=%s"
	queryAuthURL            = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=%s"
	refreshTokenURL         = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=%s"
	getComponentInfoURL     = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=%s"
	//TODO 获取授权方选项信息
	//getComponentConfigURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=%s"
	//TODO 获取已授权的账号信息
	//getuthorizerListURL = "POST https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=%s"
)

// ComponentAccessToken 第三方平台
type ComponentAccessToken struct {
	AccessToken string `json:"component_access_token"`
	ExpiresIn   int64  `json:"expires_in"`
}

// GetComponentAccessToken 获取 ComponentAccessToken
func (ctx *Context) GetComponentAccessToken() (string, error) {
	accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
	val := ctx.Cache.Get(accessTokenCacheKey)
	if val == nil {
		return "", fmt.Errorf("cann't get component access token")
	}
	return val.(string), nil
}

// SetComponentAccessToken 通过component_verify_ticket 获取 ComponentAccessToken
func (ctx *Context) SetComponentAccessToken(verifyTicket string) (*ComponentAccessToken, error) {
	body := map[string]string{
		"component_appid":         ctx.AppID,
		"component_appsecret":     ctx.AppSecret,
		"component_verify_ticket": verifyTicket,
	}
	respBody, err := util.PostJSON(componentAccessTokenURL, body)
	if err != nil {
		return nil, err
	}

	at := &ComponentAccessToken{}
	if err := json.Unmarshal(respBody, at); err != nil {
		return nil, err
	}

	accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
	expires := at.ExpiresIn - 1500
	if err := ctx.Cache.Set(accessTokenCacheKey, at.AccessToken, time.Duration(expires)*time.Second); err != nil {
		return nil, nil
	}
	return at, nil
}

// GetPreCode 获取预授权码
func (ctx *Context) GetPreCode() (string, error) {
	cat, err := ctx.GetComponentAccessToken()
	if err != nil {
		return "", err
	}
	req := map[string]string{
		"component_appid": ctx.AppID,
	}
	uri := fmt.Sprintf(getPreCodeURL, cat)
	body, err := util.PostJSON(uri, req)
	if err != nil {
		return "", err
	}

	var ret struct {
		PreCode string `json:"pre_auth_code"`
	}
	if err := json.Unmarshal(body, &ret); err != nil {
		return "", err
	}

	return ret.PreCode, nil
}

// ID 微信返回接口中各种类型字段
type ID struct {
	ID int `json:"id"`
}

// AuthBaseInfo 授权的基本信息
type AuthBaseInfo struct {
	AuthrAccessToken
	FuncInfo []AuthFuncInfo `json:"func_info"`
}

// AuthFuncInfo 授权的接口内容
type AuthFuncInfo struct {
	FuncscopeCategory ID `json:"funcscope_category"`
}

// AuthrAccessToken 授权方AccessToken
type AuthrAccessToken struct {
	Appid        string `json:"authorizer_appid"`
	AccessToken  string `json:"authorizer_access_token"`
	ExpiresIn    int64  `json:"expires_in"`
	RefreshToken string `json:"authorizer_refresh_token"`
}

// QueryAuthCode 使用授权码换取公众号或小程序的接口调用凭据和授权信息
func (ctx *Context) QueryAuthCode(authCode string) (*AuthBaseInfo, error) {
	cat, err := ctx.GetComponentAccessToken()
	if err != nil {
		return nil, err
	}

	req := map[string]string{
		"component_appid":    ctx.AppID,
		"authorization_code": authCode,
	}
	uri := fmt.Sprintf(queryAuthURL, cat)
	body, err := util.PostJSON(uri, req)
	if err != nil {
		return nil, err
	}

	var ret struct {
		util.CommonError
		Info *AuthBaseInfo `json:"authorization_info"`
	}

	if err := json.Unmarshal(body, &ret); err != nil {
		return nil, err
	}
	if ret.ErrCode != 0 {
		err = fmt.Errorf("QueryAuthCode error : errcode=%v , errmsg=%v", ret.ErrCode, ret.ErrMsg)
		return nil, err
	}
	return ret.Info, nil
}

// RefreshAuthrToken 获取（刷新）授权公众号或小程序的接口调用凭据（令牌）
func (ctx *Context) RefreshAuthrToken(appid, refreshToken string) (*AuthrAccessToken, error) {
	cat, err := ctx.GetComponentAccessToken()
	if err != nil {
		return nil, err
	}

	req := map[string]string{
		"component_appid":          ctx.AppID,
		"authorizer_appid":         appid,
		"authorizer_refresh_token": refreshToken,
	}
	uri := fmt.Sprintf(refreshTokenURL, cat)
	body, err := util.PostJSON(uri, req)
	if err != nil {
		return nil, err
	}

	ret := &AuthrAccessToken{}
	if err := json.Unmarshal(body, ret); err != nil {
		return nil, err
	}

	authrTokenKey := "authorizer_access_token_" + appid
	if err := ctx.Cache.Set(authrTokenKey, ret.AccessToken, time.Minute*80); err != nil {
		return nil, err
	}
	return ret, nil
}

// GetAuthrAccessToken 获取授权方AccessToken
func (ctx *Context) GetAuthrAccessToken(appid string) (string, error) {
	authrTokenKey := "authorizer_access_token_" + appid
	val := ctx.Cache.Get(authrTokenKey)
	if val == nil {
		return "", fmt.Errorf("cannot get authorizer %s access token", appid)
	}
	return val.(string), nil
}

// AuthorizerInfo 授权方详细信息
type AuthorizerInfo struct {
	NickName        string `json:"nick_name"`
	HeadImg         string `json:"head_img"`
	ServiceTypeInfo ID     `json:"service_type_info"`
	VerifyTypeInfo  ID     `json:"verify_type_info"`
	UserName        string `json:"user_name"`
	PrincipalName   string `json:"principal_name"`
	BusinessInfo    struct {
		OpenStore string `json:"open_store"`
		OpenScan  string `json:"open_scan"`
		OpenPay   string `json:"open_pay"`
		OpenCard  string `json:"open_card"`
		OpenShake string `json:"open_shake"`
	}
	Alias     string `json:"alias"`
	QrcodeURL string `json:"qrcode_url"`
}

// GetAuthrInfo 获取授权方的帐号基本信息
func (ctx *Context) GetAuthrInfo(appid string) (*AuthorizerInfo, *AuthBaseInfo, error) {
	cat, err := ctx.GetComponentAccessToken()
	if err != nil {
		return nil, nil, err
	}

	req := map[string]string{
		"component_appid":  ctx.AppID,
		"authorizer_appid": appid,
	}

	uri := fmt.Sprintf(getComponentInfoURL, cat)
	body, err := util.PostJSON(uri, req)
	if err != nil {
		return nil, nil, err
	}

	var ret struct {
		AuthorizerInfo    *AuthorizerInfo `json:"authorizer_info"`
		AuthorizationInfo *AuthBaseInfo   `json:"authorization_info"`
	}
	if err := json.Unmarshal(body, &ret); err != nil {
		return nil, nil, err
	}

	return ret.AuthorizerInfo, ret.AuthorizationInfo, nil
}
<commit_msg>feat: 增加获取第三方公众号授权链接的接口 (#310)<commit_after>package context

import (
	"encoding/json"
	"fmt"
	"net/url"
	"time"

	"github.com/silenceper/wechat/v2/util"
)

const (
	componentAccessTokenURL = "https://api.weixin.qq.com/cgi-bin/component/api_component_token"
	getPreCodeURL           = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=%s"
	queryAuthURL            = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=%s"
	refreshTokenURL         = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=%s"
	getComponentInfoURL     = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=%s"
	componentLoginURL       = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=%d&biz_appid=%s"
	bindComponentURL        = "https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&auth_type=%d&no_scan=1&component_appid=%s&pre_auth_code=%s&redirect_uri=%s&biz_appid=%s#wechat_redirect"
	//TODO 获取授权方选项信息
	//getComponentConfigURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=%s"
	//TODO 获取已授权的账号信息
	//getuthorizerListURL = "POST https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=%s"
)

// ComponentAccessToken 第三方平台
type ComponentAccessToken struct {
	AccessToken string `json:"component_access_token"`
	ExpiresIn   int64  `json:"expires_in"`
}

// GetComponentAccessToken 获取 ComponentAccessToken
func (ctx *Context) GetComponentAccessToken() (string, error) {
	accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
	val := ctx.Cache.Get(accessTokenCacheKey)
	if val == nil {
		return "", fmt.Errorf("cann't get component access token")
	}
	return val.(string), nil
}

// SetComponentAccessToken 通过component_verify_ticket 获取 ComponentAccessToken
func (ctx *Context) SetComponentAccessToken(verifyTicket string) (*ComponentAccessToken, error) {
	body := map[string]string{
		"component_appid":         ctx.AppID,
		"component_appsecret":     ctx.AppSecret,
		"component_verify_ticket": verifyTicket,
	}
	respBody, err := util.PostJSON(componentAccessTokenURL, body)
	if err != nil {
		return nil, err
	}

	at := &ComponentAccessToken{}
	if err := json.Unmarshal(respBody, at); err != nil {
		return nil, err
	}

	accessTokenCacheKey := fmt.Sprintf("component_access_token_%s", ctx.AppID)
	expires := at.ExpiresIn - 1500
	if err := ctx.Cache.Set(accessTokenCacheKey, at.AccessToken, time.Duration(expires)*time.Second); err != nil {
		return nil, nil
	}
	return at, nil
}

// GetPreCode 获取预授权码
func (ctx *Context) GetPreCode() (string, error) {
	cat, err := ctx.GetComponentAccessToken()
	if err != nil {
		return "", err
	}
	req := map[string]string{
		"component_appid": ctx.AppID,
	}
	uri := fmt.Sprintf(getPreCodeURL, cat)
	body, err := util.PostJSON(uri, req)
	if err != nil {
		return "", err
	}

	var ret struct {
		PreCode string `json:"pre_auth_code"`
	}
	if err := json.Unmarshal(body, &ret); err != nil {
		return "", err
	}

	return ret.PreCode, nil
}

// GetComponentLoginPage 获取第三方公众号授权链接(扫码授权)
func (ctx *Context) GetComponentLoginPage(redirectURI string, authType int, bizAppID string) (string, error) {
	code, err := ctx.GetPreCode()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf(componentLoginURL, ctx.AppID, code, url.QueryEscape(redirectURI), authType, bizAppID), nil
}

// GetBindComponentURL 获取第三方公众号授权链接(链接跳转，适用移动端)
func (ctx *Context) GetBindComponentURL(redirectURI string, authType int, bizAppID string) (string, error) {
	code, err := ctx.GetPreCode()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf(bindComponentURL, authType, ctx.AppID, code, url.QueryEscape(redirectURI), bizAppID), nil
}

// ID 微信返回接口中各种类型字段
type ID struct {
	ID int `json:"id"`
}

// AuthBaseInfo 授权的基本信息
type AuthBaseInfo struct {
	AuthrAccessToken
	FuncInfo []AuthFuncInfo `json:"func_info"`
}

// AuthFuncInfo 授权的接口内容
type AuthFuncInfo struct {
	FuncscopeCategory ID `json:"funcscope_category"`
}

// AuthrAccessToken 授权方AccessToken
type AuthrAccessToken struct {
	Appid        string `json:"authorizer_appid"`
	AccessToken  string `json:"authorizer_access_token"`
	ExpiresIn    int64  `json:"expires_in"`
	RefreshToken string `json:"authorizer_refresh_token"`
}

// QueryAuthCode 使用授权码换取公众号或小程序的接口调用凭据和授权信息
func (ctx *Context) QueryAuthCode(authCode string) (*AuthBaseInfo, error) {
	cat, err := ctx.GetComponentAccessToken()
	if err != nil {
		return nil, err
	}

	req := map[string]string{
		"component_appid":    ctx.AppID,
		"authorization_code": authCode,
	}
	uri := fmt.Sprintf(queryAuthURL, cat)
	body, err := util.PostJSON(uri, req)
	if err != nil {
		return nil, err
	}

	var ret struct {
		util.CommonError
		Info *AuthBaseInfo `json:"authorization_info"`
	}

	if err := json.Unmarshal(body, &ret); err != nil {
		return nil, err
	}
	if ret.ErrCode != 0 {
		err = fmt.Errorf("QueryAuthCode error : errcode=%v , errmsg=%v", ret.ErrCode, ret.ErrMsg)
		return nil, err
	}
	return ret.Info, nil
}

// RefreshAuthrToken 获取（刷新）授权公众号或小程序的接口调用凭据（令牌）
func (ctx *Context) RefreshAuthrToken(appid, refreshToken string) (*AuthrAccessToken, error) {
	cat, err := ctx.GetComponentAccessToken()
	if err != nil {
		return nil, err
	}

	req := map[string]string{
		"component_appid":          ctx.AppID,
		"authorizer_appid":         appid,
		"authorizer_refresh_token": refreshToken,
	}
	uri := fmt.Sprintf(refreshTokenURL, cat)
	body, err := util.PostJSON(uri, req)
	if err != nil {
		return nil, err
	}

	ret := &AuthrAccessToken{}
	if err := json.Unmarshal(body, ret); err != nil {
		return nil, err
	}

	authrTokenKey := "authorizer_access_token_" + appid
	if err := ctx.Cache.Set(authrTokenKey, ret.AccessToken, time.Minute*80); err != nil {
		return nil, err
	}
	return ret, nil
}

// GetAuthrAccessToken 获取授权方AccessToken
func (ctx *Context) GetAuthrAccessToken(appid string) (string, error) {
	authrTokenKey := "authorizer_access_token_" + appid
	val := ctx.Cache.Get(authrTokenKey)
	if val == nil {
		return "", fmt.Errorf("cannot get authorizer %s access token", appid)
	}
	return val.(string), nil
}

// AuthorizerInfo 授权方详细信息
type AuthorizerInfo struct {
	NickName        string `json:"nick_name"`
	HeadImg         string `json:"head_img"`
	ServiceTypeInfo ID     `json:"service_type_info"`
	VerifyTypeInfo  ID     `json:"verify_type_info"`
	UserName        string `json:"user_name"`
	PrincipalName   string `json:"principal_name"`
	BusinessInfo    struct {
		OpenStore string `json:"open_store"`
		OpenScan  string `json:"open_scan"`
		OpenPay   string `json:"open_pay"`
		OpenCard  string `json:"open_card"`
		OpenShake string `json:"open_shake"`
	}
	Alias     string `json:"alias"`
	QrcodeURL string `json:"qrcode_url"`
}

// GetAuthrInfo 获取授权方的帐号基本信息
func (ctx *Context) GetAuthrInfo(appid string) (*AuthorizerInfo, *AuthBaseInfo, error) {
	cat, err := ctx.GetComponentAccessToken()
	if err != nil {
		return nil, nil, err
	}

	req := map[string]string{
		"component_appid":  ctx.AppID,
		"authorizer_appid": appid,
	}

	uri := fmt.Sprintf(getComponentInfoURL, cat)
	body, err := util.PostJSON(uri, req)
	if err != nil {
		return nil, nil, err
	}

	var ret struct {
		AuthorizerInfo    *AuthorizerInfo `json:"authorizer_info"`
		AuthorizationInfo *AuthBaseInfo   `json:"authorization_info"`
	}
	if err := json.Unmarshal(body, &ret); err != nil {
		return nil, nil, err
	}

	return ret.AuthorizerInfo, ret.AuthorizationInfo, nil
}
<|endoftext|>


<commit_before>package tcpreuse

import (
	"net"
	"runtime"
	"testing"

	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr/net"
)

var loopbackV4, _ = ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
var loopbackV6, _ = ma.NewMultiaddr("/ip6/::1/tcp/0")
var unspecV6, _ = ma.NewMultiaddr("/ip6/::/tcp/0")
var unspecV4, _ = ma.NewMultiaddr("/ip4/0.0.0.0/tcp/0")

var globalV4 ma.Multiaddr
var globalV6 ma.Multiaddr

func init() {
	addrs, err := manet.InterfaceMultiaddrs()
	if err != nil {
		return
	}
	for _, addr := range addrs {
		if !manet.IsIP6LinkLocal(addr) && !manet.IsIPLoopback(addr) {
			tcp, _ := ma.NewMultiaddr("/tcp/0")
			switch addr.Protocols()[0].Code {
			case ma.P_IP4:
				if globalV4 == nil {
					globalV4 = addr.Encapsulate(tcp)
				}
			case ma.P_IP6:
				if globalV6 == nil {
					globalV6 = addr.Encapsulate(tcp)
				}
			}
		}
	}
}

func acceptOne(t *testing.T, listener manet.Listener) <-chan interface{} {
	t.Helper()
	done := make(chan interface{}, 1)
	go func() {
		defer close(done)
		done <- nil
		c, err := listener.Accept()
		if err != nil {
			t.Error(err)
			return
		}
		c.Close()
	}()
	<-done
	return done
}

func dialOne(t *testing.T, tr *Transport, listener manet.Listener, expected ...int) int {
	t.Helper()

	done := acceptOne(t, listener)
	c, err := tr.Dial(listener.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	port := c.LocalAddr().(*net.TCPAddr).Port
	<-done
	c.Close()
	if len(expected) == 0 {
		return port
	}
	for _, p := range expected {
		if p == port {
			return port
		}
	}
	t.Errorf("dialed %s from %v. expected to dial from port %v", listener.Multiaddr(), c.LocalAddr(), expected)
	return 0
}

func TestNoneAndSingle(t *testing.T) {
	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	dialOne(t, &trB, listenerA)

	listenerB, err := trB.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB.Close()

	dialOne(t, &trB, listenerA, listenerB.Addr().(*net.TCPAddr).Port)
}

func TestTwoLocal(t *testing.T) {
	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	listenerB2, err := trB.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	dialOne(t, &trB, listenerA,
		listenerB1.Addr().(*net.TCPAddr).Port,
		listenerB2.Addr().(*net.TCPAddr).Port)
}

func TestGlobalPreferenceV4(t *testing.T) {
	if runtime.GOOS == "darwin" {
		t.Skip("This test is failing on OSX: https://github.com/libp2p/go-reuseport-transport/issues/33")
	}
	if globalV4 == nil {
		t.Skip("no global IPv4 addresses configured")
		return
	}
	t.Logf("when listening on %v, should prefer %v over %v", loopbackV4, loopbackV4, globalV4)
	testPrefer(t, loopbackV4, loopbackV4, globalV4)
	t.Logf("when listening on %v, should prefer %v over %v", loopbackV4, unspecV4, globalV4)
	testPrefer(t, loopbackV4, unspecV4, globalV4)

	t.Logf("when listening on %v, should prefer %v over %v", globalV4, unspecV4, loopbackV4)
	testPrefer(t, globalV4, unspecV4, loopbackV4)
}

func TestGlobalPreferenceV6(t *testing.T) {
	if runtime.GOOS == "darwin" {
		t.Skip("This test is failing on OSX: https://github.com/libp2p/go-reuseport-transport/issues/33")
	}
	if globalV6 == nil {
		t.Skip("no global IPv6 addresses configured")
		return
	}
	testPrefer(t, loopbackV6, loopbackV6, globalV6)
	testPrefer(t, loopbackV6, unspecV6, globalV6)

	testPrefer(t, globalV6, unspecV6, loopbackV6)
}

func TestLoopbackPreference(t *testing.T) {
	if runtime.GOOS == "darwin" {
		t.Skip("This test is failing on OSX: https://github.com/libp2p/go-reuseport-transport/issues/33")
	}
	testPrefer(t, loopbackV4, loopbackV4, unspecV4)
	testPrefer(t, loopbackV6, loopbackV6, unspecV6)
}

func testPrefer(t *testing.T, listen, prefer, avoid ma.Multiaddr) {
	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(listen)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(avoid)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	dialOne(t, &trB, listenerA, listenerB1.Addr().(*net.TCPAddr).Port)

	listenerB2, err := trB.Listen(prefer)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	dialOne(t, &trB, listenerA, listenerB2.Addr().(*net.TCPAddr).Port)

	// Closing the listener should reset the dialer.
	listenerB2.Close()

	dialOne(t, &trB, listenerA, listenerB1.Addr().(*net.TCPAddr).Port)
}

func TestV6V4(t *testing.T) {
	testUseFirst(t, loopbackV4, loopbackV4, loopbackV6)
	testUseFirst(t, loopbackV6, loopbackV6, loopbackV4)
}

func TestGlobalToGlobal(t *testing.T) {
	if globalV4 == nil {
		t.Skip("no globalV4 addresses configured")
		return
	}
	testUseFirst(t, globalV4, globalV4, loopbackV4)
	testUseFirst(t, globalV6, globalV6, loopbackV6)
}

func testUseFirst(t *testing.T, listen, use, never ma.Multiaddr) {
	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	// It works (random port)
	dialOne(t, &trB, listenerA)

	listenerB2, err := trB.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	// Uses globalV4 port.
	dialOne(t, &trB, listenerA, listenerB2.Addr().(*net.TCPAddr).Port)

	// Closing the listener should reset the dialer.
	listenerB2.Close()

	// It still works.
	dialOne(t, &trB, listenerA)
}

func TestDuplicateGlobal(t *testing.T) {
	if runtime.GOOS == "darwin" {
		t.Skip("This test is failing on OSX: https://github.com/libp2p/go-reuseport-transport/issues/33")
	}
	if globalV4 == nil {
		t.Skip("no globalV4 addresses configured")
		return
	}

	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	listenerB2, err := trB.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	// Check which port we're using
	port := dialOne(t, &trB, listenerA)

	// Check consistency
	for i := 0; i < 10; i++ {
		dialOne(t, &trB, listenerA, port)
	}
}
<commit_msg>disable failing TestV6V4 on OSX (#41)<commit_after>package tcpreuse

import (
	"net"
	"runtime"
	"testing"

	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr/net"
)

var loopbackV4, _ = ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
var loopbackV6, _ = ma.NewMultiaddr("/ip6/::1/tcp/0")
var unspecV6, _ = ma.NewMultiaddr("/ip6/::/tcp/0")
var unspecV4, _ = ma.NewMultiaddr("/ip4/0.0.0.0/tcp/0")

var globalV4 ma.Multiaddr
var globalV6 ma.Multiaddr

func init() {
	addrs, err := manet.InterfaceMultiaddrs()
	if err != nil {
		return
	}
	for _, addr := range addrs {
		if !manet.IsIP6LinkLocal(addr) && !manet.IsIPLoopback(addr) {
			tcp, _ := ma.NewMultiaddr("/tcp/0")
			switch addr.Protocols()[0].Code {
			case ma.P_IP4:
				if globalV4 == nil {
					globalV4 = addr.Encapsulate(tcp)
				}
			case ma.P_IP6:
				if globalV6 == nil {
					globalV6 = addr.Encapsulate(tcp)
				}
			}
		}
	}
}

func acceptOne(t *testing.T, listener manet.Listener) <-chan interface{} {
	t.Helper()
	done := make(chan interface{}, 1)
	go func() {
		defer close(done)
		done <- nil
		c, err := listener.Accept()
		if err != nil {
			t.Error(err)
			return
		}
		c.Close()
	}()
	<-done
	return done
}

func dialOne(t *testing.T, tr *Transport, listener manet.Listener, expected ...int) int {
	t.Helper()

	done := acceptOne(t, listener)
	c, err := tr.Dial(listener.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	port := c.LocalAddr().(*net.TCPAddr).Port
	<-done
	c.Close()
	if len(expected) == 0 {
		return port
	}
	for _, p := range expected {
		if p == port {
			return port
		}
	}
	t.Errorf("dialed %s from %v. expected to dial from port %v", listener.Multiaddr(), c.LocalAddr(), expected)
	return 0
}

func TestNoneAndSingle(t *testing.T) {
	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	dialOne(t, &trB, listenerA)

	listenerB, err := trB.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB.Close()

	dialOne(t, &trB, listenerA, listenerB.Addr().(*net.TCPAddr).Port)
}

func TestTwoLocal(t *testing.T) {
	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	listenerB2, err := trB.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	dialOne(t, &trB, listenerA,
		listenerB1.Addr().(*net.TCPAddr).Port,
		listenerB2.Addr().(*net.TCPAddr).Port)
}

func TestGlobalPreferenceV4(t *testing.T) {
	if runtime.GOOS == "darwin" {
		t.Skip("This test is failing on OSX: https://github.com/libp2p/go-reuseport-transport/issues/33")
	}
	if globalV4 == nil {
		t.Skip("no global IPv4 addresses configured")
		return
	}
	t.Logf("when listening on %v, should prefer %v over %v", loopbackV4, loopbackV4, globalV4)
	testPrefer(t, loopbackV4, loopbackV4, globalV4)
	t.Logf("when listening on %v, should prefer %v over %v", loopbackV4, unspecV4, globalV4)
	testPrefer(t, loopbackV4, unspecV4, globalV4)

	t.Logf("when listening on %v, should prefer %v over %v", globalV4, unspecV4, loopbackV4)
	testPrefer(t, globalV4, unspecV4, loopbackV4)
}

func TestGlobalPreferenceV6(t *testing.T) {
	if runtime.GOOS == "darwin" {
		t.Skip("This test is failing on OSX: https://github.com/libp2p/go-reuseport-transport/issues/33")
	}
	if globalV6 == nil {
		t.Skip("no global IPv6 addresses configured")
		return
	}
	testPrefer(t, loopbackV6, loopbackV6, globalV6)
	testPrefer(t, loopbackV6, unspecV6, globalV6)

	testPrefer(t, globalV6, unspecV6, loopbackV6)
}

func TestLoopbackPreference(t *testing.T) {
	if runtime.GOOS == "darwin" {
		t.Skip("This test is failing on OSX: https://github.com/libp2p/go-reuseport-transport/issues/33")
	}
	testPrefer(t, loopbackV4, loopbackV4, unspecV4)
	testPrefer(t, loopbackV6, loopbackV6, unspecV6)
}

func testPrefer(t *testing.T, listen, prefer, avoid ma.Multiaddr) {
	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(listen)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(avoid)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	dialOne(t, &trB, listenerA, listenerB1.Addr().(*net.TCPAddr).Port)

	listenerB2, err := trB.Listen(prefer)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	dialOne(t, &trB, listenerA, listenerB2.Addr().(*net.TCPAddr).Port)

	// Closing the listener should reset the dialer.
	listenerB2.Close()

	dialOne(t, &trB, listenerA, listenerB1.Addr().(*net.TCPAddr).Port)
}

func TestV6V4(t *testing.T) {
	if runtime.GOOS == "darwin" {
		t.Skip("This test is failing on OSX: https://github.com/libp2p/go-reuseport-transport/issues/40")
	}
	testUseFirst(t, loopbackV4, loopbackV4, loopbackV6)
	testUseFirst(t, loopbackV6, loopbackV6, loopbackV4)
}

func TestGlobalToGlobal(t *testing.T) {
	if globalV4 == nil {
		t.Skip("no globalV4 addresses configured")
		return
	}
	testUseFirst(t, globalV4, globalV4, loopbackV4)
	testUseFirst(t, globalV6, globalV6, loopbackV6)
}

func testUseFirst(t *testing.T, listen, use, never ma.Multiaddr) {
	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(loopbackV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	// It works (random port)
	dialOne(t, &trB, listenerA)

	listenerB2, err := trB.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	// Uses globalV4 port.
	dialOne(t, &trB, listenerA, listenerB2.Addr().(*net.TCPAddr).Port)

	// Closing the listener should reset the dialer.
	listenerB2.Close()

	// It still works.
	dialOne(t, &trB, listenerA)
}

func TestDuplicateGlobal(t *testing.T) {
	if runtime.GOOS == "darwin" {
		t.Skip("This test is failing on OSX: https://github.com/libp2p/go-reuseport-transport/issues/33")
	}
	if globalV4 == nil {
		t.Skip("no globalV4 addresses configured")
		return
	}

	var trA Transport
	var trB Transport
	listenerA, err := trA.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	listenerB2, err := trB.Listen(globalV4)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	// Check which port we're using
	port := dialOne(t, &trB, listenerA)

	// Check consistency
	for i := 0; i < 10; i++ {
		dialOne(t, &trB, listenerA, port)
	}
}
<|endoftext|>