<commit_before>package sqltest

import (
	"database/sql"
	"fmt"
	"math/rand"
	"regexp"
	"strconv"
	"sync"
	"testing"
	"time"
)

type Dialect int

const (
	SQLITE Dialect = iota
	POSTGRESQL
	MYSQL
)

type DB struct {
	*testing.T
	*sql.DB
	dialect Dialect
	once    sync.Once
}

var db *DB

// the following tables will be created and dropped during the test
var testTables = []string{"foo", "bar", "t", "bench"}

var tests = []testing.InternalTest{
	{"TestBlobs", TestBlobs},
	{"TestManyQueryRow", TestManyQueryRow},
	{"TestTxQuery", TestTxQuery},
	{"TestPreparedStmt", TestPreparedStmt},
}

var benchmarks = []testing.InternalBenchmark{
	{"BenchmarkExec", BenchmarkExec},
	{"BenchmarkQuery", BenchmarkQuery},
	{"BenchmarkParams", BenchmarkParams},
	{"BenchmarkStmt", BenchmarkStmt},
	{"BenchmarkRows", BenchmarkRows},
	{"BenchmarkStmtRows", BenchmarkStmtRows},
}

// RunTests runs the SQL test suite
func RunTests(t *testing.T, d *sql.DB, dialect Dialect) {
	db = &DB{t, d, dialect, sync.Once{}}
	testing.RunTests(func(string, string) (bool, error) { return true, nil }, tests)

	if !testing.Short() {
		for _, b := range benchmarks {
			fmt.Printf("%-20s", b.Name)
			r := testing.Benchmark(b.F)
			fmt.Printf("%10d %10.0f req/s\n", r.N, float64(r.N)/r.T.Seconds())
		}
	}
	db.tearDown()
}

func (db *DB) mustExec(sql string, args ...interface{}) sql.Result {
	res, err := db.Exec(sql, args...)
	if err != nil {
		db.Fatalf("Error running %q: %v", sql, err)
	}
	return res
}

func (db *DB) tearDown() {
	for _, tbl := range testTables {
		switch db.dialect {
		case SQLITE:
			db.mustExec("drop table " + tbl + "if exists")
		case MYSQL, POSTGRESQL:
			db.mustExec("drop table if exists " + tbl)
		default:
			db.Fatal("unkown dialect")
		}
	}
}

// q repaces ? parameters if needed
func (db *DB) q(sql string) string {
	switch db.dialect {
	case POSTGRESQL: // repace with $1, $2, ..
		qrx := regexp.MustCompile(`\?`)
		n := 0
		return qrx.ReplaceAllStringFunc(sql, func(string) string {
			n++
			return "$" + strconv.Itoa(n)
		})
	}
	return sql
}

func (db *DB) blobType(size int) string {
	switch db.dialect {
	case SQLITE:
		return fmt.Sprintf("blob[%d]", size)
	case POSTGRESQL:
		return "bytea"
	case MYSQL:
		return fmt.Sprintf("VARBINARY(%d)", size)
	}
	panic("unkown dialect")
}

func (db *DB) serialPK() string {
	switch db.dialect {
	case SQLITE:
		return "integer primary key autoincrement"
	case POSTGRESQL:
		return "serial primary key"
	case MYSQL:
		return "integer primary key auto_increment"
	}
	panic("unkown dialect")
}

func (db *DB) now() string {
	switch db.dialect {
	case SQLITE:
		return "datetime('now')"
	case POSTGRESQL:
		return "now()"
	case MYSQL:
		return "now()"
	}
	panic("unkown dialect")
}

func makeBench() {
	if _, err := db.Exec("create table bench (n varchar(32), i integer, d double, s varchar(32), t datetime)"); err != nil {
		panic(err)
	}
	st, err := db.Prepare("insert into bench values (?, ?, ?, ?, ?)")
	if err != nil {
		panic(err)
	}
	defer st.Close()
	for i := 0; i < 100; i++ {
		if _, err = st.Exec(nil, i, float64(i), fmt.Sprintf("%d", i), time.Now()); err != nil {
			panic(err)
		}
	}
}

func TestResult(t *testing.T) {
	db.tearDown()
	db.mustExec("create temporary table test (id " + db.serialPK() + ", name varchar(10))")

	for i := 1; i < 3; i++ {
		r := db.mustExec(db.q("insert into test (name) values (?)"), fmt.Sprintf("row %d", i))
		n, err := r.RowsAffected()
		if err != nil {
			t.Fatal(err)
		}
		if n != 1 {
			t.Errorf("got %v, want %v", n, 1)
		}
		n, err = r.LastInsertId()
		if err != nil {
			t.Fatal(err)
		}
		if n != int64(i) {
			t.Errorf("got %v, want %v", n, i)
		}
	}
	if _, err := db.Exec("error!"); err == nil {
		t.Fatalf("expected error")
	}
}

func TestBlobs(t *testing.T) {
	db.tearDown()
	var blob = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
	db.mustExec("create table foo (id integer primary key, bar " + db.blobType(16) + ")")
	db.mustExec(db.q("insert into foo (id, bar) values(?,?)"), 0, blob)

	want := fmt.Sprintf("%x", blob)

	b := make([]byte, 16)
	err := db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&b)
	got := fmt.Sprintf("%x", b)
	if err != nil {
		t.Errorf("[]byte scan: %v", err)
	} else if got != want {
		t.Errorf("for []byte, got %q; want %q", got, want)
	}

	err = db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&got)
	want = string(blob)
	if err != nil {
		t.Errorf("string scan: %v", err)
	} else if got != want {
		t.Errorf("for string, got %q; want %q", got, want)
	}
}

func TestManyQueryRow(t *testing.T) {
	if testing.Short() {
		t.Log("skipping in short mode")
		return
	}
	db.tearDown()
	db.mustExec("create table foo (id integer primary key, name varchar(50))")
	db.mustExec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob")
	var name string
	for i := 0; i < 10000; i++ {
		err := db.QueryRow(db.q("select name from foo where id = ?"), 1).Scan(&name)
		if err != nil || name != "bob" {
			t.Fatalf("on query %d: err=%v, name=%q", i, err, name)
		}
	}
}

func TestTxQuery(t *testing.T) {
	db.tearDown()
	tx, err := db.Begin()
	if err != nil {
		t.Fatal(err)
	}
	defer tx.Rollback()

	_, err = tx.Exec("create table foo (id integer primary key, name varchar(50))")
	if err != nil {
		t.Fatal(err)
	}

	_, err = tx.Exec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob")
	if err != nil {
		t.Fatal(err)
	}

	r, err := tx.Query(db.q("select name from foo where id = ?"), 1)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Close()

	if !r.Next() {
		if r.Err() != nil {
			t.Fatal(err)
		}
		t.Fatal("expected one rows")
	}

	var name string
	err = r.Scan(&name)
	if err != nil {
		t.Fatal(err)
	}
}

func TestPreparedStmt(t *testing.T) {
	db.tearDown()
	db.mustExec("CREATE TABLE t (count INT)")
	sel, err := db.Prepare("SELECT count FROM t ORDER BY count DESC")
	if err != nil {
		t.Fatalf("prepare 1: %v", err)
	}
	ins, err := db.Prepare(db.q("INSERT INTO t (count) VALUES (?)"))
	if err != nil {
		t.Fatalf("prepare 2: %v", err)
	}

	for n := 1; n <= 3; n++ {
		if _, err := ins.Exec(n); err != nil {
			t.Fatalf("insert(%d) = %v", n, err)
		}
	}

	const nRuns = 10
	ch := make(chan bool)
	for i := 0; i < nRuns; i++ {
		go func() {
			defer func() {
				ch <- true
			}()
			for j := 0; j < 10; j++ {
				count := 0
				if err := sel.QueryRow().Scan(&count); err != nil && err != sql.ErrNoRows {
					t.Errorf("Query: %v", err)
					return
				}
				if _, err := ins.Exec(rand.Intn(100)); err != nil {
					t.Errorf("Insert: %v", err)
					return
				}
			}
		}()
	}
	for i := 0; i < nRuns; i++ {
		<-ch
	}
}

// Benchmarks need to use panic() since b.Error errors are lost when
// running via testing.Benchmark() I would like to run these via go
// test -bench but calling Benchmark() from a benchmark test
// currently hangs go.

func BenchmarkExec(b *testing.B) {
	for i := 0; i < b.N; i++ {
		if _, err := db.Exec("select 1"); err != nil {
			panic(err)
		}
	}
}

func BenchmarkQuery(b *testing.B) {
	for i := 0; i < b.N; i++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		if err := db.QueryRow("select null, 1, 1.1, 'foo', " + db.now()).Scan(&n, &i, &f, &s, &t); err != nil {
			panic(err)
		}
	}
}

func BenchmarkParams(b *testing.B) {
	for i := 0; i < b.N; i++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		if err := db.QueryRow("select ?, ?, ?, ?, ?", nil, 1, 1.1, "foo", time.Now()).Scan(&n, &i, &f, &s, &t); err != nil {
			panic(err)
		}
	}
}

func BenchmarkStmt(b *testing.B) {
	st, err := db.Prepare("select ?, ?, ?, ?, ?")
	if err != nil {
		panic(err)
	}
	defer st.Close()

	for n := 0; n < b.N; n++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		if err := st.QueryRow(nil, 1, 1.1, "foo", time.Now()).Scan(&n, &i, &f, &s, &t); err != nil {
			panic(err)
		}
	}
}

func BenchmarkRows(b *testing.B) {
	db.once.Do(makeBench)

	for n := 0; n < b.N; n++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		r, err := db.Query("select * from bench")
		if err != nil {
			panic(err)
		}
		for r.Next() {
			if err = r.Scan(&n, &i, &f, &s, &t); err != nil {
				panic(err)
			}
		}
		if err = r.Err(); err != nil {
			panic(err)
		}
	}
}

func BenchmarkStmtRows(b *testing.B) {
	db.once.Do(makeBench)

	st, err := db.Prepare("select * from bench")
	if err != nil {
		panic(err)
	}
	defer st.Close()

	for n := 0; n < b.N; n++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		r, err := st.Query()
		if err != nil {
			panic(err)
		}
		for r.Next() {
			if err = r.Scan(&n, &i, &f, &s, &t); err != nil {
				panic(err)
			}
		}
		if err = r.Err(); err != nil {
			panic(err)
		}
	}
}
<commit_msg>spelling<commit_after>package sqltest

import (
	"database/sql"
	"fmt"
	"math/rand"
	"regexp"
	"strconv"
	"sync"
	"testing"
	"time"
)

type Dialect int

const (
	SQLITE Dialect = iota
	POSTGRESQL
	MYSQL
)

type DB struct {
	*testing.T
	*sql.DB
	dialect Dialect
	once    sync.Once
}

var db *DB

// the following tables will be created and dropped during the test
var testTables = []string{"foo", "bar", "t", "bench"}

var tests = []testing.InternalTest{
	{"TestBlobs", TestBlobs},
	{"TestManyQueryRow", TestManyQueryRow},
	{"TestTxQuery", TestTxQuery},
	{"TestPreparedStmt", TestPreparedStmt},
}

var benchmarks = []testing.InternalBenchmark{
	{"BenchmarkExec", BenchmarkExec},
	{"BenchmarkQuery", BenchmarkQuery},
	{"BenchmarkParams", BenchmarkParams},
	{"BenchmarkStmt", BenchmarkStmt},
	{"BenchmarkRows", BenchmarkRows},
	{"BenchmarkStmtRows", BenchmarkStmtRows},
}

// RunTests runs the SQL test suite
func RunTests(t *testing.T, d *sql.DB, dialect Dialect) {
	db = &DB{t, d, dialect, sync.Once{}}
	testing.RunTests(func(string, string) (bool, error) { return true, nil }, tests)

	if !testing.Short() {
		for _, b := range benchmarks {
			fmt.Printf("%-20s", b.Name)
			r := testing.Benchmark(b.F)
			fmt.Printf("%10d %10.0f req/s\n", r.N, float64(r.N)/r.T.Seconds())
		}
	}
	db.tearDown()
}

func (db *DB) mustExec(sql string, args ...interface{}) sql.Result {
	res, err := db.Exec(sql, args...)
	if err != nil {
		db.Fatalf("Error running %q: %v", sql, err)
	}
	return res
}

func (db *DB) tearDown() {
	for _, tbl := range testTables {
		switch db.dialect {
		case SQLITE:
			db.mustExec("drop table " + tbl + "if exists")
		case MYSQL, POSTGRESQL:
			db.mustExec("drop table if exists " + tbl)
		default:
			db.Fatal("unkown dialect")
		}
	}
}

// q replaces ? parameters if needed
func (db *DB) q(sql string) string {
	switch db.dialect {
	case POSTGRESQL: // repace with $1, $2, ..
		qrx := regexp.MustCompile(`\?`)
		n := 0
		return qrx.ReplaceAllStringFunc(sql, func(string) string {
			n++
			return "$" + strconv.Itoa(n)
		})
	}
	return sql
}

func (db *DB) blobType(size int) string {
	switch db.dialect {
	case SQLITE:
		return fmt.Sprintf("blob[%d]", size)
	case POSTGRESQL:
		return "bytea"
	case MYSQL:
		return fmt.Sprintf("VARBINARY(%d)", size)
	}
	panic("unkown dialect")
}

func (db *DB) serialPK() string {
	switch db.dialect {
	case SQLITE:
		return "integer primary key autoincrement"
	case POSTGRESQL:
		return "serial primary key"
	case MYSQL:
		return "integer primary key auto_increment"
	}
	panic("unkown dialect")
}

func (db *DB) now() string {
	switch db.dialect {
	case SQLITE:
		return "datetime('now')"
	case POSTGRESQL:
		return "now()"
	case MYSQL:
		return "now()"
	}
	panic("unkown dialect")
}

func makeBench() {
	if _, err := db.Exec("create table bench (n varchar(32), i integer, d double, s varchar(32), t datetime)"); err != nil {
		panic(err)
	}
	st, err := db.Prepare("insert into bench values (?, ?, ?, ?, ?)")
	if err != nil {
		panic(err)
	}
	defer st.Close()
	for i := 0; i < 100; i++ {
		if _, err = st.Exec(nil, i, float64(i), fmt.Sprintf("%d", i), time.Now()); err != nil {
			panic(err)
		}
	}
}

func TestResult(t *testing.T) {
	db.tearDown()
	db.mustExec("create temporary table test (id " + db.serialPK() + ", name varchar(10))")

	for i := 1; i < 3; i++ {
		r := db.mustExec(db.q("insert into test (name) values (?)"), fmt.Sprintf("row %d", i))
		n, err := r.RowsAffected()
		if err != nil {
			t.Fatal(err)
		}
		if n != 1 {
			t.Errorf("got %v, want %v", n, 1)
		}
		n, err = r.LastInsertId()
		if err != nil {
			t.Fatal(err)
		}
		if n != int64(i) {
			t.Errorf("got %v, want %v", n, i)
		}
	}
	if _, err := db.Exec("error!"); err == nil {
		t.Fatalf("expected error")
	}
}

func TestBlobs(t *testing.T) {
	db.tearDown()
	var blob = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
	db.mustExec("create table foo (id integer primary key, bar " + db.blobType(16) + ")")
	db.mustExec(db.q("insert into foo (id, bar) values(?,?)"), 0, blob)

	want := fmt.Sprintf("%x", blob)

	b := make([]byte, 16)
	err := db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&b)
	got := fmt.Sprintf("%x", b)
	if err != nil {
		t.Errorf("[]byte scan: %v", err)
	} else if got != want {
		t.Errorf("for []byte, got %q; want %q", got, want)
	}

	err = db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&got)
	want = string(blob)
	if err != nil {
		t.Errorf("string scan: %v", err)
	} else if got != want {
		t.Errorf("for string, got %q; want %q", got, want)
	}
}

func TestManyQueryRow(t *testing.T) {
	if testing.Short() {
		t.Log("skipping in short mode")
		return
	}
	db.tearDown()
	db.mustExec("create table foo (id integer primary key, name varchar(50))")
	db.mustExec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob")
	var name string
	for i := 0; i < 10000; i++ {
		err := db.QueryRow(db.q("select name from foo where id = ?"), 1).Scan(&name)
		if err != nil || name != "bob" {
			t.Fatalf("on query %d: err=%v, name=%q", i, err, name)
		}
	}
}

func TestTxQuery(t *testing.T) {
	db.tearDown()
	tx, err := db.Begin()
	if err != nil {
		t.Fatal(err)
	}
	defer tx.Rollback()

	_, err = tx.Exec("create table foo (id integer primary key, name varchar(50))")
	if err != nil {
		t.Fatal(err)
	}

	_, err = tx.Exec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob")
	if err != nil {
		t.Fatal(err)
	}

	r, err := tx.Query(db.q("select name from foo where id = ?"), 1)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Close()

	if !r.Next() {
		if r.Err() != nil {
			t.Fatal(err)
		}
		t.Fatal("expected one rows")
	}

	var name string
	err = r.Scan(&name)
	if err != nil {
		t.Fatal(err)
	}
}

func TestPreparedStmt(t *testing.T) {
	db.tearDown()
	db.mustExec("CREATE TABLE t (count INT)")
	sel, err := db.Prepare("SELECT count FROM t ORDER BY count DESC")
	if err != nil {
		t.Fatalf("prepare 1: %v", err)
	}
	ins, err := db.Prepare(db.q("INSERT INTO t (count) VALUES (?)"))
	if err != nil {
		t.Fatalf("prepare 2: %v", err)
	}

	for n := 1; n <= 3; n++ {
		if _, err := ins.Exec(n); err != nil {
			t.Fatalf("insert(%d) = %v", n, err)
		}
	}

	const nRuns = 10
	ch := make(chan bool)
	for i := 0; i < nRuns; i++ {
		go func() {
			defer func() {
				ch <- true
			}()
			for j := 0; j < 10; j++ {
				count := 0
				if err := sel.QueryRow().Scan(&count); err != nil && err != sql.ErrNoRows {
					t.Errorf("Query: %v", err)
					return
				}
				if _, err := ins.Exec(rand.Intn(100)); err != nil {
					t.Errorf("Insert: %v", err)
					return
				}
			}
		}()
	}
	for i := 0; i < nRuns; i++ {
		<-ch
	}
}

// Benchmarks need to use panic() since b.Error errors are lost when
// running via testing.Benchmark() I would like to run these via go
// test -bench but calling Benchmark() from a benchmark test
// currently hangs go.

func BenchmarkExec(b *testing.B) {
	for i := 0; i < b.N; i++ {
		if _, err := db.Exec("select 1"); err != nil {
			panic(err)
		}
	}
}

func BenchmarkQuery(b *testing.B) {
	for i := 0; i < b.N; i++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		if err := db.QueryRow("select null, 1, 1.1, 'foo', " + db.now()).Scan(&n, &i, &f, &s, &t); err != nil {
			panic(err)
		}
	}
}

func BenchmarkParams(b *testing.B) {
	for i := 0; i < b.N; i++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		if err := db.QueryRow("select ?, ?, ?, ?, ?", nil, 1, 1.1, "foo", time.Now()).Scan(&n, &i, &f, &s, &t); err != nil {
			panic(err)
		}
	}
}

func BenchmarkStmt(b *testing.B) {
	st, err := db.Prepare("select ?, ?, ?, ?, ?")
	if err != nil {
		panic(err)
	}
	defer st.Close()

	for n := 0; n < b.N; n++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		if err := st.QueryRow(nil, 1, 1.1, "foo", time.Now()).Scan(&n, &i, &f, &s, &t); err != nil {
			panic(err)
		}
	}
}

func BenchmarkRows(b *testing.B) {
	db.once.Do(makeBench)

	for n := 0; n < b.N; n++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		r, err := db.Query("select * from bench")
		if err != nil {
			panic(err)
		}
		for r.Next() {
			if err = r.Scan(&n, &i, &f, &s, &t); err != nil {
				panic(err)
			}
		}
		if err = r.Err(); err != nil {
			panic(err)
		}
	}
}

func BenchmarkStmtRows(b *testing.B) {
	db.once.Do(makeBench)

	st, err := db.Prepare("select * from bench")
	if err != nil {
		panic(err)
	}
	defer st.Close()

	for n := 0; n < b.N; n++ {
		var n sql.NullString
		var i int
		var f float64
		var s string
		var t time.Time
		r, err := st.Query()
		if err != nil {
			panic(err)
		}
		for r.Next() {
			if err = r.Scan(&n, &i, &f, &s, &t); err != nil {
				panic(err)
			}
		}
		if err = r.Err(); err != nil {
			panic(err)
		}
	}
}
<|endoftext|>


<commit_before>package sorter_test

import (
	"math/rand"
	"testing"
	"sort"

	"github.com/caelifer/sorter"
)

type V struct {
	Name   string
	Number int
}

type rule struct {
	Name string
	Rule sorter.Rule
}

var tests = []struct {
	rule rule
	want []V
}{
	{
		rule: rule{
			Name: "Number",
			Rule: func(i1, i2 interface{}) bool {
				return i1.(V).Number < i2.(V).Number
			},
		},
		want: []V{{Name: "BBB", Number: 10}, {Name: "AAA", Number: 10}, {Name: "CCC", Number: 16}, {Name: "AAA", Number: 20}, {Name: "DDD", Number: 20}},
	},
	{
		rule: rule{
			Name: "Name",
			Rule: func(i1, i2 interface{}) bool {
				return i1.(V).Name < i2.(V).Name
			},
		},
		want: []V{{Name: "AAA", Number: 20}, {Name: "AAA", Number: 10}, {Name: "BBB", Number: 10}, {Name: "CCC", Number: 16}, {Name: "DDD", Number: 20}},
	},
	{
		rule: rule{
			Name: "NumberThenName",
			Rule: func(i1, i2 interface{}) bool {
				n1, n2 := i1.(V), i2.(V)

				switch {
				case n1.Number < n2.Number:
					return true
				case n2.Number < n1.Number:
					return false
				default:
					return n1.Name < n2.Name
				}
			},
		},
		want: []V{{Name: "AAA", Number: 10}, {Name: "BBB", Number: 10}, {Name: "CCC", Number: 16}, {Name: "AAA", Number: 20}, {Name: "DDD", Number: 20}},
	},
}

var values = []V{{"BBB", 10}, {"AAA", 20}, {"CCC", 16}, {"DDD", 20}, {"AAA", 10}}

func TestSorter(t *testing.T) {
	for _, tst := range tests {

		// Clone and convert our values to []interface{}
		clone := cloneSlice(values)
		want := cloneSlice(tst.want)

		// Test our sorter interface
		sorter.Sort(clone).By(tst.rule.Rule)

		if !equal(clone, want) {
			t.Errorf("[FAILED] testing rule %s\n\tgot:    %+v\n\twanted: %+v", tst.rule.Name, clone, want)
		}
	}
}

func equal(v1, v2 []interface{}) bool {
	if len(v1) != len(v2) {
		return false
	}

	for i := 0; i < len(v1); i++ {
		i1, ok1 := v1[i].(V)
		i2, ok2 := v2[i].(V)

		if !(ok1 && ok2) || i1 != i2 {
			return false
		}
	}

	return true
}

func cloneSlice(old []V) []interface{} {
	res := make([]interface{}, len(old))
	for i, v := range old {
		res[i] = v
	}
	return res
}

var testInts []int

func init() {
	rand.Seed(0) // deterministic rand generator
	testInts = genRandInts()
}

func genRandInts() []int {
	const size = 1000
	ints := make([]int, size)

	for i := 0; i < size; i++ {
		ints[i] = rand.Intn(size)
	}

	return ints
}

func cloneInts(old []int) []int {
	res := make([]int, len(old))
	for i, v := range old {
		res[i] = v
	}
	return res
}

func cloneGens(old []int) []interface{} {
	res := make([]interface{}, len(old))
	for i, v := range old {
		res[i] = v
	}
	return res
}

func BenchmarkStdSort(b *testing.B) {
	is := cloneInts(testInts)
	for i := 0; i < b.N; i++ {
		sort.Ints(is)
	}
}

func BenchmarkSorter_(b *testing.B) {
	is := cloneGens(testInts)
	for i := 0; i < b.N; i++ {
		sorter.Sort(is).By(func(i1, i2 interface{}) bool {
			return i1.(int) < i2.(int)
		})
	}
}
<commit_msg>Renamed bench function and modified rules and constants<commit_after>package sorter_test

import (
	"math/rand"
	"sort"
	"testing"

	"github.com/caelifer/sorter"
)

type V struct {
	Name   string
	Number int
}

type rule struct {
	Name string
	Rule sorter.Rule
}

var tests = []struct {
	rule rule
	want []V
}{
	{
		rule: rule{
			Name: "Number",
			Rule: func(i1, i2 interface{}) bool {
				return i1.(V).Number < i2.(V).Number
			},
		},
		want: []V{{Name: "BBB", Number: 10}, {Name: "AAA", Number: 10}, {Name: "CCC", Number: 16}, {Name: "AAA", Number: 20}, {Name: "DDD", Number: 20}},
	},
	{
		rule: rule{
			Name: "Name",
			Rule: func(i1, i2 interface{}) bool {
				return i1.(V).Name < i2.(V).Name
			},
		},
		want: []V{{Name: "AAA", Number: 20}, {Name: "AAA", Number: 10}, {Name: "BBB", Number: 10}, {Name: "CCC", Number: 16}, {Name: "DDD", Number: 20}},
	},
	{
		rule: rule{
			Name: "NumberThenName",
			Rule: func(i1, i2 interface{}) bool {
				n1, n2 := i1.(V), i2.(V)

				switch {
				case n1.Number < n2.Number:
					return true
				case n2.Number < n1.Number:
					return false
				default:
					return n1.Name <= n2.Name
				}
			},
		},
		want: []V{{Name: "AAA", Number: 10}, {Name: "BBB", Number: 10}, {Name: "CCC", Number: 16}, {Name: "AAA", Number: 20}, {Name: "DDD", Number: 20}},
	},
}

var values = []V{{"BBB", 10}, {"AAA", 20}, {"CCC", 16}, {"DDD", 20}, {"AAA", 10}}

func TestSorter(t *testing.T) {
	for _, tst := range tests {

		// Clone and convert our values to []interface{}
		clone := cloneSlice(values)
		want := cloneSlice(tst.want)

		// Test our sorter interface
		sorter.Sort(clone).By(tst.rule.Rule)

		if !equal(clone, want) {
			t.Errorf("[FAILED] testing rule %s\n\tgot:    %+v\n\twanted: %+v", tst.rule.Name, clone, want)
		}
	}
}

func equal(v1, v2 []interface{}) bool {
	if len(v1) != len(v2) {
		return false
	}

	for i := 0; i < len(v1); i++ {
		i1, ok1 := v1[i].(V)
		i2, ok2 := v2[i].(V)

		if !(ok1 && ok2) || i1 != i2 {
			return false
		}
	}

	return true
}

func cloneSlice(old []V) []interface{} {
	res := make([]interface{}, len(old))
	for i, v := range old {
		res[i] = v
	}
	return res
}

var testInts []int

func init() {
	rand.Seed(0) // deterministic rand generator
	testInts = genRandInts()
}

func genRandInts() []int {
	const size = 1024
	ints := make([]int, size)

	for i := 0; i < size; i++ {
		ints[i] = rand.Intn(size)
	}

	return ints
}

func cloneInts(old []int) []int {
	res := make([]int, len(old))
	for i, v := range old {
		res[i] = v
	}
	return res
}

func cloneGens(old []int) []interface{} {
	res := make([]interface{}, len(old))
	for i, v := range old {
		res[i] = v
	}
	return res
}

func BenchmarkStdSort(b *testing.B) {
	is := cloneInts(testInts)
	for i := 0; i < b.N; i++ {
		sort.Ints(is)
	}
}

func BenchmarkGenSort(b *testing.B) {
	is := cloneGens(testInts)
	for i := 0; i < b.N; i++ {
		sorter.Sort(is).By(func(i1, i2 interface{}) bool {
			return i1.(int) < i2.(int)
		})
	}
}
<|endoftext|>


<commit_before>// The package nsqAdapter provides a thin wrapper around bitly's nsq-services to
// simplify usage of nsq's asynchronous message queues for midsized projects.
//
// Basically, you define a new nsq-adapter struct and call the following methods:
// - Subscribe() to subscribe to a specific topic and handle incoming messages
// - Publish() to send a message to a specific topic
// - Request() to send a request to a specific topic and wait for a response
// - RespondTo() to send a response message to a request
//
// Please take a look at the example and the samples in the testing directory for
// an idea about the usage of the package.
package nsqAdapter

import (
	"encoding/json"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/bitly/go-nsq"
)

// NewNsqAdapter will create a new nsq-adapter using the given address to connect
// to a nsqlookupd-service and use the default configuration for connections
func New(serviceName string, nsqlookupHttpAddress string) *NsqAdapter {
	return NewWithCustomConfig(serviceName, nsqlookupHttpAddress, nsq.NewConfig())
}

// NewWithCustomConfig will create a new nsq-adapter with a custom nsq-configuration
func NewWithCustomConfig(serviceName string, nsqlookupHttpAddress string, config *nsq.Config) *NsqAdapter {

	// initialize a new adapter
	queue := NsqAdapter{
		Name:             serviceName,
		nsqlookupAddress: nsqlookupHttpAddress,
		consumers:        make(map[string]*nsq.Consumer),
		requests:         make(map[string]chan Message),
		config:           config,
	}
	return &queue
}

// New Message will create a new message struct to send to nsq
func (queue *NsqAdapter) NewMessage(topic string, messageType string, payload interface{}) *Message {

	// create a new Message
	message := Message{}

	// set a unique id for our message
	message.Id = uuid.NewUUID().String()

	// set the originating service
	message.From = queue.Name

	// set the message to send the data to
	message.To = topic

	// define the time until we need the response
	message.StartTime = time.Now().String()

	// set the payload
	message.Payload, _ = json.Marshal(payload)

	// set the type
	message.MessageType = messageType

	return &message
}
<commit_msg>add a handle method<commit_after>// The package nsqAdapter provides a thin wrapper around bitly's nsq-services to
// simplify usage of nsq's asynchronous message queues for midsized projects.
//
// Basically, you define a new nsq-adapter struct and call the following methods:
// - Subscribe() to subscribe to a specific topic and handle incoming messages
// - Publish() to send a message to a specific topic
// - Request() to send a request to a specific topic and wait for a response
// - RespondTo() to send a response message to a request
//
// Please take a look at the example and the samples in the testing directory for
// an idea about the usage of the package.
package nsqAdapter

import (
	"encoding/json"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/bitly/go-nsq"
)

// NewNsqAdapter will create a new nsq-adapter using the given address to connect
// to a nsqlookupd-service and use the default configuration for connections
func New(serviceName string, nsqlookupHttpAddress string) *NsqAdapter {
	return NewWithCustomConfig(serviceName, nsqlookupHttpAddress, nsq.NewConfig())
}

// NewWithCustomConfig will create a new nsq-adapter with a custom nsq-configuration
func NewWithCustomConfig(serviceName string, nsqlookupHttpAddress string, config *nsq.Config) *NsqAdapter {

	// initialize a new adapter
	queue := NsqAdapter{
		Name:             serviceName,
		nsqlookupAddress: nsqlookupHttpAddress,
		consumers:        make(map[string]*nsq.Consumer),
		requests:         make(map[string]chan Message),
		config:           config,
	}
	return &queue
}

// New Message will create a new message struct to send to nsq
func (queue *NsqAdapter) NewMessage(topic string, messageType string, payload interface{}) *Message {

	// create a new Message
	message := Message{}

	// set a unique id for our message
	message.Id = uuid.NewUUID().String()

	// set the originating service
	message.From = queue.Name

	// set the message to send the data to
	message.To = topic

	// define the time until we need the response
	message.StartTime = time.Now().String()

	// set the payload
	message.Payload, _ = json.Marshal(payload)

	// set the type
	message.MessageType = messageType

	return &message
}

// Handle will start handling all incoming messages with the given function in a separate go routine
func (queue *NsqAdapter) Handle(topic string, channel string, handleFunction func(message Message)) {

	// create a channel that will receive message from
	// a topic we would like to subscribe to
	messageChan := make(chan Message)

	// subscribe to all messages posted to the fetch process
	queue.Subscribe(topic, channel, messageChan)

	// handle all incoming requests for fetching data
	for {
		// wait for incoming messages
		message := <-messageChan

		// start processing our data
		go handleFunction(message)
	}

}
<|endoftext|>


<commit_before>package cache

import (
	"strconv"
	"time"
)

//Collection cache Collection
//Collection is flushable sub cache create from other cacheable.
type Collection struct {
	//Cache raw cache
	Cache Cacheable
	//Prefix cache key prefix
	Prefix string
	// default ttl
	TTL time.Duration
}

//CollectionTTLMultiple default collection ttl multiple
var CollectionTTLMultiple = 10

//NewCollection create new cache collection with given cache,prefix and ttl.
//Return collection created.
func NewCollection(cache Cacheable, prefix string, TTL time.Duration) *Collection {
	return &Collection{
		Cache:  cache,
		Prefix: prefix,
		TTL:    TTL,
	}
}

//GetCacheKey return raw cache key by given key.
//Return key and any error if raised.
func (c *Collection) GetCacheKey(key string) (string, error) {
	var ts string
	var data int64
	err := c.Cache.Get(c.Prefix, &ts)
	if err == ErrNotFound {
		data = time.Now().UnixNano()
		ts = strconv.FormatInt(data, 10)
		err = nil
		ttl := c.TTL
		if !c.persist() {
			ttl = ttl * time.Duration(CollectionTTLMultiple)
		}
		err2 := c.Cache.Set(c.Prefix, ts, ttl)
		if err2 == ErrNotCacheable {
			err2 = nil
		}
		if err2 != nil {
			return "", err2
		}
	}
	if err != nil {
		return "", err
	}
	return c.Prefix + KeyPrefix + ts + KeyPrefix + key, nil
}
func (c *Collection) persist() bool {
	return c.TTL < 0 || (c.TTL == 0 && c.Cache.DefualtTTL() < 0)
}

//Set Set data model to cache by given key.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) Set(key string, v interface{}, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Set(k, v, TTL)
}

//Update Update data model to cache by given key only if the cache exist.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) Update(key string, v interface{}, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Update(k, v, TTL)
}

//Get Get data model from cache by given key.
//Parameter v should be pointer to empty data model which data filled in.
//Return any error raised.
func (c *Collection) Get(key string, v interface{}) error {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Get(k, v)
}

//SetBytesValue Set bytes data to cache by given key.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) SetBytesValue(key string, bytes []byte, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.SetBytesValue(k, bytes, TTL)

}

//GetBytesValue Get bytes data from cache by given key.
//Return data bytes and any error raised.
func (c *Collection) GetBytesValue(key string) ([]byte, error) {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return nil, err
	}
	return c.Cache.GetBytesValue(k)
}

//UpdateBytesValue Update bytes data to cache by given key only if the cache exist.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) UpdateBytesValue(key string, bytes []byte, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.UpdateBytesValue(k, bytes, TTL)
}

//MGetBytesValue get multiple bytes data from cache by given keys.
//Return data bytes map and any error if raised.
func (c *Collection) MGetBytesValue(keys ...string) (map[string][]byte, error) {
	prefix, err := c.GetCacheKey("")
	var result map[string][]byte
	var prefixedKeys = make([]string, len(keys))
	for k := range keys {
		prefixedKeys[k] = prefix + keys[k]
	}
	data, err := c.Cache.MGetBytesValue(prefixedKeys...)
	if err != nil {
		return result, err
	}
	result = make(map[string][]byte, len(data))
	for k := range data {
		result[k[len(prefix):]] = data[k]
	}
	return result, nil

}

//MSetBytesValue set multiple bytes data to cache with given key-value map.
//Return  any error if raised.
func (c *Collection) MSetBytesValue(data map[string][]byte, ttl time.Duration) error {
	prefix, err := c.GetCacheKey("")
	if err != nil {
		return err
	}
	var prefixed = make(map[string][]byte, len(data))
	for k := range data {
		prefixed[prefix+k] = data[k]
	}
	return c.Cache.MSetBytesValue(prefixed, ttl)
}

//Del Delete data in cache by given name.
//Return any error raised.
func (c *Collection) Del(key string) error {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Del(k)
}

//IncrCounter Increase int val in cache by given key.Count cache and data cache are in two independent namespace.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return int data value and any error raised.
func (c *Collection) IncrCounter(key string, increment int64, TTL time.Duration) (int64, error) {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return 0, ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return 0, err
	}
	return c.Cache.IncrCounter(k, increment, TTL)

}

//SetCounter Set int val in cache by given key.Count cache and data cache are in two independent namespace.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) SetCounter(key string, v int64, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.SetCounter(k, v, TTL)

}

//DelCounter Delete int val in cache by given name.Count cache and data cache are in two independent namespace.
//Return any error raised.
func (c *Collection) DelCounter(key string) error {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.DelCounter(k)
}

//GetCounter Get int val from cache by given key.Count cache and data cache are in two independent namespace.
//Return int data value and any error raised.
func (c *Collection) GetCounter(key string) (int64, error) {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return 0, err
	}
	return c.Cache.GetCounter(k)

}

//Load Get data model from cache by given key.If data not found,call loader to get current data value and save to cache.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) Load(key string, v interface{}, TTL time.Duration, loader Loader) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return loadFromCache(c, k, v, TTL, loader)
}

//Flush Delete all data in cache.
func (c *Collection) Flush() error {
	return c.Cache.Del(c.Prefix)
}

//DefualtTTL return cache default ttl
func (c *Collection) DefualtTTL() time.Duration {
	return c.Cache.DefualtTTL()
}

//Expire set cache value expire duration by given key and ttl
func (c *Collection) Expire(key string, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Expire(k, TTL)
}

//ExpireCounter set cache counter  expire duration by given key and ttl
func (c *Collection) ExpireCounter(key string, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.ExpireCounter(k, TTL)
}

//Locker create new locker with given key.
//return locker and if locker aleady locked
func (c *Collection) Locker(key string) (*Locker, bool) {
	return c.Cache.Locker(key)
}

//Marshal Marshal data model to  bytes.
//Return marshaled bytes and any error rasied.
func (c *Collection) Marshal(v interface{}) ([]byte, error) {
	return c.Cache.Marshal(v)
}

//Unmarshal Unmarshal bytes to data model.
//Parameter v should be pointer to empty data model which data filled in.
//Return any error raseid.
func (c *Collection) Unmarshal(bytes []byte, v interface{}) error {
	return c.Cache.Unmarshal(bytes, v)
}

//Collection get a cache colletion with given prefix
func (c *Collection) Collection(prefix string) *Collection {
	return NewCollection(c, prefix, c.TTL)
}

//Node get a cache node with given prefix
func (c *Collection) Node(prefix string) *Node {
	return NewNode(c, prefix)
}

//Field retuan a cache field with given field name
func (c *Collection) Field(fieldname string) *Field {
	return &Field{
		Cache:     c,
		FieldName: fieldname,
	}
}

//FinalKey get final key which passed to cache driver .
func (c *Collection) FinalKey(key string) (string, error) {
	return c.Cache.FinalKey(c.Prefix + KeyPrefix + key)
}
<commit_msg>update<commit_after>package cache

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"time"
)

//Collection cache Collection
//Collection is flushable sub cache create from other cacheable.
type Collection struct {
	//Cache raw cache
	Cache Cacheable
	//Prefix cache key prefix
	Prefix string
	// default ttl
	TTL time.Duration
}

//CollectionTTLMultiple default collection ttl multiple
var CollectionTTLMultiple = 10

//NewCollection create new cache collection with given cache,prefix and ttl.
//Return collection created.
func NewCollection(cache Cacheable, prefix string, TTL time.Duration) *Collection {
	return &Collection{
		Cache:  cache,
		Prefix: prefix,
		TTL:    TTL,
	}
}

//GetCacheKey return raw cache key by given key.
//Return key and any error if raised.
func (c *Collection) GetCacheKey(key string) (string, error) {
	var ts string
	var data int64
	err := c.Cache.Get(c.Prefix, &ts)
	if err == ErrNotFound {
		data = time.Now().UnixNano()
		buf := bytes.NewBuffer(nil)
		err = binary.Write(buf, binary.BigEndian, data)
		if err != nil {
			return "", err
		}
		ts = hex.EncodeToString(buf.Bytes())
		ttl := c.TTL
		if !c.persist() {
			ttl = ttl * time.Duration(CollectionTTLMultiple)
		}
		err2 := c.Cache.Set(c.Prefix, ts, ttl)
		if err2 == ErrNotCacheable {
			err2 = nil
		}
		if err2 != nil {
			return "", err2
		}
	}
	if err != nil {
		return "", err
	}
	return c.Prefix + KeyPrefix + ts + KeyPrefix + key, nil
}
func (c *Collection) persist() bool {
	return c.TTL < 0 || (c.TTL == 0 && c.Cache.DefualtTTL() < 0)
}

//Set Set data model to cache by given key.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) Set(key string, v interface{}, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Set(k, v, TTL)
}

//Update Update data model to cache by given key only if the cache exist.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) Update(key string, v interface{}, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Update(k, v, TTL)
}

//Get Get data model from cache by given key.
//Parameter v should be pointer to empty data model which data filled in.
//Return any error raised.
func (c *Collection) Get(key string, v interface{}) error {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Get(k, v)
}

//SetBytesValue Set bytes data to cache by given key.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) SetBytesValue(key string, bytes []byte, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.SetBytesValue(k, bytes, TTL)

}

//GetBytesValue Get bytes data from cache by given key.
//Return data bytes and any error raised.
func (c *Collection) GetBytesValue(key string) ([]byte, error) {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return nil, err
	}
	return c.Cache.GetBytesValue(k)
}

//UpdateBytesValue Update bytes data to cache by given key only if the cache exist.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) UpdateBytesValue(key string, bytes []byte, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.UpdateBytesValue(k, bytes, TTL)
}

//MGetBytesValue get multiple bytes data from cache by given keys.
//Return data bytes map and any error if raised.
func (c *Collection) MGetBytesValue(keys ...string) (map[string][]byte, error) {
	prefix, err := c.GetCacheKey("")
	var result map[string][]byte
	var prefixedKeys = make([]string, len(keys))
	for k := range keys {
		prefixedKeys[k] = prefix + keys[k]
	}
	data, err := c.Cache.MGetBytesValue(prefixedKeys...)
	if err != nil {
		return result, err
	}
	result = make(map[string][]byte, len(data))
	for k := range data {
		result[k[len(prefix):]] = data[k]
	}
	return result, nil

}

//MSetBytesValue set multiple bytes data to cache with given key-value map.
//Return  any error if raised.
func (c *Collection) MSetBytesValue(data map[string][]byte, ttl time.Duration) error {
	prefix, err := c.GetCacheKey("")
	if err != nil {
		return err
	}
	var prefixed = make(map[string][]byte, len(data))
	for k := range data {
		prefixed[prefix+k] = data[k]
	}
	return c.Cache.MSetBytesValue(prefixed, ttl)
}

//Del Delete data in cache by given name.
//Return any error raised.
func (c *Collection) Del(key string) error {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Del(k)
}

//IncrCounter Increase int val in cache by given key.Count cache and data cache are in two independent namespace.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return int data value and any error raised.
func (c *Collection) IncrCounter(key string, increment int64, TTL time.Duration) (int64, error) {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return 0, ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return 0, err
	}
	return c.Cache.IncrCounter(k, increment, TTL)

}

//SetCounter Set int val in cache by given key.Count cache and data cache are in two independent namespace.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) SetCounter(key string, v int64, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.SetCounter(k, v, TTL)

}

//DelCounter Delete int val in cache by given name.Count cache and data cache are in two independent namespace.
//Return any error raised.
func (c *Collection) DelCounter(key string) error {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.DelCounter(k)
}

//GetCounter Get int val from cache by given key.Count cache and data cache are in two independent namespace.
//Return int data value and any error raised.
func (c *Collection) GetCounter(key string) (int64, error) {
	k, err := c.GetCacheKey(key)
	if err != nil {
		return 0, err
	}
	return c.Cache.GetCounter(k)

}

//Load Get data model from cache by given key.If data not found,call loader to get current data value and save to cache.
//If ttl is DefualtTTL(0),use default ttl in config instead.
//Return any error raised.
func (c *Collection) Load(key string, v interface{}, TTL time.Duration, loader Loader) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return loadFromCache(c, k, v, TTL, loader)
}

//Flush Delete all data in cache.
func (c *Collection) Flush() error {
	return c.Cache.Del(c.Prefix)
}

//DefualtTTL return cache default ttl
func (c *Collection) DefualtTTL() time.Duration {
	return c.Cache.DefualtTTL()
}

//Expire set cache value expire duration by given key and ttl
func (c *Collection) Expire(key string, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.Expire(k, TTL)
}

//ExpireCounter set cache counter  expire duration by given key and ttl
func (c *Collection) ExpireCounter(key string, TTL time.Duration) error {
	if TTL < 0 || (TTL == 0 && c.Cache.DefualtTTL() < 0) {
		return ErrPermanentCacheNotSupport
	}
	k, err := c.GetCacheKey(key)
	if err != nil {
		return err
	}
	return c.Cache.ExpireCounter(k, TTL)
}

//Locker create new locker with given key.
//return locker and if locker aleady locked
func (c *Collection) Locker(key string) (*Locker, bool) {
	return c.Cache.Locker(key)
}

//Marshal Marshal data model to  bytes.
//Return marshaled bytes and any error rasied.
func (c *Collection) Marshal(v interface{}) ([]byte, error) {
	return c.Cache.Marshal(v)
}

//Unmarshal Unmarshal bytes to data model.
//Parameter v should be pointer to empty data model which data filled in.
//Return any error raseid.
func (c *Collection) Unmarshal(bytes []byte, v interface{}) error {
	return c.Cache.Unmarshal(bytes, v)
}

//Collection get a cache colletion with given prefix
func (c *Collection) Collection(prefix string) *Collection {
	return NewCollection(c, prefix, c.TTL)
}

//Node get a cache node with given prefix
func (c *Collection) Node(prefix string) *Node {
	return NewNode(c, prefix)
}

//Field retuan a cache field with given field name
func (c *Collection) Field(fieldname string) *Field {
	return &Field{
		Cache:     c,
		FieldName: fieldname,
	}
}

//FinalKey get final key which passed to cache driver .
func (c *Collection) FinalKey(key string) (string, error) {
	return c.Cache.FinalKey(c.Prefix + KeyPrefix + key)
}
<|endoftext|>


<commit_before>package testutil

// TestServer is a test helper. It uses a fork/exec model to create
// a test Nomad server instance in the background and initialize it
// with some data and/or services. The test server can then be used
// to run a unit test, and offers an easy API to tear itself down
// when the test has completed. The only prerequisite is to have a nomad
// binary available on the $PATH.
//
// This package does not use Nomad's official API client. This is
// because we use TestServer to test the API client, which would
// otherwise cause an import cycle.

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"sync/atomic"
	"testing"

	"github.com/hashicorp/go-cleanhttp"
)

// offset is used to atomically increment the port numbers.
var offset uint64

// TestServerConfig is the main server configuration struct.
type TestServerConfig struct {
	NodeName          string        `json:"name,omitempty"`
	DataDir           string        `json:"data_dir,omitempty"`
	Region            string        `json:"region,omitempty"`
	DisableCheckpoint bool          `json:"disable_update_check"`
	LogLevel          string        `json:"log_level,omitempty"`
	AdvertiseAddrs    *Advertise    `json:"advertise,omitempty"`
	Ports             *PortsConfig  `json:"ports,omitempty"`
	Server            *ServerConfig `json:"server,omitempty"`
	Client            *ClientConfig `json:"client,omitempty"`
	Vault             *VaultConfig  `json:"vault,omitempty"`
	DevMode           bool          `json:"-"`
	Stdout, Stderr    io.Writer     `json:"-"`
}

// Advertise is used to configure the addresses to advertise
type Advertise struct {
	HTTP string `json:"http,omitempty"`
	RPC  string `json:"rpc,omitempty"`
	Serf string `json:"serf,omitempty"`
}

// PortsConfig is used to configure the network ports we use.
type PortsConfig struct {
	HTTP int `json:"http,omitempty"`
	RPC  int `json:"rpc,omitempty"`
	Serf int `json:"serf,omitempty"`
}

// ServerConfig is used to configure the nomad server.
type ServerConfig struct {
	Enabled         bool `json:"enabled"`
	BootstrapExpect int  `json:"bootstrap_expect"`
}

// ClientConfig is used to configure the client
type ClientConfig struct {
	Enabled bool `json:"enabled"`
}

// VaultConfig is used to configure Vault
type VaultConfig struct {
	Enabled bool `json:"enabled"`
}

// ServerConfigCallback is a function interface which can be
// passed to NewTestServerConfig to modify the server config.
type ServerConfigCallback func(c *TestServerConfig)

// defaultServerConfig returns a new TestServerConfig struct
// with all of the listen ports incremented by one.
func defaultServerConfig() *TestServerConfig {
	idx := int(atomic.AddUint64(&offset, 1))

	return &TestServerConfig{
		NodeName:          fmt.Sprintf("node%d", idx),
		DisableCheckpoint: true,
		LogLevel:          "DEBUG",
		// Advertise can't be localhost
		AdvertiseAddrs: &Advertise{
			HTTP: "169.254.42.42",
			RPC:  "169.254.42.42",
			Serf: "169.254.42.42",
		},
		Ports: &PortsConfig{
			HTTP: 20000 + idx,
			RPC:  21000 + idx,
			Serf: 22000 + idx,
		},
		Server: &ServerConfig{
			Enabled:         true,
			BootstrapExpect: 1,
		},
		Client: &ClientConfig{
			Enabled: false,
		},
		Vault: &VaultConfig{
			Enabled: false,
		},
	}
}

// TestServer is the main server wrapper struct.
type TestServer struct {
	cmd    *exec.Cmd
	Config *TestServerConfig
	t      *testing.T

	HTTPAddr   string
	SerfAddr   string
	HTTPClient *http.Client
}

// NewTestServer creates a new TestServer, and makes a call to
// an optional callback function to modify the configuration.
func NewTestServer(t *testing.T, cb ServerConfigCallback) *TestServer {
	if path, err := exec.LookPath("nomad"); err != nil || path == "" {
		t.Skip("nomad not found on $PATH, skipping")
	}

	dataDir, err := ioutil.TempDir("", "nomad")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	configFile, err := ioutil.TempFile(dataDir, "nomad")
	if err != nil {
		defer os.RemoveAll(dataDir)
		t.Fatalf("err: %s", err)
	}
	defer configFile.Close()

	nomadConfig := defaultServerConfig()
	nomadConfig.DataDir = dataDir

	if cb != nil {
		cb(nomadConfig)
	}

	configContent, err := json.Marshal(nomadConfig)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if _, err := configFile.Write(configContent); err != nil {
		t.Fatalf("err: %s", err)
	}
	configFile.Close()

	stdout := io.Writer(os.Stdout)
	if nomadConfig.Stdout != nil {
		stdout = nomadConfig.Stdout
	}

	stderr := io.Writer(os.Stderr)
	if nomadConfig.Stderr != nil {
		stderr = nomadConfig.Stderr
	}

	args := []string{"agent", "-config", configFile.Name()}
	if nomadConfig.DevMode {
		args = append(args, "-dev")
	}

	// Start the server
	cmd := exec.Command("nomad", args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	if err := cmd.Start(); err != nil {
		t.Fatalf("err: %s", err)
	}

	client := cleanhttp.DefaultClient()

	server := &TestServer{
		Config: nomadConfig,
		cmd:    cmd,
		t:      t,

		HTTPAddr:   fmt.Sprintf("127.0.0.1:%d", nomadConfig.Ports.HTTP),
		SerfAddr:   fmt.Sprintf("127.0.0.1:%d", nomadConfig.Ports.Serf),
		HTTPClient: client,
	}

	// Wait for the server to be ready
	if nomadConfig.Server.Enabled && nomadConfig.Server.BootstrapExpect != 0 {
		server.waitForLeader()
	} else {
		server.waitForAPI()
	}

	// Wait for the client to be ready
	if nomadConfig.DevMode {
		server.waitForClient()
	}
	return server
}

// Stop stops the test Nomad server, and removes the Nomad data
// directory once we are done.
func (s *TestServer) Stop() {
	defer os.RemoveAll(s.Config.DataDir)

	if err := s.cmd.Process.Kill(); err != nil {
		s.t.Errorf("err: %s", err)
	}

	// wait for the process to exit to be sure that the data dir can be
	// deleted on all platforms.
	s.cmd.Wait()
}

// waitForAPI waits for only the agent HTTP endpoint to start
// responding. This is an indication that the agent has started,
// but will likely return before a leader is elected.
func (s *TestServer) waitForAPI() {
	WaitForResult(func() (bool, error) {
		resp, err := s.HTTPClient.Get(s.url("/v1/agent/self"))
		if err != nil {
			return false, err
		}
		defer resp.Body.Close()
		if err := s.requireOK(resp); err != nil {
			return false, err
		}
		return true, nil
	}, func(err error) {
		defer s.Stop()
		s.t.Fatalf("err: %s", err)
	})
}

// waitForLeader waits for the Nomad server's HTTP API to become
// available, and then waits for a known leader and an index of
// 1 or more to be observed to confirm leader election is done.
func (s *TestServer) waitForLeader() {
	WaitForResult(func() (bool, error) {
		// Query the API and check the status code
		resp, err := s.HTTPClient.Get(s.url("/v1/jobs"))
		if err != nil {
			return false, err
		}
		defer resp.Body.Close()
		if err := s.requireOK(resp); err != nil {
			return false, err
		}

		// Ensure we have a leader and a node registeration
		if leader := resp.Header.Get("X-Nomad-KnownLeader"); leader != "true" {
			return false, fmt.Errorf("Nomad leader status: %#v", leader)
		}
		return true, nil
	}, func(err error) {
		defer s.Stop()
		s.t.Fatalf("err: %s", err)
	})
}

// waitForClient waits for the Nomad client to be ready. The function returns
// immediately if the server is not in dev mode.
func (s *TestServer) waitForClient() {
	if !s.Config.DevMode {
		return
	}

	WaitForResult(func() (bool, error) {
		resp, err := s.HTTPClient.Get(s.url("/v1/nodes"))
		if err != nil {
			return false, err
		}
		defer resp.Body.Close()
		if err := s.requireOK(resp); err != nil {
			return false, err
		}

		var decoded []struct {
			ID     string
			Status string
		}

		dec := json.NewDecoder(resp.Body)
		if err := dec.Decode(&decoded); err != nil {
			return false, err
		}

		if len(decoded) != 1 || decoded[0].Status != "ready" {
			return false, fmt.Errorf("Node not ready: %v", decoded)
		}

		return true, nil
	}, func(err error) {
		defer s.Stop()
		s.t.Fatalf("err: %s", err)
	})
}

// url is a helper function which takes a relative URL and
// makes it into a proper URL against the local Nomad server.
func (s *TestServer) url(path string) string {
	return fmt.Sprintf("http://%s%s", s.HTTPAddr, path)
}

// requireOK checks the HTTP response code and ensures it is acceptable.
func (s *TestServer) requireOK(resp *http.Response) error {
	if resp.StatusCode != 200 {
		return fmt.Errorf("Bad status code: %d", resp.StatusCode)
	}
	return nil
}

// put performs a new HTTP PUT request.
func (s *TestServer) put(path string, body io.Reader) *http.Response {
	req, err := http.NewRequest("PUT", s.url(path), body)
	if err != nil {
		s.t.Fatalf("err: %s", err)
	}
	resp, err := s.HTTPClient.Do(req)
	if err != nil {
		s.t.Fatalf("err: %s", err)
	}
	if err := s.requireOK(resp); err != nil {
		defer resp.Body.Close()
		s.t.Fatal(err)
	}
	return resp
}

// get performs a new HTTP GET request.
func (s *TestServer) get(path string) *http.Response {
	resp, err := s.HTTPClient.Get(s.url(path))
	if err != nil {
		s.t.Fatalf("err: %s", err)
	}
	if err := s.requireOK(resp); err != nil {
		defer resp.Body.Close()
		s.t.Fatal(err)
	}
	return resp
}

// encodePayload returns a new io.Reader wrapping the encoded contents
// of the payload, suitable for passing directly to a new request.
func (s *TestServer) encodePayload(payload interface{}) io.Reader {
	var encoded bytes.Buffer
	enc := json.NewEncoder(&encoded)
	if err := enc.Encode(payload); err != nil {
		s.t.Fatalf("err: %s", err)
	}
	return &encoded
}
<commit_msg>Change testserver binary lookup<commit_after>package testutil

// TestServer is a test helper. It uses a fork/exec model to create
// a test Nomad server instance in the background and initialize it
// with some data and/or services. The test server can then be used
// to run a unit test, and offers an easy API to tear itself down
// when the test has completed. The only prerequisite is to have a nomad
// binary available on the $PATH.
//
// This package does not use Nomad's official API client. This is
// because we use TestServer to test the API client, which would
// otherwise cause an import cycle.

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"sync/atomic"
	"testing"

	"github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/nomad/helper/discover"
)

// offset is used to atomically increment the port numbers.
var offset uint64

// TestServerConfig is the main server configuration struct.
type TestServerConfig struct {
	NodeName          string        `json:"name,omitempty"`
	DataDir           string        `json:"data_dir,omitempty"`
	Region            string        `json:"region,omitempty"`
	DisableCheckpoint bool          `json:"disable_update_check"`
	LogLevel          string        `json:"log_level,omitempty"`
	AdvertiseAddrs    *Advertise    `json:"advertise,omitempty"`
	Ports             *PortsConfig  `json:"ports,omitempty"`
	Server            *ServerConfig `json:"server,omitempty"`
	Client            *ClientConfig `json:"client,omitempty"`
	Vault             *VaultConfig  `json:"vault,omitempty"`
	DevMode           bool          `json:"-"`
	Stdout, Stderr    io.Writer     `json:"-"`
}

// Advertise is used to configure the addresses to advertise
type Advertise struct {
	HTTP string `json:"http,omitempty"`
	RPC  string `json:"rpc,omitempty"`
	Serf string `json:"serf,omitempty"`
}

// PortsConfig is used to configure the network ports we use.
type PortsConfig struct {
	HTTP int `json:"http,omitempty"`
	RPC  int `json:"rpc,omitempty"`
	Serf int `json:"serf,omitempty"`
}

// ServerConfig is used to configure the nomad server.
type ServerConfig struct {
	Enabled         bool `json:"enabled"`
	BootstrapExpect int  `json:"bootstrap_expect"`
}

// ClientConfig is used to configure the client
type ClientConfig struct {
	Enabled bool `json:"enabled"`
}

// VaultConfig is used to configure Vault
type VaultConfig struct {
	Enabled bool `json:"enabled"`
}

// ServerConfigCallback is a function interface which can be
// passed to NewTestServerConfig to modify the server config.
type ServerConfigCallback func(c *TestServerConfig)

// defaultServerConfig returns a new TestServerConfig struct
// with all of the listen ports incremented by one.
func defaultServerConfig() *TestServerConfig {
	idx := int(atomic.AddUint64(&offset, 1))

	return &TestServerConfig{
		NodeName:          fmt.Sprintf("node%d", idx),
		DisableCheckpoint: true,
		LogLevel:          "DEBUG",
		// Advertise can't be localhost
		AdvertiseAddrs: &Advertise{
			HTTP: "169.254.42.42",
			RPC:  "169.254.42.42",
			Serf: "169.254.42.42",
		},
		Ports: &PortsConfig{
			HTTP: 20000 + idx,
			RPC:  21000 + idx,
			Serf: 22000 + idx,
		},
		Server: &ServerConfig{
			Enabled:         true,
			BootstrapExpect: 1,
		},
		Client: &ClientConfig{
			Enabled: false,
		},
		Vault: &VaultConfig{
			Enabled: false,
		},
	}
}

// TestServer is the main server wrapper struct.
type TestServer struct {
	cmd    *exec.Cmd
	Config *TestServerConfig
	t      *testing.T

	HTTPAddr   string
	SerfAddr   string
	HTTPClient *http.Client
}

// NewTestServer creates a new TestServer, and makes a call to
// an optional callback function to modify the configuration.
func NewTestServer(t *testing.T, cb ServerConfigCallback) *TestServer {
	path, err := discover.NomadExecutable()
	if err != nil {
		t.Skipf("nomad not found, skipping: %v", err)
	}

	dataDir, err := ioutil.TempDir("", "nomad")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	configFile, err := ioutil.TempFile(dataDir, "nomad")
	if err != nil {
		defer os.RemoveAll(dataDir)
		t.Fatalf("err: %s", err)
	}
	defer configFile.Close()

	nomadConfig := defaultServerConfig()
	nomadConfig.DataDir = dataDir

	if cb != nil {
		cb(nomadConfig)
	}

	configContent, err := json.Marshal(nomadConfig)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if _, err := configFile.Write(configContent); err != nil {
		t.Fatalf("err: %s", err)
	}
	configFile.Close()

	stdout := io.Writer(os.Stdout)
	if nomadConfig.Stdout != nil {
		stdout = nomadConfig.Stdout
	}

	stderr := io.Writer(os.Stderr)
	if nomadConfig.Stderr != nil {
		stderr = nomadConfig.Stderr
	}

	args := []string{"agent", "-config", configFile.Name()}
	if nomadConfig.DevMode {
		args = append(args, "-dev")
	}

	// Start the server
	cmd := exec.Command(path, args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	if err := cmd.Start(); err != nil {
		t.Fatalf("err: %s", err)
	}

	client := cleanhttp.DefaultClient()

	server := &TestServer{
		Config: nomadConfig,
		cmd:    cmd,
		t:      t,

		HTTPAddr:   fmt.Sprintf("127.0.0.1:%d", nomadConfig.Ports.HTTP),
		SerfAddr:   fmt.Sprintf("127.0.0.1:%d", nomadConfig.Ports.Serf),
		HTTPClient: client,
	}

	// Wait for the server to be ready
	if nomadConfig.Server.Enabled && nomadConfig.Server.BootstrapExpect != 0 {
		server.waitForLeader()
	} else {
		server.waitForAPI()
	}

	// Wait for the client to be ready
	if nomadConfig.DevMode {
		server.waitForClient()
	}
	return server
}

// Stop stops the test Nomad server, and removes the Nomad data
// directory once we are done.
func (s *TestServer) Stop() {
	defer os.RemoveAll(s.Config.DataDir)

	if err := s.cmd.Process.Kill(); err != nil {
		s.t.Errorf("err: %s", err)
	}

	// wait for the process to exit to be sure that the data dir can be
	// deleted on all platforms.
	s.cmd.Wait()
}

// waitForAPI waits for only the agent HTTP endpoint to start
// responding. This is an indication that the agent has started,
// but will likely return before a leader is elected.
func (s *TestServer) waitForAPI() {
	WaitForResult(func() (bool, error) {
		resp, err := s.HTTPClient.Get(s.url("/v1/agent/self"))
		if err != nil {
			return false, err
		}
		defer resp.Body.Close()
		if err := s.requireOK(resp); err != nil {
			return false, err
		}
		return true, nil
	}, func(err error) {
		defer s.Stop()
		s.t.Fatalf("err: %s", err)
	})
}

// waitForLeader waits for the Nomad server's HTTP API to become
// available, and then waits for a known leader and an index of
// 1 or more to be observed to confirm leader election is done.
func (s *TestServer) waitForLeader() {
	WaitForResult(func() (bool, error) {
		// Query the API and check the status code
		resp, err := s.HTTPClient.Get(s.url("/v1/jobs"))
		if err != nil {
			return false, err
		}
		defer resp.Body.Close()
		if err := s.requireOK(resp); err != nil {
			return false, err
		}

		// Ensure we have a leader and a node registeration
		if leader := resp.Header.Get("X-Nomad-KnownLeader"); leader != "true" {
			return false, fmt.Errorf("Nomad leader status: %#v", leader)
		}
		return true, nil
	}, func(err error) {
		defer s.Stop()
		s.t.Fatalf("err: %s", err)
	})
}

// waitForClient waits for the Nomad client to be ready. The function returns
// immediately if the server is not in dev mode.
func (s *TestServer) waitForClient() {
	if !s.Config.DevMode {
		return
	}

	WaitForResult(func() (bool, error) {
		resp, err := s.HTTPClient.Get(s.url("/v1/nodes"))
		if err != nil {
			return false, err
		}
		defer resp.Body.Close()
		if err := s.requireOK(resp); err != nil {
			return false, err
		}

		var decoded []struct {
			ID     string
			Status string
		}

		dec := json.NewDecoder(resp.Body)
		if err := dec.Decode(&decoded); err != nil {
			return false, err
		}

		if len(decoded) != 1 || decoded[0].Status != "ready" {
			return false, fmt.Errorf("Node not ready: %v", decoded)
		}

		return true, nil
	}, func(err error) {
		defer s.Stop()
		s.t.Fatalf("err: %s", err)
	})
}

// url is a helper function which takes a relative URL and
// makes it into a proper URL against the local Nomad server.
func (s *TestServer) url(path string) string {
	return fmt.Sprintf("http://%s%s", s.HTTPAddr, path)
}

// requireOK checks the HTTP response code and ensures it is acceptable.
func (s *TestServer) requireOK(resp *http.Response) error {
	if resp.StatusCode != 200 {
		return fmt.Errorf("Bad status code: %d", resp.StatusCode)
	}
	return nil
}

// put performs a new HTTP PUT request.
func (s *TestServer) put(path string, body io.Reader) *http.Response {
	req, err := http.NewRequest("PUT", s.url(path), body)
	if err != nil {
		s.t.Fatalf("err: %s", err)
	}
	resp, err := s.HTTPClient.Do(req)
	if err != nil {
		s.t.Fatalf("err: %s", err)
	}
	if err := s.requireOK(resp); err != nil {
		defer resp.Body.Close()
		s.t.Fatal(err)
	}
	return resp
}

// get performs a new HTTP GET request.
func (s *TestServer) get(path string) *http.Response {
	resp, err := s.HTTPClient.Get(s.url(path))
	if err != nil {
		s.t.Fatalf("err: %s", err)
	}
	if err := s.requireOK(resp); err != nil {
		defer resp.Body.Close()
		s.t.Fatal(err)
	}
	return resp
}

// encodePayload returns a new io.Reader wrapping the encoded contents
// of the payload, suitable for passing directly to a new request.
func (s *TestServer) encodePayload(payload interface{}) io.Reader {
	var encoded bytes.Buffer
	enc := json.NewEncoder(&encoded)
	if err := enc.Encode(payload); err != nil {
		s.t.Fatalf("err: %s", err)
	}
	return &encoded
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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.
*/

// Refresh retries GitHub status updates for stale PR statuses.
package main

import (
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/prow/interrupts"

	"k8s.io/test-infra/pkg/flagutil"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/config/secret"
	prowflagutil "k8s.io/test-infra/prow/flagutil"
	"k8s.io/test-infra/prow/pluginhelp/externalplugins"
)

type options struct {
	port int

	configPath string
	dryRun     bool
	github     prowflagutil.GitHubOptions
	prowURL    string

	webhookSecretFile string
}

func (o *options) Validate() error {
	for _, group := range []flagutil.OptionGroup{&o.github} {
		if err := group.Validate(o.dryRun); err != nil {
			return err
		}
	}

	if _, err := url.ParseRequestURI(o.prowURL); err != nil {
		return fmt.Errorf("invalid -prow-url URI: %q", o.prowURL)
	}

	return nil
}

func gatherOptions() options {
	o := options{}
	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	fs.IntVar(&o.port, "port", 8888, "Port to listen on.")
	fs.StringVar(&o.configPath, "config-path", "/etc/config/config.yaml", "Path to config.yaml.")
	fs.BoolVar(&o.dryRun, "dry-run", true, "Dry run for testing. Uses API tokens but does not mutate.")
	fs.StringVar(&o.webhookSecretFile, "hmac-secret-file", "/etc/webhook/hmac", "Path to the file containing the GitHub HMAC secret.")
	fs.StringVar(&o.prowURL, "prow-url", "", "Prow frontend URL.")
	for _, group := range []flagutil.OptionGroup{&o.github} {
		group.AddFlags(fs)
	}
	fs.Parse(os.Args[1:])
	return o
}

func main() {
	o := gatherOptions()
	if err := o.Validate(); err != nil {
		logrus.Fatalf("Invalid options: %v", err)
	}

	logrus.SetFormatter(&logrus.JSONFormatter{})
	// TODO: Use global option from the prow config.
	logrus.SetLevel(logrus.DebugLevel)
	log := logrus.StandardLogger().WithField("plugin", "refresh")

	configAgent := &config.Agent{}
	if err := configAgent.Start(o.configPath, ""); err != nil {
		log.WithError(err).Fatal("Error starting config agent.")
	}

	secretAgent := &secret.Agent{}
	if err := secretAgent.Start([]string{o.github.TokenPath, o.webhookSecretFile}); err != nil {
		logrus.WithError(err).Fatal("Error starting secrets agent.")
	}

	githubClient, err := o.github.GitHubClient(secretAgent, o.dryRun)
	if err != nil {
		logrus.WithError(err).Fatal("Error getting GitHub client.")
	}

	serv := &server{
		tokenGenerator: secretAgent.GetTokenGenerator(o.webhookSecretFile),
		prowURL:        o.prowURL,
		configAgent:    configAgent,
		ghc:            githubClient,
		log:            log,
	}

	mux := http.NewServeMux()
	mux.Handle("/", serv)
	externalplugins.ServeExternalPluginHelp(mux, log, helpProvider)
	httpServer := &http.Server{Addr: ":" + strconv.Itoa(o.port), Handler: mux}
	defer interrupts.WaitForGracefulShutdown()
	interrupts.ListenAndServe(httpServer, 5*time.Second)
}
<commit_msg>add read/liveness probes in refresh external plugin<commit_after>/*
Copyright 2017 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.
*/

// Refresh retries GitHub status updates for stale PR statuses.
package main

import (
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/prow/interrupts"
	"k8s.io/test-infra/prow/pjutil"

	"k8s.io/test-infra/pkg/flagutil"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/config/secret"
	prowflagutil "k8s.io/test-infra/prow/flagutil"
	"k8s.io/test-infra/prow/pluginhelp/externalplugins"
)

type options struct {
	port int

	configPath string
	dryRun     bool
	github     prowflagutil.GitHubOptions
	prowURL    string

	webhookSecretFile string
}

func (o *options) Validate() error {
	for _, group := range []flagutil.OptionGroup{&o.github} {
		if err := group.Validate(o.dryRun); err != nil {
			return err
		}
	}

	if _, err := url.ParseRequestURI(o.prowURL); err != nil {
		return fmt.Errorf("invalid -prow-url URI: %q", o.prowURL)
	}

	return nil
}

func gatherOptions() options {
	o := options{}
	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	fs.IntVar(&o.port, "port", 8888, "Port to listen on.")
	fs.StringVar(&o.configPath, "config-path", "/etc/config/config.yaml", "Path to config.yaml.")
	fs.BoolVar(&o.dryRun, "dry-run", true, "Dry run for testing. Uses API tokens but does not mutate.")
	fs.StringVar(&o.webhookSecretFile, "hmac-secret-file", "/etc/webhook/hmac", "Path to the file containing the GitHub HMAC secret.")
	fs.StringVar(&o.prowURL, "prow-url", "", "Prow frontend URL.")
	for _, group := range []flagutil.OptionGroup{&o.github} {
		group.AddFlags(fs)
	}
	fs.Parse(os.Args[1:])
	return o
}

func main() {
	o := gatherOptions()
	if err := o.Validate(); err != nil {
		logrus.Fatalf("Invalid options: %v", err)
	}

	logrus.SetFormatter(&logrus.JSONFormatter{})
	// TODO: Use global option from the prow config.
	logrus.SetLevel(logrus.DebugLevel)
	log := logrus.StandardLogger().WithField("plugin", "refresh")

	configAgent := &config.Agent{}
	if err := configAgent.Start(o.configPath, ""); err != nil {
		log.WithError(err).Fatal("Error starting config agent.")
	}

	secretAgent := &secret.Agent{}
	if err := secretAgent.Start([]string{o.github.TokenPath, o.webhookSecretFile}); err != nil {
		logrus.WithError(err).Fatal("Error starting secrets agent.")
	}

	githubClient, err := o.github.GitHubClient(secretAgent, o.dryRun)
	if err != nil {
		logrus.WithError(err).Fatal("Error getting GitHub client.")
	}

	serv := &server{
		tokenGenerator: secretAgent.GetTokenGenerator(o.webhookSecretFile),
		prowURL:        o.prowURL,
		configAgent:    configAgent,
		ghc:            githubClient,
		log:            log,
	}

	health := pjutil.NewHealth()
	health.ServeReady()

	mux := http.NewServeMux()
	mux.Handle("/", serv)
	externalplugins.ServeExternalPluginHelp(mux, log, helpProvider)
	httpServer := &http.Server{Addr: ":" + strconv.Itoa(o.port), Handler: mux}
	defer interrupts.WaitForGracefulShutdown()
	interrupts.ListenAndServe(httpServer, 5*time.Second)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Tekton 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 v1alpha1_test

import (
	"fmt"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
	tb "github.com/tektoncd/pipeline/test/builder"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"knative.dev/pkg/apis"
)

func TestPipelineRunStatusConditions(t *testing.T) {
	p := &v1alpha1.PipelineRun{}
	foo := &apis.Condition{
		Type:   "Foo",
		Status: "True",
	}
	bar := &apis.Condition{
		Type:   "Bar",
		Status: "True",
	}

	var ignoreVolatileTime = cmp.Comparer(func(_, _ apis.VolatileTime) bool {
		return true
	})

	// Add a new condition.
	p.Status.SetCondition(foo)

	fooStatus := p.Status.GetCondition(foo.Type)
	if d := cmp.Diff(fooStatus, foo, ignoreVolatileTime); d != "" {
		t.Errorf("Unexpected pipeline run condition type; want %v got %v; diff %v", fooStatus, foo, d)
	}

	// Add a second condition.
	p.Status.SetCondition(bar)

	barStatus := p.Status.GetCondition(bar.Type)

	if d := cmp.Diff(barStatus, bar, ignoreVolatileTime); d != "" {
		t.Fatalf("Unexpected pipeline run condition type; want %v got %v; diff %s", barStatus, bar, d)
	}
}

func TestPipelineRun_TaskRunref(t *testing.T) {
	p := &v1alpha1.PipelineRun{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-name",
			Namespace: "test-ns",
		},
	}

	expectTaskRunRef := corev1.ObjectReference{
		APIVersion: "tekton.dev/v1alpha1",
		Kind:       "TaskRun",
		Namespace:  p.Namespace,
		Name:       p.Name,
	}

	if d := cmp.Diff(p.GetTaskRunRef(), expectTaskRunRef); d != "" {
		t.Fatalf("Taskrun reference mismatch; want %v got %v; diff %s", expectTaskRunRef, p.GetTaskRunRef(), d)
	}
}

func TestInitializeConditions(t *testing.T) {
	p := &v1alpha1.PipelineRun{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-name",
			Namespace: "test-ns",
		},
	}
	p.Status.InitializeConditions()

	if p.Status.TaskRuns == nil {
		t.Fatalf("PipelineRun status not initialized correctly")
	}

	if p.Status.StartTime.IsZero() {
		t.Fatalf("PipelineRun StartTime not initialized correctly")
	}

	p.Status.TaskRuns["fooTask"] = &v1alpha1.PipelineRunTaskRunStatus{}

	p.Status.InitializeConditions()
	if len(p.Status.TaskRuns) != 1 {
		t.Fatalf("PipelineRun status getting reset")
	}
}

func TestPipelineRunIsDone(t *testing.T) {
	pr := &v1alpha1.PipelineRun{}
	foo := &apis.Condition{
		Type:   apis.ConditionSucceeded,
		Status: corev1.ConditionFalse,
	}
	pr.Status.SetCondition(foo)
	if !pr.IsDone() {
		t.Fatal("Expected pipelinerun status to be done")
	}
}

func TestPipelineRunIsCancelled(t *testing.T) {
	pr := &v1alpha1.PipelineRun{
		Spec: v1alpha1.PipelineRunSpec{
			Status: v1alpha1.PipelineRunSpecStatusCancelled,
		},
	}
	if !pr.IsCancelled() {
		t.Fatal("Expected pipelinerun status to be cancelled")
	}
}

func TestPipelineRunKey(t *testing.T) {
	pr := tb.PipelineRun("prunname", "testns")
	expectedKey := fmt.Sprintf("PipelineRun/%p", pr)
	if pr.GetRunKey() != expectedKey {
		t.Fatalf("Expected taskrun key to be %s but got %s", expectedKey, pr.GetRunKey())
	}
}

func TestPipelineRunHasStarted(t *testing.T) {
	params := []struct {
		name          string
		prStatus      v1alpha1.PipelineRunStatus
		expectedValue bool
	}{{
		name:          "prWithNoStartTime",
		prStatus:      v1alpha1.PipelineRunStatus{},
		expectedValue: false,
	}, {
		name: "prWithStartTime",
		prStatus: v1alpha1.PipelineRunStatus{
			PipelineRunStatusFields: v1alpha1.PipelineRunStatusFields{
				StartTime: &metav1.Time{Time: time.Now()},
			},
		},
		expectedValue: true,
	}, {
		name: "prWithZeroStartTime",
		prStatus: v1alpha1.PipelineRunStatus{
			PipelineRunStatusFields: v1alpha1.PipelineRunStatusFields{
				StartTime: &metav1.Time{},
			},
		},
		expectedValue: false,
	}}
	for _, tc := range params {
		t.Run(tc.name, func(t *testing.T) {
			pr := &v1alpha1.PipelineRun{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "prunname",
					Namespace: "testns",
				},
				Status: tc.prStatus,
			}
			if pr.HasStarted() != tc.expectedValue {
				t.Fatalf("Expected pipelinerun HasStarted() to return %t but got %t", tc.expectedValue, pr.HasStarted())
			}
		})
	}
}

func TestPipelineRunHasTimedOut(t *testing.T) {
	tcs := []struct {
		name      string
		timeout   time.Duration
		starttime time.Time
		expected  bool
	}{{
		name:      "timedout",
		timeout:   1 * time.Second,
		starttime: time.Now().AddDate(0, 0, -1),
		expected:  true,
	}, {
		name:      "nottimedout",
		timeout:   25 * time.Hour,
		starttime: time.Now().AddDate(0, 0, -1),
		expected:  false,
	}, {
		name:      "notimeoutspecified",
		timeout:   0 * time.Second,
		starttime: time.Now().AddDate(0, 0, -1),
		expected:  false,
	},
	}

	for _, tc := range tcs {
		t.Run(t.Name(), func(t *testing.T) {
			pr := tb.PipelineRun("pr", "foo",
				tb.PipelineRunSpec("test-pipeline",
					tb.PipelineRunTimeout(tc.timeout),
				),
				tb.PipelineRunStatus(
					tb.PipelineRunStartTime(tc.starttime),
				),
			)

			if pr.IsTimedOut() != tc.expected {
				t.Fatalf("Expected isTimedOut to be %t", tc.expected)
			}
		})
	}
}

func TestPipelineRunGetServiceAccountName(t *testing.T) {
	for _, tt := range []struct {
		name    string
		pr      *v1alpha1.PipelineRun
		saNames map[string]string
	}{
		{
			"default SA",
			tb.PipelineRun("pr", "ns",
				tb.PipelineRunSpec("prs",
					tb.PipelineRunServiceAccountName("defaultSA"),
					tb.PipelineRunServiceAccountNameTask("taskName", "taskSA"))),
			map[string]string{
				"unknown":  "defaultSA",
				"taskName": "taskSA",
			},
		},
		{
			"mixed default SA",
			tb.PipelineRun("defaultSA", "defaultSA",
				tb.PipelineRunSpec("defaultSA",
					tb.PipelineRunServiceAccountName("defaultSA"),
					tb.PipelineRunServiceAccountNameTask("task1", "task1SA"),
					tb.PipelineRunServiceAccountNameTask("task2", "task2SA"),
				)),
			map[string]string{
				"unknown": "defaultSA",
				"task1":   "task1SA",
				"task2":   "task2SA",
			},
		},
	} {
		for taskName, expected := range tt.saNames {
			sa := tt.pr.GetServiceAccountName(taskName)
			if expected != sa {
				t.Errorf("%s: wrong service account: got: %v, want: %v", tt.name, sa, expected)
			}
		}
	}
}
<commit_msg>Use named field in test tables 🌮<commit_after>/*
Copyright 2019 The Tekton 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 v1alpha1_test

import (
	"fmt"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
	tb "github.com/tektoncd/pipeline/test/builder"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"knative.dev/pkg/apis"
)

func TestPipelineRunStatusConditions(t *testing.T) {
	p := &v1alpha1.PipelineRun{}
	foo := &apis.Condition{
		Type:   "Foo",
		Status: "True",
	}
	bar := &apis.Condition{
		Type:   "Bar",
		Status: "True",
	}

	var ignoreVolatileTime = cmp.Comparer(func(_, _ apis.VolatileTime) bool {
		return true
	})

	// Add a new condition.
	p.Status.SetCondition(foo)

	fooStatus := p.Status.GetCondition(foo.Type)
	if d := cmp.Diff(fooStatus, foo, ignoreVolatileTime); d != "" {
		t.Errorf("Unexpected pipeline run condition type; want %v got %v; diff %v", fooStatus, foo, d)
	}

	// Add a second condition.
	p.Status.SetCondition(bar)

	barStatus := p.Status.GetCondition(bar.Type)

	if d := cmp.Diff(barStatus, bar, ignoreVolatileTime); d != "" {
		t.Fatalf("Unexpected pipeline run condition type; want %v got %v; diff %s", barStatus, bar, d)
	}
}

func TestPipelineRun_TaskRunref(t *testing.T) {
	p := &v1alpha1.PipelineRun{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-name",
			Namespace: "test-ns",
		},
	}

	expectTaskRunRef := corev1.ObjectReference{
		APIVersion: "tekton.dev/v1alpha1",
		Kind:       "TaskRun",
		Namespace:  p.Namespace,
		Name:       p.Name,
	}

	if d := cmp.Diff(p.GetTaskRunRef(), expectTaskRunRef); d != "" {
		t.Fatalf("Taskrun reference mismatch; want %v got %v; diff %s", expectTaskRunRef, p.GetTaskRunRef(), d)
	}
}

func TestInitializeConditions(t *testing.T) {
	p := &v1alpha1.PipelineRun{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-name",
			Namespace: "test-ns",
		},
	}
	p.Status.InitializeConditions()

	if p.Status.TaskRuns == nil {
		t.Fatalf("PipelineRun status not initialized correctly")
	}

	if p.Status.StartTime.IsZero() {
		t.Fatalf("PipelineRun StartTime not initialized correctly")
	}

	p.Status.TaskRuns["fooTask"] = &v1alpha1.PipelineRunTaskRunStatus{}

	p.Status.InitializeConditions()
	if len(p.Status.TaskRuns) != 1 {
		t.Fatalf("PipelineRun status getting reset")
	}
}

func TestPipelineRunIsDone(t *testing.T) {
	pr := &v1alpha1.PipelineRun{}
	foo := &apis.Condition{
		Type:   apis.ConditionSucceeded,
		Status: corev1.ConditionFalse,
	}
	pr.Status.SetCondition(foo)
	if !pr.IsDone() {
		t.Fatal("Expected pipelinerun status to be done")
	}
}

func TestPipelineRunIsCancelled(t *testing.T) {
	pr := &v1alpha1.PipelineRun{
		Spec: v1alpha1.PipelineRunSpec{
			Status: v1alpha1.PipelineRunSpecStatusCancelled,
		},
	}
	if !pr.IsCancelled() {
		t.Fatal("Expected pipelinerun status to be cancelled")
	}
}

func TestPipelineRunKey(t *testing.T) {
	pr := tb.PipelineRun("prunname", "testns")
	expectedKey := fmt.Sprintf("PipelineRun/%p", pr)
	if pr.GetRunKey() != expectedKey {
		t.Fatalf("Expected taskrun key to be %s but got %s", expectedKey, pr.GetRunKey())
	}
}

func TestPipelineRunHasStarted(t *testing.T) {
	params := []struct {
		name          string
		prStatus      v1alpha1.PipelineRunStatus
		expectedValue bool
	}{{
		name:          "prWithNoStartTime",
		prStatus:      v1alpha1.PipelineRunStatus{},
		expectedValue: false,
	}, {
		name: "prWithStartTime",
		prStatus: v1alpha1.PipelineRunStatus{
			PipelineRunStatusFields: v1alpha1.PipelineRunStatusFields{
				StartTime: &metav1.Time{Time: time.Now()},
			},
		},
		expectedValue: true,
	}, {
		name: "prWithZeroStartTime",
		prStatus: v1alpha1.PipelineRunStatus{
			PipelineRunStatusFields: v1alpha1.PipelineRunStatusFields{
				StartTime: &metav1.Time{},
			},
		},
		expectedValue: false,
	}}
	for _, tc := range params {
		t.Run(tc.name, func(t *testing.T) {
			pr := &v1alpha1.PipelineRun{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "prunname",
					Namespace: "testns",
				},
				Status: tc.prStatus,
			}
			if pr.HasStarted() != tc.expectedValue {
				t.Fatalf("Expected pipelinerun HasStarted() to return %t but got %t", tc.expectedValue, pr.HasStarted())
			}
		})
	}
}

func TestPipelineRunHasTimedOut(t *testing.T) {
	tcs := []struct {
		name      string
		timeout   time.Duration
		starttime time.Time
		expected  bool
	}{{
		name:      "timedout",
		timeout:   1 * time.Second,
		starttime: time.Now().AddDate(0, 0, -1),
		expected:  true,
	}, {
		name:      "nottimedout",
		timeout:   25 * time.Hour,
		starttime: time.Now().AddDate(0, 0, -1),
		expected:  false,
	}, {
		name:      "notimeoutspecified",
		timeout:   0 * time.Second,
		starttime: time.Now().AddDate(0, 0, -1),
		expected:  false,
	},
	}

	for _, tc := range tcs {
		t.Run(t.Name(), func(t *testing.T) {
			pr := tb.PipelineRun("pr", "foo",
				tb.PipelineRunSpec("test-pipeline",
					tb.PipelineRunTimeout(tc.timeout),
				),
				tb.PipelineRunStatus(
					tb.PipelineRunStartTime(tc.starttime),
				),
			)

			if pr.IsTimedOut() != tc.expected {
				t.Fatalf("Expected isTimedOut to be %t", tc.expected)
			}
		})
	}
}

func TestPipelineRunGetServiceAccountName(t *testing.T) {
	for _, tt := range []struct {
		name    string
		pr      *v1alpha1.PipelineRun
		saNames map[string]string
	}{
		{
			name: "default SA",
			pr: tb.PipelineRun("pr", "ns",
				tb.PipelineRunSpec("prs",
					tb.PipelineRunServiceAccountName("defaultSA"),
					tb.PipelineRunServiceAccountNameTask("taskName", "taskSA"))),
			saNames: map[string]string{
				"unknown":  "defaultSA",
				"taskName": "taskSA",
			},
		},
		{
			name: "mixed default SA",
			pr: tb.PipelineRun("defaultSA", "defaultSA",
				tb.PipelineRunSpec("defaultSA",
					tb.PipelineRunServiceAccountName("defaultSA"),
					tb.PipelineRunServiceAccountNameTask("task1", "task1SA"),
					tb.PipelineRunServiceAccountNameTask("task2", "task2SA"),
				)),
			saNames: map[string]string{
				"unknown": "defaultSA",
				"task1":   "task1SA",
				"task2":   "task2SA",
			},
		},
	} {
		for taskName, expected := range tt.saNames {
			sa := tt.pr.GetServiceAccountName(taskName)
			if expected != sa {
				t.Errorf("%s: wrong service account: got: %v, want: %v", tt.name, sa, expected)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/flant/kubedog/pkg/kube"
	"github.com/flant/werf/pkg/deploy"
	"github.com/flant/werf/pkg/lock"
)

func main() {
	if err := lock.Init(); err != nil {
		fmt.Fprintf(os.Stderr, "Lock init error: %s\n", err)
		os.Exit(1)
	}

	if err := kube.Init(kube.InitOptions{}); err != nil {
		fmt.Fprintf(os.Stderr, "Kube init error: %s\n", err)
		os.Exit(1)
	}

	err := deploy.DeployHelmChart(os.Args[1], os.Args[2], os.Args[3], deploy.HelmChartOptions{})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error deploying helm chart: %s\n", err)
		os.Exit(1)
	}
}
<commit_msg>[playground] Remove deploy-helm-chart<commit_after><|endoftext|>


<commit_before>package main

import (
    "fmt"
    "flag"
    "log"
    "runtime"
    "encoding/json"
    "functorama.com/demo/libgodelbrot"
)

// Golang entry point
func main() {
        // Set number of cores
    runtime.GOMAXPROCS(runtime.NumCPU())

    args := parseArguments()
    request, argErr := extractRenderParameters(args)
    if argErr != nil {
        log.Fatal("Error:", argErr)
    }

    text, jsonErr := json.Marshal(request)
    if jsonErr == nil {
        fmt.Print(text)
    } else {
        log.Fatal("Error creating JSON:", jsonErr)
    }
}

// Structure representing our command line arguments
type commandLine struct {
    iterateLimit   uint
    divergeLimit   float64
    width          uint
    height         uint
    realMin        string
    realMax        string
    imagMin        string
    imagMax        string
    mode           string
    regionCollapse uint
    renderThreads  uint
    storedPalette  string
    fixAspect      bool
    numericalSystem string
    glitchSamples uint
    precision uint
}

// Parse command line arguments into a `commandLine' structure
func parseArguments() commandLine {
    args := commandLine{}
    realMin := string(real(libgodelbrot.MandelbrotMin))
    imagMin := string(imag(libgodelbrot.MandelbrotMin))
    realMax := string(real(libgodelbrot.MandelbrotMax))
    imagMax := string(imag(libgodelbrot.MandelbrotMax))

    var renderThreads uint
    if cpus := runtime.NumCPU(); cpus > 1 {
        renderThreads = uint(cpus - 1)
    } else {
        renderThreads = 1
    }

    flag.UintVar(&args.iterateLimit, "iterateLimit",
        uint(libgodelbrot.DefaultIterations), "Maximum number of iterations")
    flag.Float64Var(&args.divergeLimit, "divergeLimit",
        libgodelbrot.DefaultDivergeLimit, "Limit where function is said to diverge to infinity")
    flag.UintVar(&args.width, "imageWidth",
        libgodelbrot.DefaultImageWidth, "Width of output PNG")
    flag.UintVar(&args.height, "imageHeight",
        libgodelbrot.DefaultImageHeight, "Height of output PNG")
    flag.StringVar(&args.realMin, "realMin",
        realMin, "Leftmost position on complex plane")
    flag.StringVar(&args.imagMax, "imagMax",
        imagMax, "Topmost position on complex plane")
    flag.StringVar(&args.realMax, "realMax",
        realMax, "Rightmost position on complex plane")
    flag.StringVar(&args.imagMin, "imagMin",
        imagMin, "Bottommost position on complex plane")
    flag.StringVar(&args.mode, "mode", "auto",
        "Render mode.  (auto|sequence|region|concurrent)")
    flag.UintVar(&args.regionCollapse, "collapse",
        libgodelbrot.DefaultCollapse, "Pixel width of region at which sequential render is forced")
    flag.UintVar(&args.renderThreads, "jobs",
        renderThreads, "Number of rendering threads in concurrent renderer")
    flag.UintVar(&args.glitchSamples, "regionGlitchSamples",
        libgodelbrot.DefaultRegionGlitchSampleSize, "Size of region render glitch-correncting sample set")
    flag.UintVar(&args.precision, "prec",
        libgodelbrot.DefaultPrecision, "Precision for big.Float render mode")
    flag.StringVar(&args.storedPalette, "storedPalette",
        "pretty", "Name of stored palette (pretty|redscale)")
    flag.StringVAr(&args.numericalSystem, "numerics",
        "auto", "Numerical system (auto|native|bigfloat)")
    flag.BoolVar(&args.fixAspect, "fixAspect",
        true, "Resize plane window to fit image aspect ratio")
    flag.Parse()

    return args
}

// Validate and extract a render description from the command line arguments
func extractRenderParameters(args commandLine) (libgodelbrot.RenderDescription, error) {
    if args.iterateLimit > 255 {
        return nil, fmt.Errorf("iterateLimit out of bounds.  Valid values in range (0,255)")
    }

    if args.divergeLimit <= 0.0 {
        return nil, fmt.Errorf("divergeLimit out of bounds.  Valid values in range (0,)")
    }

    const max16 = uint(^uint16(0))
    if args.Jobs > max16 {
        return nil, fmt.Errorf("jobs out of bounds.  Valid values in range (0, 65535)")
    }

    numerics := libgodelbrot.AutoDetectNumericsMode
    switch args.numerics {
    case "auto":
        // No change
    case "bigfloat":
        numerics = libgodelbrot.BigFloatNumericsMode
    case "native":
        numerics = libgodelbrot.NativeNumericsMode
    default:
        log.Fatal("Unknown numerics mode:", args.numerics)
    }

    renderer := libgodelbrot.AutoDetectRenderMode
    switch args.mode {
    case "auto":
        // No change
    case "sequence":
        renderer = libgodelbrot.SequenceRenderMode
    case "region":
        renderer = libgodelbrot.RegionRenderMode
    case "concurrent":
        renderer = libgodelbrot.SharedRegionRenderMode
    default:
        log.Fatal("Unknown render mode:", args.mode)
    }

    description := libgodelbrot.Request {
        RealMin: args.realMin,
        RealMax: args.realMax,
        ImagMin: args.imagMin,
        ImagMax: args.imagMax,
        ImageWidth: args.imageWidth,
        ImageHeight: args.imageHeight,
        ThreadBufferSize: args.threadBuffer,
        PaletteType: libgodelbrot.StoredPalette,
        PaletteCode: args.storedPalette,
        FixAspect: args.fixAspect,
        Numerics: numerics,
        Renderer: renderer,
        Jobs: uint16(args.jobs),
    }

    return description, nil
}<commit_msg>Godelbrot configurator app<commit_after>package main

import (
    "fmt"
    "flag"
    "log"
    "runtime"
    "strconv"
    "functorama.com/demo/libgodelbrot"
)

// Golang entry point
func main() {
    // Set number of cores
    runtime.GOMAXPROCS(runtime.NumCPU())

    args := parseArguments()
    request, argErr := extractRenderParameters(args)
    if argErr != nil {
        log.Fatal("Error:", argErr)
    }

    info, godelErr := libgodelbrot.AutoConf(request)

    if godelErr != nil {
        log.Fatal(godelErr)
    }

    text, jsonErr := libgodelbrot.ToJSON(info)
    if jsonErr == nil {
        fmt.Printf("%s", text)
    } else {
        log.Fatal("Error creating JSON:", jsonErr)
    }
}

// Structure representing our command line arguments
type commandLine struct {
    iterateLimit   uint
    divergeLimit   float64
    width          uint
    height         uint
    realMin        string
    realMax        string
    imagMin        string
    imagMax        string
    mode           string
    regionCollapse uint
    jobs  uint
    storedPalette  string
    fixAspect      bool
    numerics string
    glitchSamples uint
    precision uint
}

// Parse command line arguments into a `commandLine' structure
func parseArguments() commandLine {
    args := commandLine{}

    components := []float64{
        real(libgodelbrot.MandelbrotMin),
        imag(libgodelbrot.MandelbrotMin),
        real(libgodelbrot.MandelbrotMax),
        imag(libgodelbrot.MandelbrotMax),
    }
    bounds := make([]string, len(components))
    for i, num := range components {
        bounds[i] = strconv.FormatFloat(num, 'e', -1, 64)
    }

    var renderThreads uint
    if cpus := runtime.NumCPU(); cpus > 1 {
        renderThreads = uint(cpus - 1)
    } else {
        renderThreads = 1
    }

    flag.UintVar(&args.iterateLimit, "iterateLimit",
        uint(libgodelbrot.DefaultIterations), "Maximum number of iterations")
    flag.Float64Var(&args.divergeLimit, "divergeLimit",
        libgodelbrot.DefaultDivergeLimit, "Limit where function is said to diverge to infinity")
    flag.UintVar(&args.width, "imageWidth",
        libgodelbrot.DefaultImageWidth, "Width of output PNG")
    flag.UintVar(&args.height, "imageHeight",
        libgodelbrot.DefaultImageHeight, "Height of output PNG")
    flag.StringVar(&args.realMin, "realMin",
        bounds[0], "Leftmost position on complex plane")
    flag.StringVar(&args.imagMin, "imagMin",
        bounds[1], "Bottommost position on complex plane")
    flag.StringVar(&args.realMax, "realMax",
        bounds[2], "Rightmost position on complex plane")
    flag.StringVar(&args.imagMax, "imagMax",
        bounds[3], "Topmost position on complex plane")
    flag.StringVar(&args.mode, "mode", "auto",
        "Render mode.  (auto|sequence|region|concurrent)")
    flag.UintVar(&args.regionCollapse, "collapse",
        libgodelbrot.DefaultCollapse, "Pixel width of region at which sequential render is forced")
    flag.UintVar(&args.jobs, "jobs",
        renderThreads, "Number of rendering threads in concurrent renderer")
    flag.UintVar(&args.glitchSamples, "regionGlitchSamples",
        libgodelbrot.DefaultGlitchSamples, "Size of region render glitch-correncting sample set")
    flag.UintVar(&args.precision, "prec",
        libgodelbrot.DefaultPrecision, "Precision for big.Float render mode")
    flag.StringVar(&args.storedPalette, "storedPalette",
        "pretty", "Name of stored palette (pretty|redscale)")
    flag.StringVar(&args.numerics, "numerics",
        "auto", "Numerical system (auto|native|bigfloat)")
    flag.BoolVar(&args.fixAspect, "fixAspect",
        true, "Resize plane window to fit image aspect ratio")
    flag.Parse()

    return args
}

// Validate and extract a render description from the command line arguments
func extractRenderParameters(args commandLine) (*libgodelbrot.Request, error) {
    if args.iterateLimit > 255 {
        return nil, fmt.Errorf("iterateLimit out of bounds.  Valid values in range (0,255)")
    }

    if args.divergeLimit <= 0.0 {
        return nil, fmt.Errorf("divergeLimit out of bounds.  Valid values in range (0,)")
    }

    const max16 = uint(^uint16(0))
    if args.jobs > max16 {
        return nil, fmt.Errorf("jobs out of bounds.  Valid values in range (0, 65535)")
    }

    numerics := libgodelbrot.AutoDetectNumericsMode
    switch args.numerics {
    case "auto":
        // No change
    case "bigfloat":
        numerics = libgodelbrot.BigFloatNumericsMode
    case "native":
        numerics = libgodelbrot.NativeNumericsMode
    default:
        log.Fatal("Unknown numerics mode:", args.numerics)
    }

    renderer := libgodelbrot.AutoDetectRenderMode
    switch args.mode {
    case "auto":
        // No change
    case "sequence":
        renderer = libgodelbrot.SequenceRenderMode
    case "region":
        renderer = libgodelbrot.RegionRenderMode
    case "concurrent":
        renderer = libgodelbrot.SharedRegionRenderMode
    default:
        log.Fatal("Unknown render mode:", args.mode)
    }

    description := &libgodelbrot.Request {
        RealMin: args.realMin,
        RealMax: args.realMax,
        ImagMin: args.imagMin,
        ImagMax: args.imagMax,
        ImageWidth: args.width,
        ImageHeight: args.height,
        PaletteType: libgodelbrot.StoredPalette,
        PaletteCode: args.storedPalette,
        FixAspect: args.fixAspect,
        Numerics: numerics,
        Renderer: renderer,
        Jobs: uint16(args.jobs),
    }

    return description, nil
}<|endoftext|>