<commit_before>package common

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path"
	"reflect"
	"runtime"
	"strconv"
	"strings"
)

var (
	// Convenience Directories
	GoPath          = os.Getenv("GOPATH")
	ErisLtd         = path.Join(GoPath, "src", "github.com", "eris-ltd")
	usr, _          = user.Current() // error?!
	ErisRoot        = ResolveErisRoot()

	// Major Directories
	ActionsPath        = path.Join(ErisRoot, "actions")
	BlockchainsPath    = path.Join(ErisRoot, "blockchains")
	ChainsTypesPath    = path.Join(BlockchainsPath, "types")
	DataContainersPath = path.Join(ErisRoot, "data")
	DappsPath          = path.Join(ErisRoot, "dapps")
	FilesPath          = path.Join(ErisRoot, "files")
	KeysPath           = path.Join(ErisRoot, "keys")
	LanguagesPath      = path.Join(ErisRoot, "languages")
	ServicesPath       = path.Join(ErisRoot, "services")
	ScratchPath        = path.Join(ErisRoot, "scratch")

	// Scratch Directories (globally coordinated)
	EpmScratchPath  = path.Join(ScratchPath, "epm")
	LllcScratchPath = path.Join(ScratchPath, "lllc")
	SolcScratchPath = path.Join(ScratchPath, "sol")
	SerpScratchPath = path.Join(ScratchPath, "ser")

	// Blockchains stuff
	HEAD            = path.Join(BlockchainsPath, "HEAD")
	Refs            = path.Join(BlockchainsPath, "refs")
)

var MajorDirs = []string{
	ErisRoot, ActionsPath, BlockchainsPath, ChainsTypesPath, DataContainersPath, DappsPath, FilesPath, KeysPath, LanguagesPath, ServicesPath, ScratchPath, EpmScratchPath, LllcScratchPath, SolcScratchPath, SerpScratchPath,
}

//---------------------------------------------
// user and process

func Usr() string {
	u, _ := user.Current()
	return u.HomeDir
}

func Exit(err error) {
	status := 0
	if err != nil {
		fmt.Println(err)
		status = 1
	}
	os.Exit(status)
}

func IfExit(err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

// user and process
//---------------------------------------------------------------------------
// filesystem

func AbsolutePath(Datadir string, filename string) string {
	if path.IsAbs(filename) {
		return filename
	}
	return path.Join(Datadir, filename)
}

func InitDataDir(Datadir string) error {
	_, err := os.Stat(Datadir)
	if err != nil {
		if os.IsNotExist(err) {
			err := os.MkdirAll(Datadir, 0777)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func ResolveErisRoot() string {
	var eris string
	if os.Getenv("ERIS") != "" {
		eris = os.Getenv("ERIS")
	} else {
		if runtime.GOOS == "windows" {
			home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
			if home == "" {
				home = os.Getenv("USERPROFILE")
			}
			eris = path.Join(home, ".eris")
		} else {
			eris = path.Join(Usr(), ".eris")
		}
	}
	return eris
}

// Create the default eris tree
func InitErisDir() (err error) {
	for _, d := range MajorDirs {
		err := InitDataDir(d)
		if err != nil {
			return err
		}
	}
	if _, err = os.Stat(HEAD); err != nil {
		_, err = os.Create(HEAD)
	}
	return
}

func ClearDir(dir string) error {
	fs, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	for _, f := range fs {
		n := f.Name()
		if f.IsDir() {
			if err := os.RemoveAll(path.Join(dir, f.Name())); err != nil {
				return err
			}
		} else {
			if err := os.Remove(path.Join(dir, n)); err != nil {
				return err
			}
		}
	}
	return nil
}

func Copy(src, dst string) error {
	f, err := os.Stat(src)
	if err != nil {
		return err
	}
	if f.IsDir() {
		if _, err := os.Stat(dst); err == nil {
			return fmt.Errorf("destination already exists")
		}
		return copyDir(src, dst)
	}
	return copyFile(src, dst)
}

// assumes we've done our checking
func copyDir(src, dst string) error {
	fi, _ := os.Stat(src)
	if err := os.MkdirAll(dst, fi.Mode()); err != nil {
		return err
	}
	fs, err := ioutil.ReadDir(src)
	if err != nil {
		return err
	}

	for _, f := range fs {
		s := path.Join(src, f.Name())
		d := path.Join(dst, f.Name())
		if f.IsDir() {
			if err := copyDir(s, d); err != nil {
				return err
			}
		} else {
			if err := copyFile(s, d); err != nil {
				return err
			}
		}
	}
	return nil
}

// common golang, really?
func copyFile(src, dst string) error {
	r, err := os.Open(src)
	if err != nil {
		return err
	}
	defer r.Close()

	w, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer w.Close()

	_, err = io.Copy(w, r)
	if err != nil {
		return err
	}
	return nil
}

// filesystem
//-------------------------------------------------------
// hex and ints

// keeps N bytes of the conversion
func NumberToBytes(num interface{}, N int) []byte {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, num)
	if err != nil {
		// TODO: get this guy a return error?
	}
	//fmt.Println("btyes!", buf.Bytes())
	if buf.Len() > N {
		return buf.Bytes()[buf.Len()-N:]
	}
	return buf.Bytes()
}

// s can be string, hex, or int.
// returns properly formatted 32byte hex value
func Coerce2Hex(s string) string {
	//fmt.Println("coercing to hex:", s)
	// is int?
	i, err := strconv.Atoi(s)
	if err == nil {
		return "0x" + hex.EncodeToString(NumberToBytes(int32(i), i/256+1))
	}
	// is already prefixed hex?
	if len(s) > 1 && s[:2] == "0x" {
		if len(s)%2 == 0 {
			return s
		}
		return "0x0" + s[2:]
	}
	// is unprefixed hex?
	if len(s) > 32 {
		return "0x" + s
	}
	pad := strings.Repeat("\x00", (32-len(s))) + s
	ret := "0x" + hex.EncodeToString([]byte(pad))
	//fmt.Println("result:", ret)
	return ret
}

func IsHex(s string) bool {
	if len(s) < 2 {
		return false
	}
	if s[:2] == "0x" {
		return true
	}
	return false
}

func AddHex(s string) string {
	if len(s) < 2 {
		return "0x" + s
	}

	if s[:2] != "0x" {
		return "0x" + s
	}

	return s
}

func StripHex(s string) string {
	if len(s) > 1 {
		if s[:2] == "0x" {
			s = s[2:]
			if len(s)%2 != 0 {
				s = "0" + s
			}
			return s
		}
	}
	return s
}

func StripZeros(s string) string {
	i := 0
	for ; i < len(s); i++ {
		if s[i] != '0' {
			break
		}
	}
	return s[i:]
}

// hex and ints
//---------------------------------------------------------------------------
// reflection and json

func WriteJson(config interface{}, config_file string) error {
	b, err := json.Marshal(config)
	if err != nil {
		return err
	}
	var out bytes.Buffer
	err = json.Indent(&out, b, "", "\t")
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(config_file, out.Bytes(), 0600)
	return err
}

func ReadJson(config interface{}, config_file string) error {
	b, err := ioutil.ReadFile(config_file)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, config)
	if err != nil {
		fmt.Println("error unmarshalling config from file:", err)
		return err
	}
	return nil
}

func NewInvalidKindErr(kind, k reflect.Kind) error {
	return fmt.Errorf("Invalid kind. Expected %s, received %s", kind, k)
}

func FieldFromTag(v reflect.Value, field string) (string, error) {
	iv := v.Interface()
	st := reflect.TypeOf(iv)
	for i := 0; i < v.NumField(); i++ {
		tag := st.Field(i).Tag.Get("json")
		if tag == field {
			return st.Field(i).Name, nil
		}
	}
	return "", fmt.Errorf("Invalid field name")
}

// Set a field in a struct value
// Field can be field name or json tag name
// Values can be strings that can be cast to int or bool
//  only handles strings, ints, bool
func SetProperty(cv reflect.Value, field string, value interface{}) error {
	f := cv.FieldByName(field)
	if !f.IsValid() {
		name, err := FieldFromTag(cv, field)
		if err != nil {
			return err
		}
		f = cv.FieldByName(name)
	}
	kind := f.Kind()

	k := reflect.ValueOf(value).Kind()
	if k != kind && k != reflect.String {
		return NewInvalidKindErr(kind, k)
	}

	if kind == reflect.String {
		f.SetString(value.(string))
	} else if kind == reflect.Int {
		if k != kind {
			v, err := strconv.Atoi(value.(string))
			if err != nil {
				return err
			}
			f.SetInt(int64(v))
		} else {
			f.SetInt(int64(value.(int)))
		}
	} else if kind == reflect.Bool {
		if k != kind {
			v, err := strconv.ParseBool(value.(string))
			if err != nil {
				return err
			}
			f.SetBool(v)
		} else {
			f.SetBool(value.(bool))
		}
	}
	return nil
}

// reflection and json
//---------------------------------------------------------------------------
// open text editors

func Editor(file string) error {
	editr := os.Getenv("EDITOR")
	if strings.Contains(editr, "/") {
		editr = path.Base(editr)
	}
	switch editr {
	case "", "vim", "vi":
		return vi(file)
	case "emacs":
		return emacs(file)
	}
	return fmt.Errorf("Unknown editor %s", editr)
}

func emacs(file string) error {
	cmd := exec.Command("emacs", file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func vi(file string) error {
	cmd := exec.Command("vim", file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}
<commit_msg>there shall be no blockchains types folder<commit_after>package common

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path"
	"reflect"
	"runtime"
	"strconv"
	"strings"
)

var (
	// Convenience Directories
	GoPath          = os.Getenv("GOPATH")
	ErisLtd         = path.Join(GoPath, "src", "github.com", "eris-ltd")
	usr, _          = user.Current() // error?!
	ErisRoot        = ResolveErisRoot()

	// Major Directories
	ActionsPath        = path.Join(ErisRoot, "actions")
	BlockchainsPath    = path.Join(ErisRoot, "blockchains")
	DataContainersPath = path.Join(ErisRoot, "data")
	DappsPath          = path.Join(ErisRoot, "dapps")
	FilesPath          = path.Join(ErisRoot, "files")
	KeysPath           = path.Join(ErisRoot, "keys")
	LanguagesPath      = path.Join(ErisRoot, "languages")
	ServicesPath       = path.Join(ErisRoot, "services")
	ScratchPath        = path.Join(ErisRoot, "scratch")

	// Scratch Directories (globally coordinated)
	EpmScratchPath  = path.Join(ScratchPath, "epm")
	LllcScratchPath = path.Join(ScratchPath, "lllc")
	SolcScratchPath = path.Join(ScratchPath, "sol")
	SerpScratchPath = path.Join(ScratchPath, "ser")

	// Blockchains stuff
	HEAD            = path.Join(BlockchainsPath, "HEAD")
	Refs            = path.Join(BlockchainsPath, "refs")
)

var MajorDirs = []string{
	ErisRoot, ActionsPath, BlockchainsPath, DataContainersPath, DappsPath, FilesPath, KeysPath, LanguagesPath, ServicesPath, ScratchPath, EpmScratchPath, LllcScratchPath, SolcScratchPath, SerpScratchPath,
}

//---------------------------------------------
// user and process

func Usr() string {
	u, _ := user.Current()
	return u.HomeDir
}

func Exit(err error) {
	status := 0
	if err != nil {
		fmt.Println(err)
		status = 1
	}
	os.Exit(status)
}

func IfExit(err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

// user and process
//---------------------------------------------------------------------------
// filesystem

func AbsolutePath(Datadir string, filename string) string {
	if path.IsAbs(filename) {
		return filename
	}
	return path.Join(Datadir, filename)
}

func InitDataDir(Datadir string) error {
	_, err := os.Stat(Datadir)
	if err != nil {
		if os.IsNotExist(err) {
			err := os.MkdirAll(Datadir, 0777)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func ResolveErisRoot() string {
	var eris string
	if os.Getenv("ERIS") != "" {
		eris = os.Getenv("ERIS")
	} else {
		if runtime.GOOS == "windows" {
			home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
			if home == "" {
				home = os.Getenv("USERPROFILE")
			}
			eris = path.Join(home, ".eris")
		} else {
			eris = path.Join(Usr(), ".eris")
		}
	}
	return eris
}

// Create the default eris tree
func InitErisDir() (err error) {
	for _, d := range MajorDirs {
		err := InitDataDir(d)
		if err != nil {
			return err
		}
	}
	if _, err = os.Stat(HEAD); err != nil {
		_, err = os.Create(HEAD)
	}
	return
}

func ClearDir(dir string) error {
	fs, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	for _, f := range fs {
		n := f.Name()
		if f.IsDir() {
			if err := os.RemoveAll(path.Join(dir, f.Name())); err != nil {
				return err
			}
		} else {
			if err := os.Remove(path.Join(dir, n)); err != nil {
				return err
			}
		}
	}
	return nil
}

func Copy(src, dst string) error {
	f, err := os.Stat(src)
	if err != nil {
		return err
	}
	if f.IsDir() {
		if _, err := os.Stat(dst); err == nil {
			return fmt.Errorf("destination already exists")
		}
		return copyDir(src, dst)
	}
	return copyFile(src, dst)
}

// assumes we've done our checking
func copyDir(src, dst string) error {
	fi, _ := os.Stat(src)
	if err := os.MkdirAll(dst, fi.Mode()); err != nil {
		return err
	}
	fs, err := ioutil.ReadDir(src)
	if err != nil {
		return err
	}

	for _, f := range fs {
		s := path.Join(src, f.Name())
		d := path.Join(dst, f.Name())
		if f.IsDir() {
			if err := copyDir(s, d); err != nil {
				return err
			}
		} else {
			if err := copyFile(s, d); err != nil {
				return err
			}
		}
	}
	return nil
}

// common golang, really?
func copyFile(src, dst string) error {
	r, err := os.Open(src)
	if err != nil {
		return err
	}
	defer r.Close()

	w, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer w.Close()

	_, err = io.Copy(w, r)
	if err != nil {
		return err
	}
	return nil
}

// filesystem
//-------------------------------------------------------
// hex and ints

// keeps N bytes of the conversion
func NumberToBytes(num interface{}, N int) []byte {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, num)
	if err != nil {
		// TODO: get this guy a return error?
	}
	//fmt.Println("btyes!", buf.Bytes())
	if buf.Len() > N {
		return buf.Bytes()[buf.Len()-N:]
	}
	return buf.Bytes()
}

// s can be string, hex, or int.
// returns properly formatted 32byte hex value
func Coerce2Hex(s string) string {
	//fmt.Println("coercing to hex:", s)
	// is int?
	i, err := strconv.Atoi(s)
	if err == nil {
		return "0x" + hex.EncodeToString(NumberToBytes(int32(i), i/256+1))
	}
	// is already prefixed hex?
	if len(s) > 1 && s[:2] == "0x" {
		if len(s)%2 == 0 {
			return s
		}
		return "0x0" + s[2:]
	}
	// is unprefixed hex?
	if len(s) > 32 {
		return "0x" + s
	}
	pad := strings.Repeat("\x00", (32-len(s))) + s
	ret := "0x" + hex.EncodeToString([]byte(pad))
	//fmt.Println("result:", ret)
	return ret
}

func IsHex(s string) bool {
	if len(s) < 2 {
		return false
	}
	if s[:2] == "0x" {
		return true
	}
	return false
}

func AddHex(s string) string {
	if len(s) < 2 {
		return "0x" + s
	}

	if s[:2] != "0x" {
		return "0x" + s
	}

	return s
}

func StripHex(s string) string {
	if len(s) > 1 {
		if s[:2] == "0x" {
			s = s[2:]
			if len(s)%2 != 0 {
				s = "0" + s
			}
			return s
		}
	}
	return s
}

func StripZeros(s string) string {
	i := 0
	for ; i < len(s); i++ {
		if s[i] != '0' {
			break
		}
	}
	return s[i:]
}

// hex and ints
//---------------------------------------------------------------------------
// reflection and json

func WriteJson(config interface{}, config_file string) error {
	b, err := json.Marshal(config)
	if err != nil {
		return err
	}
	var out bytes.Buffer
	err = json.Indent(&out, b, "", "\t")
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(config_file, out.Bytes(), 0600)
	return err
}

func ReadJson(config interface{}, config_file string) error {
	b, err := ioutil.ReadFile(config_file)
	if err != nil {
		return err
	}
	err = json.Unmarshal(b, config)
	if err != nil {
		fmt.Println("error unmarshalling config from file:", err)
		return err
	}
	return nil
}

func NewInvalidKindErr(kind, k reflect.Kind) error {
	return fmt.Errorf("Invalid kind. Expected %s, received %s", kind, k)
}

func FieldFromTag(v reflect.Value, field string) (string, error) {
	iv := v.Interface()
	st := reflect.TypeOf(iv)
	for i := 0; i < v.NumField(); i++ {
		tag := st.Field(i).Tag.Get("json")
		if tag == field {
			return st.Field(i).Name, nil
		}
	}
	return "", fmt.Errorf("Invalid field name")
}

// Set a field in a struct value
// Field can be field name or json tag name
// Values can be strings that can be cast to int or bool
//  only handles strings, ints, bool
func SetProperty(cv reflect.Value, field string, value interface{}) error {
	f := cv.FieldByName(field)
	if !f.IsValid() {
		name, err := FieldFromTag(cv, field)
		if err != nil {
			return err
		}
		f = cv.FieldByName(name)
	}
	kind := f.Kind()

	k := reflect.ValueOf(value).Kind()
	if k != kind && k != reflect.String {
		return NewInvalidKindErr(kind, k)
	}

	if kind == reflect.String {
		f.SetString(value.(string))
	} else if kind == reflect.Int {
		if k != kind {
			v, err := strconv.Atoi(value.(string))
			if err != nil {
				return err
			}
			f.SetInt(int64(v))
		} else {
			f.SetInt(int64(value.(int)))
		}
	} else if kind == reflect.Bool {
		if k != kind {
			v, err := strconv.ParseBool(value.(string))
			if err != nil {
				return err
			}
			f.SetBool(v)
		} else {
			f.SetBool(value.(bool))
		}
	}
	return nil
}

// reflection and json
//---------------------------------------------------------------------------
// open text editors

func Editor(file string) error {
	editr := os.Getenv("EDITOR")
	if strings.Contains(editr, "/") {
		editr = path.Base(editr)
	}
	switch editr {
	case "", "vim", "vi":
		return vi(file)
	case "emacs":
		return emacs(file)
	}
	return fmt.Errorf("Unknown editor %s", editr)
}

func emacs(file string) error {
	cmd := exec.Command("emacs", file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func vi(file string) error {
	cmd := exec.Command("vim", file)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}
<|endoftext|>


<commit_before>package jws

import (
	"errors"
	"testing"
	"time"

	"github.com/SermoDigital/jose/crypto"
)

var claims = Claims{
	"name": "Eric",
	"scopes": []string{
		"user.account.info",
		"user.account.update",
		"user.account.delete",
	},
	"admin": true,
	"data": struct {
		Foo, Bar int
	}{
		Foo: 12,
		Bar: 50,
	},
}

func TestBasicJWT(t *testing.T) {
	j := NewJWT(claims, crypto.SigningMethodRS512)
	b, err := j.Serialize(rsaPriv)
	if err != nil {
		t.Error(err)
	}

	w, err := ParseJWT(b)
	if err != nil {
		t.Error(err)
	}

	if w.Claims().Get("name") != "Eric" &&
		w.Claims().Get("admin") != true &&
		w.Claims().Get("scopes").([]string)[0] != "user.account.info" {
		Error(t, claims, w.Claims())
	}

	if err := w.Validate(rsaPub, crypto.SigningMethodRS512); err != nil {
		t.Error(err)
	}
}

func TestJWTValidator(t *testing.T) {
	j := NewJWT(claims, crypto.SigningMethodRS512)
	j.Claims().SetIssuer("example.com")

	b, err := j.Serialize(rsaPriv)
	if err != nil {
		t.Error(err)
	}

	w, err := ParseJWT(b)
	if err != nil {
		t.Error(err)
	}

	d := float64(time.Now().Add(1 * time.Hour).Unix())
	fn := func(c Claims) error {
		if c.Get("name") != "Eric" &&
			c.Get("admin") != true &&
			c.Get("scopes").([]string)[0] != "user.account.info" {
			return errors.New("invalid")
		}
		return nil
	}
	v := NewValidator(Claims{"iss": "example.com"}, d, d, fn)
	if err := w.Validate(rsaPub, crypto.SigningMethodRS512, v); err != nil {
		t.Error(err)
	}
}
<commit_msg>Add more precise validation function test<commit_after>package jws

import (
	"errors"
	"testing"
	"time"

	"github.com/SermoDigital/jose/crypto"
)

var claims = Claims{
	"name": "Eric",
	"scopes": []string{
		"user.account.info",
		"user.account.update",
		"user.account.delete",
	},
	"admin": true,
	"data": struct {
		Foo, Bar int
	}{
		Foo: 12,
		Bar: 50,
	},
}

func TestBasicJWT(t *testing.T) {
	j := NewJWT(claims, crypto.SigningMethodRS512)
	b, err := j.Serialize(rsaPriv)
	if err != nil {
		t.Error(err)
	}

	w, err := ParseJWT(b)
	if err != nil {
		t.Error(err)
	}

	if w.Claims().Get("name") != "Eric" &&
		w.Claims().Get("admin") != true &&
		w.Claims().Get("scopes").([]string)[0] != "user.account.info" {
		Error(t, claims, w.Claims())
	}

	if err := w.Validate(rsaPub, crypto.SigningMethodRS512); err != nil {
		t.Error(err)
	}
}

func TestJWTValidator(t *testing.T) {
	j := NewJWT(claims, crypto.SigningMethodRS512)
	j.Claims().SetIssuer("example.com")

	b, err := j.Serialize(rsaPriv)
	if err != nil {
		t.Error(err)
	}

	w, err := ParseJWT(b)
	if err != nil {
		t.Error(err)
	}

	d := float64(time.Now().Add(1 * time.Hour).Unix())
	fn := func(c Claims) error {

		scopes, ok := c.Get("scopes").([]interface{})

		if !ok {
			return errors.New("Unexpected scopes type. Expected string")
		}

		if c.Get("name") != "Eric" &&
			c.Get("admin") != true &&
			scopes[0] != "user.account.info" {
			return errors.New("invalid")
		}
		return nil
	}
	v := NewValidator(Claims{"iss": "example.com"}, d, d, fn)
	if err := w.Validate(rsaPub, crypto.SigningMethodRS512, v); err != nil {
		t.Error(err)
	}
}
<|endoftext|>


<commit_before>package jwtauth_test

import (
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/goware/jwtauth"
	"github.com/pressly/chi"
	"golang.org/x/net/context"
)

var (
	TokenAuth   *jwtauth.JwtAuth
	TokenSecret = []byte("secretpass")
)

func init() {
	TokenAuth = jwtauth.New("HS256", []byte("secretpass"), nil)
}

//
// Tests
//

func TestSimple(t *testing.T) {
	r := chi.NewRouter()

	r.Use(TokenAuth.Verifier)
	r.Use(jwtauth.Authenticator)

	r.Get("/", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("welcome"))
	})

	ts := httptest.NewServer(r)
	defer ts.Close()

	// sending unauthorized requests
	if resp := testRequest(t, ts, "GET", "/", nil, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}

	h := http.Header{}
	h.Set("Authorization", "BEARER "+newJwtToken([]byte("wrong"), map[string]interface{}{}))
	if resp := testRequest(t, ts, "GET", "/", h, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}
	h.Set("Authorization", "BEARER asdf")
	if resp := testRequest(t, ts, "GET", "/", h, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}

	// sending authorized requests
	if resp := testRequest(t, ts, "GET", "/", newAuthHeader(), nil); resp != "welcome" {
		t.Fatalf(resp)
	}
}

func TestMore(t *testing.T) {
	r := chi.NewRouter()

	// Protected routes
	r.Group(func(r chi.Router) {
		r.Use(TokenAuth.Verifier)

		authenticator := func(next chi.Handler) chi.Handler {
			return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
				if jwtErr, ok := ctx.Value("jwt.err").(error); ok {
					switch jwtErr {
					default:
						http.Error(w, http.StatusText(401), 401)
						return
					case jwtauth.ErrExpired:
						http.Error(w, "expired", 401)
						return
					case jwtauth.ErrUnauthorized:
						http.Error(w, http.StatusText(401), 401)
						return
					case nil:
						// no error
					}
				}

				jwtToken, ok := ctx.Value("jwt").(*jwt.Token)
				if !ok || jwtToken == nil || !jwtToken.Valid {
					http.Error(w, http.StatusText(401), 401)
					return
				}

				// Token is authenticated, pass it through
				next.ServeHTTPC(ctx, w, r)
			})
		}
		r.Use(authenticator)

		r.Get("/admin", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			w.Write([]byte("protected"))
		})
	})

	// Public routes
	r.Group(func(r chi.Router) {
		r.Get("/", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			w.Write([]byte("welcome"))
		})
	})

	ts := httptest.NewServer(r)
	defer ts.Close()

	// sending unauthorized requests
	if resp := testRequest(t, ts, "GET", "/admin", nil, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}

	h := http.Header{}
	h.Set("Authorization", "BEARER "+newJwtToken([]byte("wrong"), map[string]interface{}{}))
	if resp := testRequest(t, ts, "GET", "/admin", h, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}
	h.Set("Authorization", "BEARER asdf")
	if resp := testRequest(t, ts, "GET", "/admin", h, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}

	h = newAuthHeader((jwtauth.Claims{}).Set("exp", jwtauth.EpochNow()-1000))
	if resp := testRequest(t, ts, "GET", "/admin", h, nil); resp != "expired\n" {
		t.Fatalf(resp)
	}

	// sending authorized requests
	if resp := testRequest(t, ts, "GET", "/", nil, nil); resp != "welcome" {
		t.Fatalf(resp)
	}

	h = newAuthHeader((jwtauth.Claims{}).SetExpiryIn(5 * time.Minute))
	if resp := testRequest(t, ts, "GET", "/admin", h, nil); resp != "protected" {
		t.Fatalf(resp)
	}
}

//
// Test helper functions
//

func testRequest(t *testing.T, ts *httptest.Server, method, path string, header http.Header, body io.Reader) string {
	req, err := http.NewRequest(method, ts.URL+path, body)
	if err != nil {
		t.Fatal(err)
		return ""
	}

	if header != nil {
		for k, v := range header {
			req.Header.Set(k, v[0])
		}
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Fatal(err)
		return ""
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatal(err)
		return ""
	}
	defer resp.Body.Close()

	return string(respBody)
}

func newJwtToken(secret []byte, claims ...jwtauth.Claims) string {
	token := jwt.New(jwt.GetSigningMethod("HS256"))
	if len(claims) > 0 {
		for k, v := range claims[0] {
			token.Claims[k] = v
		}
	}
	tokenStr, err := token.SignedString(secret)
	if err != nil {
		log.Fatal(err)
	}
	return tokenStr
}

func newAuthHeader(claims ...jwtauth.Claims) http.Header {
	h := http.Header{}
	h.Set("Authorization", "BEARER "+newJwtToken(TokenSecret, claims...))
	return h
}
<commit_msg>Use test secret<commit_after>package jwtauth_test

import (
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/goware/jwtauth"
	"github.com/pressly/chi"
	"golang.org/x/net/context"
)

var (
	TokenAuth   *jwtauth.JwtAuth
	TokenSecret = []byte("secretpass")
)

func init() {
	TokenAuth = jwtauth.New("HS256", TokenSecret, nil)
}

//
// Tests
//

func TestSimple(t *testing.T) {
	r := chi.NewRouter()

	r.Use(TokenAuth.Verifier)
	r.Use(jwtauth.Authenticator)

	r.Get("/", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("welcome"))
	})

	ts := httptest.NewServer(r)
	defer ts.Close()

	// sending unauthorized requests
	if resp := testRequest(t, ts, "GET", "/", nil, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}

	h := http.Header{}
	h.Set("Authorization", "BEARER "+newJwtToken([]byte("wrong"), map[string]interface{}{}))
	if resp := testRequest(t, ts, "GET", "/", h, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}
	h.Set("Authorization", "BEARER asdf")
	if resp := testRequest(t, ts, "GET", "/", h, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}

	// sending authorized requests
	if resp := testRequest(t, ts, "GET", "/", newAuthHeader(), nil); resp != "welcome" {
		t.Fatalf(resp)
	}
}

func TestMore(t *testing.T) {
	r := chi.NewRouter()

	// Protected routes
	r.Group(func(r chi.Router) {
		r.Use(TokenAuth.Verifier)

		authenticator := func(next chi.Handler) chi.Handler {
			return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
				if jwtErr, ok := ctx.Value("jwt.err").(error); ok {
					switch jwtErr {
					default:
						http.Error(w, http.StatusText(401), 401)
						return
					case jwtauth.ErrExpired:
						http.Error(w, "expired", 401)
						return
					case jwtauth.ErrUnauthorized:
						http.Error(w, http.StatusText(401), 401)
						return
					case nil:
						// no error
					}
				}

				jwtToken, ok := ctx.Value("jwt").(*jwt.Token)
				if !ok || jwtToken == nil || !jwtToken.Valid {
					http.Error(w, http.StatusText(401), 401)
					return
				}

				// Token is authenticated, pass it through
				next.ServeHTTPC(ctx, w, r)
			})
		}
		r.Use(authenticator)

		r.Get("/admin", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			w.Write([]byte("protected"))
		})
	})

	// Public routes
	r.Group(func(r chi.Router) {
		r.Get("/", func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			w.Write([]byte("welcome"))
		})
	})

	ts := httptest.NewServer(r)
	defer ts.Close()

	// sending unauthorized requests
	if resp := testRequest(t, ts, "GET", "/admin", nil, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}

	h := http.Header{}
	h.Set("Authorization", "BEARER "+newJwtToken([]byte("wrong"), map[string]interface{}{}))
	if resp := testRequest(t, ts, "GET", "/admin", h, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}
	h.Set("Authorization", "BEARER asdf")
	if resp := testRequest(t, ts, "GET", "/admin", h, nil); resp != "Unauthorized\n" {
		t.Fatalf(resp)
	}

	h = newAuthHeader((jwtauth.Claims{}).Set("exp", jwtauth.EpochNow()-1000))
	if resp := testRequest(t, ts, "GET", "/admin", h, nil); resp != "expired\n" {
		t.Fatalf(resp)
	}

	// sending authorized requests
	if resp := testRequest(t, ts, "GET", "/", nil, nil); resp != "welcome" {
		t.Fatalf(resp)
	}

	h = newAuthHeader((jwtauth.Claims{}).SetExpiryIn(5 * time.Minute))
	if resp := testRequest(t, ts, "GET", "/admin", h, nil); resp != "protected" {
		t.Fatalf(resp)
	}
}

//
// Test helper functions
//

func testRequest(t *testing.T, ts *httptest.Server, method, path string, header http.Header, body io.Reader) string {
	req, err := http.NewRequest(method, ts.URL+path, body)
	if err != nil {
		t.Fatal(err)
		return ""
	}

	if header != nil {
		for k, v := range header {
			req.Header.Set(k, v[0])
		}
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Fatal(err)
		return ""
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatal(err)
		return ""
	}
	defer resp.Body.Close()

	return string(respBody)
}

func newJwtToken(secret []byte, claims ...jwtauth.Claims) string {
	token := jwt.New(jwt.GetSigningMethod("HS256"))
	if len(claims) > 0 {
		for k, v := range claims[0] {
			token.Claims[k] = v
		}
	}
	tokenStr, err := token.SignedString(secret)
	if err != nil {
		log.Fatal(err)
	}
	return tokenStr
}

func newAuthHeader(claims ...jwtauth.Claims) http.Header {
	h := http.Header{}
	h.Set("Authorization", "BEARER "+newJwtToken(TokenSecret, claims...))
	return h
}
<|endoftext|>


<commit_before>package sendgrid

import (
	"context"
	"errors"
	"net/http"
	"strconv"
	"time"

	"github.com/sendgrid/rest"
	"github.com/sendgrid/sendgrid-go/helpers/mail"
)

// Version is this client library's current version
const (
	Version        = "3.11.1"
	rateLimitRetry = 5
	rateLimitSleep = 1100
)

type options struct {
	Auth     string
	Endpoint string
	Host     string
	Subuser  string
}

// Client is the Twilio SendGrid Go client
type Client struct {
	rest.Request
}

func (o *options) baseURL() string {
	return o.Host + o.Endpoint
}

// requestNew create Request
// @return [Request] a default request object
func requestNew(options options) rest.Request {
	requestHeaders := map[string]string{
		"Authorization": options.Auth,
		"User-Agent":    "sendgrid/" + Version + ";go",
		"Accept":        "application/json",
	}

	if len(options.Subuser) != 0 {
		requestHeaders["On-Behalf-Of"] = options.Subuser
	}

	return rest.Request{
		BaseURL: options.baseURL(),
		Headers: requestHeaders,
	}
}

// Send sends an email through Twilio SendGrid
func (cl *Client) Send(email *mail.SGMailV3) (*rest.Response, error) {
	return cl.SendWithContext(context.Background(), email)
}

// SendWithContext sends an email through Twilio SendGrid with context.Context.
func (cl *Client) SendWithContext(ctx context.Context, email *mail.SGMailV3) (*rest.Response, error) {
	cl.Body = mail.GetRequestBody(email)
	return MakeRequestWithContext(ctx, cl.Request)
}

// DefaultClient is used if no custom HTTP client is defined
var DefaultClient = rest.DefaultClient

// API sets up the request to the Twilio SendGrid API, this is main interface.
// Please use the MakeRequest or MakeRequestAsync functions instead.
// (deprecated)
func API(request rest.Request) (*rest.Response, error) {
	return MakeRequest(request)
}

// MakeRequest attempts a Twilio SendGrid request synchronously.
func MakeRequest(request rest.Request) (*rest.Response, error) {
	return MakeRequestWithContext(context.Background(), request)
}

// MakeRequestWithContext attempts a Twilio SendGrid request synchronously with context.Context.
func MakeRequestWithContext(ctx context.Context, request rest.Request) (*rest.Response, error) {
	return DefaultClient.SendWithContext(ctx, request)
}

// MakeRequestRetry a synchronous request, but retry in the event of a rate
// limited response.
func MakeRequestRetry(request rest.Request) (*rest.Response, error) {
	return MakeRequestRetryWithContext(context.Background(), request)
}

// MakeRequestRetryWithContext a synchronous request with context.Context, but retry in the event of a rate
// limited response.
func MakeRequestRetryWithContext(ctx context.Context, request rest.Request) (*rest.Response, error) {
	retry := 0
	var response *rest.Response
	var err error

	for {
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		default:
		}
		response, err = MakeRequestWithContext(ctx, request)
		if err != nil {
			return nil, err
		}

		if response.StatusCode != http.StatusTooManyRequests {
			return response, nil
		}

		if retry > rateLimitRetry {
			return nil, errors.New("rate limit retry exceeded")
		}
		retry++

		resetTime := time.Now().Add(rateLimitSleep * time.Millisecond)

		reset, ok := response.Headers["X-RateLimit-Reset"]
		if ok && len(reset) > 0 {
			t, err := strconv.Atoi(reset[0])
			if err == nil {
				resetTime = time.Unix(int64(t), 0)
			}
		}
		time.Sleep(resetTime.Sub(time.Now()))
	}
}

// MakeRequestAsync attempts a request asynchronously in a new go
// routine. This function returns two channels: responses
// and errors. This function will retry in the case of a
// rate limit.
func MakeRequestAsync(request rest.Request) (chan *rest.Response, chan error) {
	return MakeRequestAsyncWithContext(context.Background(), request)
}

// MakeRequestAsyncWithContext attempts a request asynchronously in a new go
// routine with context.Context. This function returns two channels: responses
// and errors. This function will retry in the case of a
// rate limit.
func MakeRequestAsyncWithContext(ctx context.Context, request rest.Request) (chan *rest.Response, chan error) {
	r := make(chan *rest.Response)
	e := make(chan error)

	go func() {
		response, err := MakeRequestRetryWithContext(ctx, request)
		if err != nil {
			e <- err
		}
		if response != nil {
			r <- response
		}
	}()

	return r, e
}
<commit_msg>Release v3.12.0<commit_after>package sendgrid

import (
	"context"
	"errors"
	"net/http"
	"strconv"
	"time"

	"github.com/sendgrid/rest"
	"github.com/sendgrid/sendgrid-go/helpers/mail"
)

// Version is this client library's current version
const (
	Version        = "3.12.0"
	rateLimitRetry = 5
	rateLimitSleep = 1100
)

type options struct {
	Auth     string
	Endpoint string
	Host     string
	Subuser  string
}

// Client is the Twilio SendGrid Go client
type Client struct {
	rest.Request
}

func (o *options) baseURL() string {
	return o.Host + o.Endpoint
}

// requestNew create Request
// @return [Request] a default request object
func requestNew(options options) rest.Request {
	requestHeaders := map[string]string{
		"Authorization": options.Auth,
		"User-Agent":    "sendgrid/" + Version + ";go",
		"Accept":        "application/json",
	}

	if len(options.Subuser) != 0 {
		requestHeaders["On-Behalf-Of"] = options.Subuser
	}

	return rest.Request{
		BaseURL: options.baseURL(),
		Headers: requestHeaders,
	}
}

// Send sends an email through Twilio SendGrid
func (cl *Client) Send(email *mail.SGMailV3) (*rest.Response, error) {
	return cl.SendWithContext(context.Background(), email)
}

// SendWithContext sends an email through Twilio SendGrid with context.Context.
func (cl *Client) SendWithContext(ctx context.Context, email *mail.SGMailV3) (*rest.Response, error) {
	cl.Body = mail.GetRequestBody(email)
	return MakeRequestWithContext(ctx, cl.Request)
}

// DefaultClient is used if no custom HTTP client is defined
var DefaultClient = rest.DefaultClient

// API sets up the request to the Twilio SendGrid API, this is main interface.
// Please use the MakeRequest or MakeRequestAsync functions instead.
// (deprecated)
func API(request rest.Request) (*rest.Response, error) {
	return MakeRequest(request)
}

// MakeRequest attempts a Twilio SendGrid request synchronously.
func MakeRequest(request rest.Request) (*rest.Response, error) {
	return MakeRequestWithContext(context.Background(), request)
}

// MakeRequestWithContext attempts a Twilio SendGrid request synchronously with context.Context.
func MakeRequestWithContext(ctx context.Context, request rest.Request) (*rest.Response, error) {
	return DefaultClient.SendWithContext(ctx, request)
}

// MakeRequestRetry a synchronous request, but retry in the event of a rate
// limited response.
func MakeRequestRetry(request rest.Request) (*rest.Response, error) {
	return MakeRequestRetryWithContext(context.Background(), request)
}

// MakeRequestRetryWithContext a synchronous request with context.Context, but retry in the event of a rate
// limited response.
func MakeRequestRetryWithContext(ctx context.Context, request rest.Request) (*rest.Response, error) {
	retry := 0
	var response *rest.Response
	var err error

	for {
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		default:
		}
		response, err = MakeRequestWithContext(ctx, request)
		if err != nil {
			return nil, err
		}

		if response.StatusCode != http.StatusTooManyRequests {
			return response, nil
		}

		if retry > rateLimitRetry {
			return nil, errors.New("rate limit retry exceeded")
		}
		retry++

		resetTime := time.Now().Add(rateLimitSleep * time.Millisecond)

		reset, ok := response.Headers["X-RateLimit-Reset"]
		if ok && len(reset) > 0 {
			t, err := strconv.Atoi(reset[0])
			if err == nil {
				resetTime = time.Unix(int64(t), 0)
			}
		}
		time.Sleep(resetTime.Sub(time.Now()))
	}
}

// MakeRequestAsync attempts a request asynchronously in a new go
// routine. This function returns two channels: responses
// and errors. This function will retry in the case of a
// rate limit.
func MakeRequestAsync(request rest.Request) (chan *rest.Response, chan error) {
	return MakeRequestAsyncWithContext(context.Background(), request)
}

// MakeRequestAsyncWithContext attempts a request asynchronously in a new go
// routine with context.Context. This function returns two channels: responses
// and errors. This function will retry in the case of a
// rate limit.
func MakeRequestAsyncWithContext(ctx context.Context, request rest.Request) (chan *rest.Response, chan error) {
	r := make(chan *rest.Response)
	e := make(chan error)

	go func() {
		response, err := MakeRequestRetryWithContext(ctx, request)
		if err != nil {
			e <- err
		}
		if response != nil {
			r <- response
		}
	}()

	return r, e
}
<|endoftext|>


<commit_before>/*
Copyright 2013 The Perkeep 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.
*/

// This file adds the "test" subcommand to devcam, to run the full test suite.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"perkeep.org/internal/osutil"
	"perkeep.org/pkg/cmdmain"
)

type testCmd struct {
	// start of flag vars
	verbose   bool
	precommit bool
	short     bool
	run       string
	sqlite    bool
}

func init() {
	cmdmain.RegisterMode("test", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := new(testCmd)
		flags.BoolVar(&cmd.short, "short", false, "Use '-short' with go test.")
		flags.BoolVar(&cmd.precommit, "precommit", true, "Run the pre-commit githook as part of tests.")
		flags.BoolVar(&cmd.verbose, "v", false, "Use '-v' (for verbose) with go test.")
		flags.StringVar(&cmd.run, "run", "", "Use '-run' with go test.")
		flags.BoolVar(&cmd.sqlite, "sqlite", false, "Run tests with SQLite built-in where relevant.")
		return cmd
	})
}

func (c *testCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: devcam test [test_opts] [targets]\n")
}

func (c *testCmd) Describe() string {
	return "run the full test suite, or the tests in the specified target packages."
}

func (c *testCmd) RunCommand(args []string) error {
	if c.precommit {
		if err := c.runPrecommitHook(); err != nil {
			return err
		}
	}
	if err := c.buildSelf(); err != nil {
		return err
	}
	if err := c.runTests(args); err != nil {
		return err
	}
	println("PASS")
	return nil
}

func (c *testCmd) env() *Env {
	env := NewCopyEnv()
	env.NoGo()
	cmd := exec.Command("go", "env", "GOPATH")
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		panic("Cannot find GOPATH with 'go env GOPATH'")
	}
	gopath := strings.TrimSpace(string(out))
	if gopath == "" {
		panic("devcam test needs GOPATH to be set")
	}
	env.Set("GOPATH", gopath)
	return env
}

func (c *testCmd) buildSelf() error {
	args := []string{
		"install",
		filepath.FromSlash("./dev/devcam"),
	}
	cmd := exec.Command("go", args...)
	binDir, err := filepath.Abs("bin")
	if err != nil {
		return fmt.Errorf("Error setting GOBIN: %v", err)
	}
	env := c.env()
	env.Set("GOBIN", binDir)
	cmd.Env = env.Flat()
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("Error building devcam: %v", err)
	}
	return nil
}

func (c *testCmd) runTests(args []string) error {
	targs := []string{"test"}
	if c.sqlite {
		targs = append(targs, "--tags=with_sqlite fake_android")
	} else {
		targs = append(targs, "--tags=fake_android")
	}
	if c.short {
		targs = append(targs, "-short")
	}
	if c.verbose {
		targs = append(targs, "-v")
	}
	if c.run != "" {
		targs = append(targs, "-run="+c.run)
	}
	if len(args) > 0 {
		targs = append(targs, args...)
	} else {
		targs = append(targs, []string{
			"./pkg/...",
			"./server/perkeepd",
			"./cmd/...",
			"./misc/docker/...",
			"./website",
		}...)
	}
	env := c.env()
	env.Set("SKIP_DEP_TESTS", "1")
	return runExec("go", targs, env)
}

func (c *testCmd) runPrecommitHook() error {
	cmdBin, err := osutil.LookPathGopath("devcam")
	if err != nil {
		return err
	}
	out, err := exec.Command(cmdBin, "hook", "pre-commit", "test").CombinedOutput()
	if err != nil {
		fmt.Println(string(out))
	}
	return err

}
<commit_msg>dev/devcam: fix website go code location<commit_after>/*
Copyright 2013 The Perkeep 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.
*/

// This file adds the "test" subcommand to devcam, to run the full test suite.

package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"perkeep.org/internal/osutil"
	"perkeep.org/pkg/cmdmain"
)

type testCmd struct {
	// start of flag vars
	verbose   bool
	precommit bool
	short     bool
	run       string
	sqlite    bool
}

func init() {
	cmdmain.RegisterMode("test", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := new(testCmd)
		flags.BoolVar(&cmd.short, "short", false, "Use '-short' with go test.")
		flags.BoolVar(&cmd.precommit, "precommit", true, "Run the pre-commit githook as part of tests.")
		flags.BoolVar(&cmd.verbose, "v", false, "Use '-v' (for verbose) with go test.")
		flags.StringVar(&cmd.run, "run", "", "Use '-run' with go test.")
		flags.BoolVar(&cmd.sqlite, "sqlite", false, "Run tests with SQLite built-in where relevant.")
		return cmd
	})
}

func (c *testCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: devcam test [test_opts] [targets]\n")
}

func (c *testCmd) Describe() string {
	return "run the full test suite, or the tests in the specified target packages."
}

func (c *testCmd) RunCommand(args []string) error {
	if c.precommit {
		if err := c.runPrecommitHook(); err != nil {
			return err
		}
	}
	if err := c.buildSelf(); err != nil {
		return err
	}
	if err := c.runTests(args); err != nil {
		return err
	}
	println("PASS")
	return nil
}

func (c *testCmd) env() *Env {
	env := NewCopyEnv()
	env.NoGo()
	cmd := exec.Command("go", "env", "GOPATH")
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		panic("Cannot find GOPATH with 'go env GOPATH'")
	}
	gopath := strings.TrimSpace(string(out))
	if gopath == "" {
		panic("devcam test needs GOPATH to be set")
	}
	env.Set("GOPATH", gopath)
	return env
}

func (c *testCmd) buildSelf() error {
	args := []string{
		"install",
		filepath.FromSlash("./dev/devcam"),
	}
	cmd := exec.Command("go", args...)
	env := c.env()
	cmd.Env = env.Flat()
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("Error building devcam: %v", err)
	}
	return nil
}

func (c *testCmd) runTests(args []string) error {
	targs := []string{"test"}
	if c.sqlite {
		targs = append(targs, "--tags=with_sqlite fake_android")
	} else {
		targs = append(targs, "--tags=fake_android")
	}
	if c.short {
		targs = append(targs, "-short")
	}
	if c.verbose {
		targs = append(targs, "-v")
	}
	if c.run != "" {
		targs = append(targs, "-run="+c.run)
	}
	if len(args) > 0 {
		targs = append(targs, args...)
	} else {
		targs = append(targs, []string{
			"./pkg/...",
			"./server/perkeepd",
			"./cmd/...",
			"./misc/docker/...",
			"./website/pk-web",
		}...)
	}
	env := c.env()
	env.Set("SKIP_DEP_TESTS", "1")
	return runExec("go", targs, env)
}

func (c *testCmd) runPrecommitHook() error {
	cmdBin, err := osutil.LookPathGopath("devcam")
	if err != nil {
		return err
	}
	out, err := exec.Command(cmdBin, "hook", "pre-commit", "test").CombinedOutput()
	if err != nil {
		fmt.Println(string(out))
	}
	return err

}
<|endoftext|>


<commit_before>package task

import "time"
import "github.com/nttdots/go-dots/libcoap"
import log "github.com/sirupsen/logrus"

type ResponseHandler func(*MessageTask, *libcoap.Pdu, *Env)
type TimeoutHandler  func(*MessageTask, *Env)

type MessageTask struct {
    TaskBase

    message  *libcoap.Pdu
    response chan *libcoap.Pdu

    interval time.Duration
    retry    int
    timeout  time.Duration

    isStop bool
    isHeartBeat bool
    responseHandler ResponseHandler
    timeoutHandler  TimeoutHandler
}

type TimeoutEvent struct { EventBase }
type MessageEvent struct { EventBase }

func NewMessageTask(message *libcoap.Pdu,
                    interval time.Duration,
                    retry int,
                    timeout time.Duration,
                    isStop bool,
                    isHeartBeat bool,
                    responseHandler ResponseHandler,
                    timeoutHandler TimeoutHandler) *MessageTask {
    return &MessageTask {
        newTaskBase(),
        message,
        make(chan *libcoap.Pdu),
        interval,
        retry,
        timeout,
        isStop,
        isHeartBeat,
        responseHandler,
        timeoutHandler,
    }
}

func (task *MessageTask) GetMessage() (*libcoap.Pdu) {
    return task.message
}

func (task *MessageTask) SetMessage(pdu *libcoap.Pdu) {
    task.message = pdu
}

func (task *MessageTask) IsStop() (bool) {
    return task.isStop
}

func (task *MessageTask) Stop() {
    task.stop()
}

func (task *MessageTask) GetResponseHandler() ResponseHandler {
    return task.responseHandler
}

func (task *MessageTask) GetTimeoutHandler() TimeoutHandler {
    return task.timeoutHandler
}

func (t *MessageTask) run(out chan Event) {
    timeout := time.After(t.timeout)

    out <- &MessageEvent{ EventBase{ t } }

    for i := 0; i < t.retry; i++ {
        select {
        case <- t.stopChan:
            return
        case <- time.After(t.interval):
            out <- &MessageEvent{ EventBase{ t } }
        case <- timeout:
            out <- &TimeoutEvent{ EventBase{ t } }
            t.stop()
        }
    }

    if t.message.Type == libcoap.TypeNon {
        select {
        case <- t.stopChan:
            return
        case <- timeout:
            if !t.isHeartBeat {
                log.Debug("Mitigation request timeout")
            }
            t.isStop = true
            out <- &TimeoutEvent{ EventBase{ t } }
            t.stop()
        }
    }else {
        select {
        case <- t.stopChan:
            return
        }
    }
}

func (e *MessageEvent) Handle(env *Env) {
    task := e.Task().(*MessageTask)
    env.session.Send(task.message)
}

func (e *TimeoutEvent) Handle(env *Env) {
    task := e.Task().(*MessageTask)
    task.timeoutHandler(task, env)
}

func (t *MessageTask) AddResponse(pdu *libcoap.Pdu) {
    t.response <- pdu
}<commit_msg>Update log when request timeout<commit_after>package task

import "time"
import "github.com/nttdots/go-dots/libcoap"
import log "github.com/sirupsen/logrus"

type ResponseHandler func(*MessageTask, *libcoap.Pdu, *Env)
type TimeoutHandler  func(*MessageTask, *Env)

type MessageTask struct {
    TaskBase

    message  *libcoap.Pdu
    response chan *libcoap.Pdu

    interval time.Duration
    retry    int
    timeout  time.Duration

    isStop bool
    isHeartBeat bool
    responseHandler ResponseHandler
    timeoutHandler  TimeoutHandler
}

type TimeoutEvent struct { EventBase }
type MessageEvent struct { EventBase }

func NewMessageTask(message *libcoap.Pdu,
                    interval time.Duration,
                    retry int,
                    timeout time.Duration,
                    isStop bool,
                    isHeartBeat bool,
                    responseHandler ResponseHandler,
                    timeoutHandler TimeoutHandler) *MessageTask {
    return &MessageTask {
        newTaskBase(),
        message,
        make(chan *libcoap.Pdu),
        interval,
        retry,
        timeout,
        isStop,
        isHeartBeat,
        responseHandler,
        timeoutHandler,
    }
}

func (task *MessageTask) GetMessage() (*libcoap.Pdu) {
    return task.message
}

func (task *MessageTask) SetMessage(pdu *libcoap.Pdu) {
    task.message = pdu
}

func (task *MessageTask) IsStop() (bool) {
    return task.isStop
}

func (task *MessageTask) Stop() {
    task.stop()
}

func (task *MessageTask) GetResponseHandler() ResponseHandler {
    return task.responseHandler
}

func (task *MessageTask) GetTimeoutHandler() TimeoutHandler {
    return task.timeoutHandler
}

func (t *MessageTask) run(out chan Event) {
    timeout := time.After(t.timeout)

    out <- &MessageEvent{ EventBase{ t } }

    for i := 0; i < t.retry; i++ {
        select {
        case <- t.stopChan:
            return
        case <- time.After(t.interval):
            out <- &MessageEvent{ EventBase{ t } }
        case <- timeout:
            out <- &TimeoutEvent{ EventBase{ t } }
            t.stop()
        }
    }

    if t.message.Type == libcoap.TypeNon {
        select {
        case <- t.stopChan:
            return
        case <- timeout:
            if !t.isHeartBeat {
                log.Debug("Request timeout")
            }
            t.isStop = true
            out <- &TimeoutEvent{ EventBase{ t } }
            t.stop()
        }
    }else {
        select {
        case <- t.stopChan:
            return
        }
    }
}

func (e *MessageEvent) Handle(env *Env) {
    task := e.Task().(*MessageTask)
    env.session.Send(task.message)
}

func (e *TimeoutEvent) Handle(env *Env) {
    task := e.Task().(*MessageTask)
    task.timeoutHandler(task, env)
}

func (t *MessageTask) AddResponse(pdu *libcoap.Pdu) {
    t.response <- pdu
}<|endoftext|>


<commit_before>// Package event handles incremental building of a log event.
package event

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"strconv"
	"time"
)

// Event holds a buffer of a log event content.
type Event struct {
	out      io.Writer
	buf      *bytes.Buffer
	wbuf     []byte
	maxLen   int
	exceeded int
	prefix   []byte
	suffix   []byte
	start    chan (<-chan time.Time) // timer
	stop     chan bool
	close    chan bool
}

var autoFlushCalledHook = func() {}

// New creates an event buffer writing to the out writer on flush.
// When flush, the eol string is appended to the event content.
// When jsonKey is not empty, the output is wrapped into a JSON object
// with jsonKey as message key.
func New(out io.Writer, ctx map[string]string, maxLen int, eol string, jsonKey string) (e *Event, err error) {
	e = &Event{
		out:    out,
		buf:    bytes.NewBuffer(make([]byte, 0, 4096)),
		wbuf:   make([]byte, 0, 2),
		maxLen: maxLen,
		start:  make(chan (<-chan time.Time)),
		stop:   make(chan bool),
		close:  make(chan bool, 1),
	}
	var ctxJSON []byte
	if len(ctx) > 0 {
		ctxJSON, err = json.Marshal(ctx)
		if err != nil {
			return nil, err
		}
		// Prepare for embedding by removing { } and append a comma
		ctxJSON = ctxJSON[1:]
		ctxJSON[len(ctxJSON)-1] = ','
	}
	if jsonKey != "" {
		e.prefix = []byte(fmt.Sprintf(`{%s"%s":"`, ctxJSON, jsonKey))
		e.suffix = []byte(fmt.Sprintf(`"}%s`, eol))
	} else {
		e.suffix = []byte(eol)
	}
	if maxLen > 0 && maxLen < len(e.prefix)+len(e.suffix) {
		return nil, errors.New("max len is lower than JSON envelope")
	}
	go e.autoFlushLoop()
	return
}

// Empty returns true if the event's buffer is empty.
func (e *Event) Empty() bool {
	return e.buf.Len() == 0
}

// Write appends the contents of p to the buffer. The return value
// n is the length of p; err is always nil.
func (e *Event) Write(p []byte) (n int, err error) {
	if e.exceeded > 0 {
		e.exceeded += len(p)
		return
	}
	overhead := len(e.prefix) + len(e.suffix)
	if e.maxLen > 0 && e.buf.Len()+overhead > e.maxLen {
		e.exceeded = len(p)
		return
	}
	e.buf.Grow(len(p))
	for i, b := range p {
		e.wbuf = e.wbuf[:0]
		switch b {
		case '"':
			e.wbuf = append(e.wbuf, '\\', b)
		case '\\':
			e.wbuf = append(e.wbuf, `\\`...)
		case '\b':
			e.wbuf = append(e.wbuf, `\b`...)
		case '\f':
			e.wbuf = append(e.wbuf, `\f`...)
		case '\n':
			e.wbuf = append(e.wbuf, `\n`...)
		case '\r':
			e.wbuf = append(e.wbuf, `\r`...)
		case '\t':
			e.wbuf = append(e.wbuf, `\t`...)
		default:
			e.wbuf = append(e.wbuf, b)
		}
		if e.maxLen > 0 && e.buf.Len()+overhead+len(e.wbuf) > e.maxLen {
			e.exceeded = len(p) - i
			break
		}
		var _n int
		_n, err = e.buf.Write(e.wbuf)
		n += _n
		if err != nil {
			break
		}
	}
	return
}

// Flush appends the eol string to the buffer and copies it to the
// output writer. The buffer is reset after this operation so the
// event can be reused.
//
// If an AutoFlush was in progress, it is stopped by this operation.
func (e *Event) Flush() {
	if e.buf.Len() == 0 {
		return
	}
	e.Stop()
	e.flush()
}

func (e *Event) flush() {
	if e.buf.Len() == 0 {
		return
	}
	if len(e.prefix) > 0 {
		if _, err := e.out.Write(e.prefix); err != nil {
			log.Fatal(err)
		}
	}
	if e.exceeded > 0 {
		const elipse = "[]…" // size of … is 3 bytes
		eb := []byte(strconv.FormatInt(int64(e.exceeded+len(elipse)), 10))
		if t := e.buf.Len() - (len(eb) + len(elipse)); t > 0 {
			// Insert [total_bytes_truncated]… at the end of the message is possible
			e.buf.Truncate(t)
			e.buf.WriteByte(elipse[0])
			e.buf.Write(eb)
			e.buf.WriteString(elipse[1:])
		}
	}
	if len(e.suffix) > 0 {
		e.buf.Write(e.suffix)
	}
	if _, err := io.Copy(e.out, e.buf); err != nil {
		log.Fatal(err)
	}
	e.buf.Reset()
	e.exceeded = 0
}

// AutoFlush schedule a flush after delay.
func (e *Event) AutoFlush(delay time.Duration) {
	e.start <- time.After(delay)
}

// Stop clears the auto flush timer
func (e *Event) Stop() {
	e.stop <- true
}

// Close stops the flush loop and releases resources.
func (e *Event) Close() error {
	close(e.close)
	return nil
}

func (e *Event) autoFlushLoop() {
	paused := make(<-chan time.Time) // will never fire
	next := paused
	for {
		select {
		case <-next:
			e.flush()
			autoFlushCalledHook()
		case <-e.stop:
			next = paused
		case timer := <-e.start:
			next = timer
		case <-e.close:
			return
		}
	}
}
<commit_msg>Prevent any concurrent flush<commit_after>// Package event handles incremental building of a log event.
package event

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"strconv"
	"time"
)

// Event holds a buffer of a log event content.
type Event struct {
	out      io.Writer
	buf      *bytes.Buffer
	wbuf     []byte
	maxLen   int
	exceeded int
	prefix   []byte
	suffix   []byte
	flush    chan chan bool
	start    chan (<-chan time.Time) // timer
	stop     chan bool
	close    chan bool
}

var autoFlushCalledHook = func() {}

// New creates an event buffer writing to the out writer on flush.
// When flush, the eol string is appended to the event content.
// When jsonKey is not empty, the output is wrapped into a JSON object
// with jsonKey as message key.
func New(out io.Writer, ctx map[string]string, maxLen int, eol string, jsonKey string) (e *Event, err error) {
	e = &Event{
		out:    out,
		buf:    bytes.NewBuffer(make([]byte, 0, 4096)),
		wbuf:   make([]byte, 0, 2),
		maxLen: maxLen,
		flush:  make(chan chan bool),
		start:  make(chan (<-chan time.Time)),
		stop:   make(chan bool),
		close:  make(chan bool, 1),
	}
	var ctxJSON []byte
	if len(ctx) > 0 {
		ctxJSON, err = json.Marshal(ctx)
		if err != nil {
			return nil, err
		}
		// Prepare for embedding by removing { } and append a comma
		ctxJSON = ctxJSON[1:]
		ctxJSON[len(ctxJSON)-1] = ','
	}
	if jsonKey != "" {
		e.prefix = []byte(fmt.Sprintf(`{%s"%s":"`, ctxJSON, jsonKey))
		e.suffix = []byte(fmt.Sprintf(`"}%s`, eol))
	} else {
		e.suffix = []byte(eol)
	}
	if maxLen > 0 && maxLen < len(e.prefix)+len(e.suffix) {
		return nil, errors.New("max len is lower than JSON envelope")
	}
	go e.flushLoop()
	return
}

// Empty returns true if the event's buffer is empty.
func (e *Event) Empty() bool {
	return e.buf.Len() == 0
}

// Write appends the contents of p to the buffer. The return value
// n is the length of p; err is always nil.
func (e *Event) Write(p []byte) (n int, err error) {
	if e.exceeded > 0 {
		e.exceeded += len(p)
		return
	}
	overhead := len(e.prefix) + len(e.suffix)
	if e.maxLen > 0 && e.buf.Len()+overhead > e.maxLen {
		e.exceeded = len(p)
		return
	}
	e.buf.Grow(len(p))
	for i, b := range p {
		e.wbuf = e.wbuf[:0]
		switch b {
		case '"':
			e.wbuf = append(e.wbuf, '\\', b)
		case '\\':
			e.wbuf = append(e.wbuf, `\\`...)
		case '\b':
			e.wbuf = append(e.wbuf, `\b`...)
		case '\f':
			e.wbuf = append(e.wbuf, `\f`...)
		case '\n':
			e.wbuf = append(e.wbuf, `\n`...)
		case '\r':
			e.wbuf = append(e.wbuf, `\r`...)
		case '\t':
			e.wbuf = append(e.wbuf, `\t`...)
		default:
			e.wbuf = append(e.wbuf, b)
		}
		if e.maxLen > 0 && e.buf.Len()+overhead+len(e.wbuf) > e.maxLen {
			e.exceeded = len(p) - i
			break
		}
		var _n int
		_n, err = e.buf.Write(e.wbuf)
		n += _n
		if err != nil {
			break
		}
	}
	return
}

// Flush appends the eol string to the buffer and copies it to the
// output writer. The buffer is reset after this operation so the
// event can be reused.
//
// If an AutoFlush was in progress, it is stopped by this operation.
func (e *Event) Flush() {
	if e.buf.Len() == 0 {
		return
	}
	e.Stop()
	c := make(chan bool)
	// Make the flushLoop to flush
	e.flush <- c
	// Wait for the flush to end
	<-c
}

func (e *Event) doFlush() {
	if e.buf.Len() == 0 {
		return
	}
	if len(e.prefix) > 0 {
		if _, err := e.out.Write(e.prefix); err != nil {
			log.Fatal(err)
		}
	}
	if e.exceeded > 0 {
		const elipse = "[]…" // size of … is 3 bytes
		eb := []byte(strconv.FormatInt(int64(e.exceeded+len(elipse)), 10))
		if t := e.buf.Len() - (len(eb) + len(elipse)); t > 0 {
			// Insert [total_bytes_truncated]… at the end of the message is possible
			e.buf.Truncate(t)
			e.buf.WriteByte(elipse[0])
			e.buf.Write(eb)
			e.buf.WriteString(elipse[1:])
		}
	}
	if len(e.suffix) > 0 {
		e.buf.Write(e.suffix)
	}
	if _, err := io.Copy(e.out, e.buf); err != nil {
		log.Fatal(err)
	}
	e.buf.Reset()
	e.exceeded = 0
}

// AutoFlush schedule a flush after delay.
func (e *Event) AutoFlush(delay time.Duration) {
	e.start <- time.After(delay)
}

// Stop clears the auto flush timer
func (e *Event) Stop() {
	e.stop <- true
}

// Close stops the flush loop and releases resources.
func (e *Event) Close() error {
	close(e.close)
	return nil
}

func (e *Event) flushLoop() {
	paused := make(<-chan time.Time) // will never fire
	next := paused
	for {
		select {
		case done := <-e.flush:
			e.doFlush()
			close(done) // notify caller
		case <-next:
			e.doFlush()
			autoFlushCalledHook()
		case <-e.stop:
			next = paused
		case timer := <-e.start:
			next = timer
		case <-e.close:
			return
		}
	}
}
<|endoftext|>


<commit_before>package event

import (
	"fmt"
	"os"
	"os/signal"
	"reflect"
	"syscall"
)

const (
	EXIT = "exit"
	WAIT = "wait"
)

var (
	Events = make(map[string][]func(interface{}), 2)
)

func On(name string, fs ...func(interface{})) error {
	evs, ok := Events[name]
	if !ok {
		evs = make([]func(interface{}), 0, len(fs))
	}

	for _, f := range fs {
		if fs == nil {
			continue
		}

		fp := reflect.ValueOf(f).Pointer()
		for i := 0; i < len(evs); i++ {
			if reflect.ValueOf(evs[i]).Pointer() == fp {
				return fmt.Errorf("func[%v] already exists in event[%s]", fp, name)
			}
		}
		evs = append(evs, f)
	}
	Events[name] = evs
	return nil
}

func Emit(name string, arg interface{}) {
	evs, ok := Events[name]
	if !ok {
		return
	}

	for _, f := range evs {
		f(arg)
	}
}

func EmitAll(arg interface{}) {
	for _, fs := range Events {
		for _, f := range fs {
			f(arg)
		}
	}
	return
}

func Off(name string, f func(interface{})) error {
	evs, ok := Events[name]
	if !ok || len(evs) == 0 {
		return fmt.Errorf("envet[%s] doesn't have any funcs", name)
	}

	fp := reflect.ValueOf(f).Pointer()
	for i := 0; i < len(evs); i++ {
		if reflect.ValueOf(evs[i]).Pointer() == fp {
			evs = append(evs[:i], evs[i+1:]...)
			Events[name] = evs
			return nil
		}
	}

	return fmt.Errorf("%v func dones't exist in event[%s]", fp, name)
}

func OffAll(name string) error {
	Events[name] = nil
	return nil
}

// 等待信号
// 如果信号参数为空，则会等待常见的终止信号
// SIGINT 2 A 键盘中断（如break键被按下）
// SIGTERM 15 A 终止信号
func Wait(sig ...os.Signal) os.Signal {
	c := make(chan os.Signal, 1)
	if len(sig) == 0 {
		signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	} else {
		signal.Notify(c, sig...)
	}
	return <-c
}
<commit_msg>bugfix: emit nil event<commit_after>package event

import (
	"fmt"
	"os"
	"os/signal"
	"reflect"
	"syscall"
)

const (
	EXIT = "exit"
	WAIT = "wait"
)

var (
	Events = make(map[string][]func(interface{}), 2)
)

func On(name string, fs ...func(interface{})) error {
	evs, ok := Events[name]
	if !ok {
		evs = make([]func(interface{}), 0, len(fs))
	}

	for _, f := range fs {
		if f == nil {
			continue
		}

		fp := reflect.ValueOf(f).Pointer()
		for i := 0; i < len(evs); i++ {
			if reflect.ValueOf(evs[i]).Pointer() == fp {
				return fmt.Errorf("func[%v] already exists in event[%s]", fp, name)
			}
		}
		evs = append(evs, f)
	}
	Events[name] = evs
	return nil
}

func Emit(name string, arg interface{}) {
	evs, ok := Events[name]
	if !ok {
		return
	}

	for _, f := range evs {
		f(arg)
	}
}

func EmitAll(arg interface{}) {
	for _, fs := range Events {
		for _, f := range fs {
			f(arg)
		}
	}
	return
}

func Off(name string, f func(interface{})) error {
	evs, ok := Events[name]
	if !ok || len(evs) == 0 {
		return fmt.Errorf("envet[%s] doesn't have any funcs", name)
	}

	fp := reflect.ValueOf(f).Pointer()
	for i := 0; i < len(evs); i++ {
		if reflect.ValueOf(evs[i]).Pointer() == fp {
			evs = append(evs[:i], evs[i+1:]...)
			Events[name] = evs
			return nil
		}
	}

	return fmt.Errorf("%v func dones't exist in event[%s]", fp, name)
}

func OffAll(name string) error {
	Events[name] = nil
	return nil
}

// 等待信号
// 如果信号参数为空，则会等待常见的终止信号
// SIGINT 2 A 键盘中断（如break键被按下）
// SIGTERM 15 A 终止信号
func Wait(sig ...os.Signal) os.Signal {
	c := make(chan os.Signal, 1)
	if len(sig) == 0 {
		signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	} else {
		signal.Notify(c, sig...)
	}
	return <-c
}
<|endoftext|>


<commit_before>// Modified UTF-7 encoding defined in RFC 3501 section 5.1.3
package utf7

import (
	"encoding/base64"

	"golang.org/x/text/encoding"
)

const (
	min = 0x20 // Minimum self-representing UTF-7 value
	max = 0x7E // Maximum self-representing UTF-7 value

	repl = '\uFFFD' // Unicode replacement code point
)

var b64Enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,")

type enc struct{}

func (e enc) NewDecoder() *encoding.Decoder {
	return &encoding.Decoder{
		Transformer: &decoder{true},
	}
}

func (e enc) NewEncoder() *encoding.Encoder {
	return &encoding.Encoder{
		Transformer: &encoder{},
	}
}

// Encoding is the modified UTF-7 encoding.
var Encoding = enc{}
<commit_msg>utf7: explicitly set Encoding type to improve docs<commit_after>// Modified UTF-7 encoding defined in RFC 3501 section 5.1.3
package utf7

import (
	"encoding/base64"

	"golang.org/x/text/encoding"
)

const (
	min = 0x20 // Minimum self-representing UTF-7 value
	max = 0x7E // Maximum self-representing UTF-7 value

	repl = '\uFFFD' // Unicode replacement code point
)

var b64Enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,")

type enc struct{}

func (e enc) NewDecoder() *encoding.Decoder {
	return &encoding.Decoder{
		Transformer: &decoder{true},
	}
}

func (e enc) NewEncoder() *encoding.Encoder {
	return &encoding.Encoder{
		Transformer: &encoder{},
	}
}

// Encoding is the modified UTF-7 encoding.
var Encoding encoding.Encoding = enc{}
<|endoftext|>


<commit_before>package main

import (
	//	"fmt"
	"github.com/nsf/termbox-go"
	"github.com/simulatedsimian/go_sandbox/geom"
)

func printAt(x, y int, s string, fg, bg termbox.Attribute) {
	for _, r := range s {
		termbox.SetCell(x, y, r, fg, bg)
		x++
	}
}

func printAtDef(x, y int, s string) {
	printAt(x, y, s, termbox.ColorDefault, termbox.ColorDefault)
}

func clearRect(rect geom.Rectangle, c rune, fg, bg termbox.Attribute) {
	w, h := termbox.Size()
	sz := geom.RectangleFromSize(geom.Coord{w, h})

	toClear, ok := geom.RectangleIntersection(rect, sz)
	if ok {
		for y := toClear.Min.Y; y < toClear.Max.Y; y++ {
			for x := toClear.Min.X; x < toClear.Max.X; x++ {
				termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorDefault)
			}
		}
	}
}

func clearRectDef(rect geom.Rectangle) {
	clearRect(rect, '.', termbox.ColorDefault, termbox.ColorDefault)
}

type DisplayElement interface {
	GiveFocus() bool
	HandleInput(k termbox.Key, r rune)
	Draw()
}

type DisplayList struct {
	list       []DisplayElement
	focusIndex int
}

func (dl *DisplayList) AddElement(elem DisplayElement) {
	dl.list = append(dl.list, elem)
}

func (dl *DisplayList) Draw() {
	w, h := termbox.Size()
	clearRectDef(geom.RectangleFromSize(geom.Coord{w, h}))

	for _, elem := range dl.list {
		elem.Draw()
	}
}

func (dl *DisplayList) NextFocus() {
	if dl.list != nil && len(dl.list) > 0 {
		for {
			dl.focusIndex++
			if dl.focusIndex >= len(dl.list) {
				dl.focusIndex = 0
			}

			if dl.list[dl.focusIndex].GiveFocus() {
				break
			}
		}
	}
}

func (dl *DisplayList) PrevFocus() {
	if dl.list != nil && len(dl.list) > 0 {
		for {
			dl.focusIndex--
			if dl.focusIndex < 0 {
				dl.focusIndex = len(dl.list)
			}

			if dl.list[dl.focusIndex].GiveFocus() {
				break
			}
		}
	}
}

func (dl *DisplayList) HandleInput(k termbox.Key, r rune) {

	if dl.list != nil && len(dl.list) > 0 {
		if k == termbox.KeyTab {
			dl.NextFocus()
		} else {
			dl.list[dl.focusIndex].HandleInput(k, r)
		}
	}
}

type InputHandler func(inp string)

type TextInputField struct {
	x, y       int
	inp        []rune
	cursorLoc  int
	inpHandler InputHandler
	hasFocus   bool
	history    [][]rune
	histPos    int
}

func MakeTextInputField(x, y int, inpHandler InputHandler) *TextInputField {
	return &TextInputField{x, y, nil, 0, inpHandler, false, nil, -1}
}

func (t *TextInputField) HandleInput(k termbox.Key, r rune) {
	if k == termbox.KeyEnter {
		t.history = append(t.history, t.inp)
		t.histPos = len(t.history) - 1
		t.inpHandler(string(t.inp))
		t.inp = nil
		t.cursorLoc = 0
	}

	if k == termbox.KeyArrowUp {
		if len(t.history) > 0 {
			t.inp = nil
			t.inp = append(t.inp, t.history[t.histPos]...)
			t.cursorLoc = len(t.inp)
			if t.histPos > 0 {
				t.histPos--
			}
		}
	}

	if k == termbox.KeyArrowDown {
		if len(t.history) > 0 {
			t.inp = nil
			t.inp = append(t.inp, t.history[t.histPos]...)
			t.cursorLoc = len(t.inp)
			if t.histPos < (len(t.history) - 1) {
				t.histPos++
			}
		}
	}

	if r > ' ' {
		t.inp = append(t.inp, r)
		t.cursorLoc++
	}

	if k == 32 {
		t.inp = append(t.inp, ' ')
		t.cursorLoc++
	}

	if len(t.inp) > 0 && (k == termbox.KeyBackspace || k == termbox.KeyBackspace2) {
		t.inp = t.inp[:len(t.inp)-1]
		t.cursorLoc--
	}

	termbox.SetCursor(t.x+t.cursorLoc, t.y)
	//	printAtDef(t.x, t.y+1, fmt.Sprintf("%v, %v               ", k, r))
}

func (t *TextInputField) Draw() {
	printAtDef(t.x, t.y, string(t.inp)+" ")
}

func (t *TextInputField) GiveFocus() bool {
	termbox.SetCursor(t.x, t.y)
	return true
}

type StaticText struct {
	x, y int
	text string
}

func (t *StaticText) HandleInput(k termbox.Key, r rune) {
}

func (t *StaticText) Draw() {
	printAtDef(t.x, t.y, t.text)
}

func (t *StaticText) GiveFocus() bool {
	return false
}

type ScrollingTextOutput struct {
	x, y int
	w, h int
	text []string
}

func (t *ScrollingTextOutput) HandleInput(k termbox.Key, r rune) {
}

func (t *ScrollingTextOutput) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func (t *ScrollingTextOutput) WriteLine(l string) {
	t.text = append(t.text, l)
}

func (t *ScrollingTextOutput) Draw() {
	clearRectDef(geom.RectangleFromPosSize(geom.Coord{t.x, t.y}, geom.Coord{t.w, t.h}))

	start := 0

	if len(t.text) > t.h {
		start = len(t.text) - t.h
	}

	y := t.y
	for l := start; l < len(t.text); l++ {
		printAtDef(t.x, y, t.text[l])
		y++
	}
}

func (t *ScrollingTextOutput) GiveFocus() bool {
	return false
}
<commit_msg>fixed command history<commit_after>package main

import (
	//	"fmt"
	"container/list"
	"github.com/nsf/termbox-go"
	"github.com/simulatedsimian/go_sandbox/geom"
)

func printAt(x, y int, s string, fg, bg termbox.Attribute) {
	for _, r := range s {
		termbox.SetCell(x, y, r, fg, bg)
		x++
	}
}

func printAtDef(x, y int, s string) {
	printAt(x, y, s, termbox.ColorDefault, termbox.ColorDefault)
}

func clearRect(rect geom.Rectangle, c rune, fg, bg termbox.Attribute) {
	w, h := termbox.Size()
	sz := geom.RectangleFromSize(geom.Coord{w, h})

	toClear, ok := geom.RectangleIntersection(rect, sz)
	if ok {
		for y := toClear.Min.Y; y < toClear.Max.Y; y++ {
			for x := toClear.Min.X; x < toClear.Max.X; x++ {
				termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorDefault)
			}
		}
	}
}

func clearRectDef(rect geom.Rectangle) {
	clearRect(rect, '.', termbox.ColorDefault, termbox.ColorDefault)
}

type DisplayElement interface {
	GiveFocus() bool
	HandleInput(k termbox.Key, r rune)
	Draw()
}

type DisplayList struct {
	list       []DisplayElement
	focusIndex int
}

func (dl *DisplayList) AddElement(elem DisplayElement) {
	dl.list = append(dl.list, elem)
}

func (dl *DisplayList) Draw() {
	w, h := termbox.Size()
	clearRectDef(geom.RectangleFromSize(geom.Coord{w, h}))

	for _, elem := range dl.list {
		elem.Draw()
	}
}

func (dl *DisplayList) NextFocus() {
	if dl.list != nil && len(dl.list) > 0 {
		for {
			dl.focusIndex++
			if dl.focusIndex >= len(dl.list) {
				dl.focusIndex = 0
			}

			if dl.list[dl.focusIndex].GiveFocus() {
				break
			}
		}
	}
}

func (dl *DisplayList) PrevFocus() {
	if dl.list != nil && len(dl.list) > 0 {
		for {
			dl.focusIndex--
			if dl.focusIndex < 0 {
				dl.focusIndex = len(dl.list)
			}

			if dl.list[dl.focusIndex].GiveFocus() {
				break
			}
		}
	}
}

func (dl *DisplayList) HandleInput(k termbox.Key, r rune) {

	if dl.list != nil && len(dl.list) > 0 {
		if k == termbox.KeyTab {
			dl.NextFocus()
		} else {
			dl.list[dl.focusIndex].HandleInput(k, r)
		}
	}
}

type InputHandler func(inp string)

type TextInputField struct {
	x, y       int
	inp        []rune
	cursorLoc  int
	inpHandler InputHandler
	hasFocus   bool
	history    *list.List
	histPos    *list.Element
}

func MakeTextInputField(x, y int, inpHandler InputHandler) *TextInputField {
	return &TextInputField{x, y, nil, 0, inpHandler, false, list.New(), nil}
}

func (t *TextInputField) HandleInput(k termbox.Key, r rune) {
	if k == termbox.KeyEnter {
		if t.histPos == nil {
			t.history.PushBack(t.inp)
		} else {
			t.history.MoveToBack(t.histPos)
		}
		t.histPos = nil

		t.inpHandler(string(t.inp))
		t.inp = nil
		t.cursorLoc = 0
	}

	if k == termbox.KeyArrowUp {
		if t.history.Len() > 0 {
			if t.histPos == nil {
				t.histPos = t.history.Back()
			} else {
				t.histPos = t.histPos.Prev()
				if t.histPos == nil {
					t.histPos = t.history.Front()
				}
			}
			t.inp = nil
			t.inp = append(t.inp, t.histPos.Value.([]rune)...)
			t.cursorLoc = len(t.inp)
		}
	}

	if k == termbox.KeyArrowDown {
		if t.history.Len() > 0 {
			if t.histPos != nil {
				t.histPos = t.histPos.Next()
			}

			t.inp = nil
			if t.histPos != nil {
				t.inp = append(t.inp, t.histPos.Value.([]rune)...)
			}
			t.cursorLoc = len(t.inp)
		}
	}

	if r > ' ' {
		t.inp = append(t.inp, r)
		t.cursorLoc++
	}

	if k == 32 {
		t.inp = append(t.inp, ' ')
		t.cursorLoc++
	}

	if len(t.inp) > 0 && (k == termbox.KeyBackspace || k == termbox.KeyBackspace2) {
		t.inp = t.inp[:len(t.inp)-1]
		t.cursorLoc--
	}

	termbox.SetCursor(t.x+t.cursorLoc, t.y)
	//	printAtDef(t.x, t.y+1, fmt.Sprintf("%v, %v               ", k, r))
}

func (t *TextInputField) Draw() {
	printAtDef(t.x, t.y, string(t.inp)+" ")
}

func (t *TextInputField) GiveFocus() bool {
	termbox.SetCursor(t.x, t.y)
	return true
}

type StaticText struct {
	x, y int
	text string
}

func (t *StaticText) HandleInput(k termbox.Key, r rune) {
}

func (t *StaticText) Draw() {
	printAtDef(t.x, t.y, t.text)
}

func (t *StaticText) GiveFocus() bool {
	return false
}

type ScrollingTextOutput struct {
	x, y int
	w, h int
	text []string
}

func (t *ScrollingTextOutput) HandleInput(k termbox.Key, r rune) {
}

func (t *ScrollingTextOutput) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func (t *ScrollingTextOutput) WriteLine(l string) {
	t.text = append(t.text, l)
}

func (t *ScrollingTextOutput) Draw() {
	clearRectDef(geom.RectangleFromPosSize(geom.Coord{t.x, t.y}, geom.Coord{t.w, t.h}))

	start := 0

	if len(t.text) > t.h {
		start = len(t.text) - t.h
	}

	y := t.y
	for l := start; l < len(t.text); l++ {
		printAtDef(t.x, y, t.text[l])
		y++
	}
}

func (t *ScrollingTextOutput) GiveFocus() bool {
	return false
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Ernest Micklei
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package proto

import (
	"testing"
)

func TestOptionCases(t *testing.T) {
	for i, each := range []struct {
		proto     string
		name      string
		strLit    string
		nonStrLit string
	}{{
		`option (full).java_package = "com.example.foo";`,
		"(full).java_package",
		"com.example.foo",
		"",
	}, {
		`option Bool = true;`,
		"Bool",
		"",
		"true",
	}, {
		`option Float = -3.14E1;`,
		"Float",
		"",
		"-3.14E1",
	}, {
		`option (foo_options) = { opt1: 123 opt2: "baz" };`,
		"(foo_options)",
		"",
		"",
	}, {
		`option optimize_for = SPEED;`,
		"optimize_for",
		"",
		"SPEED",
	}} {
		p := newParserOn(each.proto)
		pr, err := p.Parse()
		if err != nil {
			t.Fatal("testcase failed:", i, err)
		}
		if got, want := len(pr.Elements), 1; got != want {
			t.Fatalf("[%d] got [%v] want [%v]", i, got, want)
		}
		o := pr.Elements[0].(*Option)
		if got, want := o.Name, each.name; got != want {
			t.Errorf("[%d] got [%v] want [%v]", i, got, want)
		}
		if len(each.strLit) > 0 {
			if got, want := o.Constant.Source, each.strLit; got != want {
				t.Errorf("[%d] got [%v] want [%v]", i, got, want)
			}
		}
		if len(each.nonStrLit) > 0 {
			if got, want := o.Constant.Source, each.nonStrLit; got != want {
				t.Errorf("[%d] got [%v] want [%v]", i, got, want)
			}
		}
		if got, want := o.IsEmbedded, false; got != want {
			t.Errorf("[%d] got [%v] want [%v]", i, got, want)
		}
	}
}

func TestLiteralString(t *testing.T) {
	proto := `"string"`
	p := newParserOn(proto)
	l := new(Literal)
	if err := l.parse(p); err != nil {
		t.Fatal(err)
	}
	if got, want := l.IsString, true; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := l.Source, "string"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestOptionComments(t *testing.T) {
	proto := `
// comment
option Help = "me"; // inline`
	p := newParserOn(proto)
	pr, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	o := pr.Elements[0].(*Option)
	if got, want := o.IsEmbedded, false; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Lines[0], " comment"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment.Lines[0], " inline"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Position.Line, 3; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Position.Line, 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment.Position.Line, 3; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

func TestAggregateSyntax(t *testing.T) {
	proto := `
// usage:
message Bar {
  // alternative aggregate syntax (uses TextFormat):
  int32 b = 2 [(foo_options) = {
    opt1: 123,
    opt2: "baz"
  }];
}
	`
	p := newParserOn(proto)
	pr, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	o := pr.Elements[0].(*Message)
	f := o.Elements[0].(*NormalField)
	if got, want := len(f.Options), 1; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	ac := f.Options[0].AggregatedConstants
	if got, want := len(ac), 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[0].Name, "opt1"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[1].Name, "opt2"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[0].Source, "123"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[1].Source, "baz"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Position.Line, 3; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Position.String(), "<input>:2:1"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Position.String(), "<input>:5:3"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[0].Position.Line, 6; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[1].Position.Line, 7; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

func TestNonPrimitiveOptionComment(t *testing.T) {
	proto := `
// comment
option Help = { string_field: "value" }; // inline`
	p := newParserOn(proto)
	pr, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	o := pr.Elements[0].(*Option)
	if got, want := o.Comment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Lines[0], " comment"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment.Lines[0], " inline"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

func TestFieldCustomOptions(t *testing.T) {
	proto := `foo.bar lots = 1 [foo={hello:1}, bar=2];`
	p := newParserOn(proto)
	f := newNormalField()
	err := f.parse(p)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := f.Type, "foo.bar"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Name, "lots"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(f.Options), 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].Name, "foo"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[1].Name, "bar"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[1].Constant.Source, "2"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

// issue #50
func TestNestedAggregateConstants(t *testing.T) {
	src := `syntax = "proto3";

	package baz;

	option (foo.bar) = {
	  woot: 100
	  foo {
		hello: 200
		hello2: 300
		bar {
			hello3: 400
		}
	  }
	};`
	p := newParserOn(src)
	proto, err := p.Parse()
	if err != nil {
		t.Error(err)
	}
	option := proto.Elements[2].(*Option)
	if got, want := option.Name, "(foo.bar)"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(option.AggregatedConstants), 4; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[0].Name, "woot"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[1].Name, "foo.hello"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[2].Name, "foo.hello2"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[3].Name, "foo.bar.hello3"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[1].Literal.SourceRepresentation(), "200"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[2].Literal.SourceRepresentation(), "300"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[3].Literal.SourceRepresentation(), "400"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}
<commit_msg>Additional tests for complex options (#65)<commit_after>// Copyright (c) 2017 Ernest Micklei
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package proto

import (
	"testing"
)

func TestOptionCases(t *testing.T) {
	for i, each := range []struct {
		proto     string
		name      string
		strLit    string
		nonStrLit string
	}{{
		`option (full).java_package = "com.example.foo";`,
		"(full).java_package",
		"com.example.foo",
		"",
	}, {
		`option Bool = true;`,
		"Bool",
		"",
		"true",
	}, {
		`option Float = -3.14E1;`,
		"Float",
		"",
		"-3.14E1",
	}, {
		`option (foo_options) = { opt1: 123 opt2: "baz" };`,
		"(foo_options)",
		"",
		"",
	}, {
		`option optimize_for = SPEED;`,
		"optimize_for",
		"",
		"SPEED",
	}} {
		p := newParserOn(each.proto)
		pr, err := p.Parse()
		if err != nil {
			t.Fatal("testcase failed:", i, err)
		}
		if got, want := len(pr.Elements), 1; got != want {
			t.Fatalf("[%d] got [%v] want [%v]", i, got, want)
		}
		o := pr.Elements[0].(*Option)
		if got, want := o.Name, each.name; got != want {
			t.Errorf("[%d] got [%v] want [%v]", i, got, want)
		}
		if len(each.strLit) > 0 {
			if got, want := o.Constant.Source, each.strLit; got != want {
				t.Errorf("[%d] got [%v] want [%v]", i, got, want)
			}
		}
		if len(each.nonStrLit) > 0 {
			if got, want := o.Constant.Source, each.nonStrLit; got != want {
				t.Errorf("[%d] got [%v] want [%v]", i, got, want)
			}
		}
		if got, want := o.IsEmbedded, false; got != want {
			t.Errorf("[%d] got [%v] want [%v]", i, got, want)
		}
	}
}

func TestLiteralString(t *testing.T) {
	proto := `"string"`
	p := newParserOn(proto)
	l := new(Literal)
	if err := l.parse(p); err != nil {
		t.Fatal(err)
	}
	if got, want := l.IsString, true; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := l.Source, "string"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestOptionComments(t *testing.T) {
	proto := `
// comment
option Help = "me"; // inline`
	p := newParserOn(proto)
	pr, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	o := pr.Elements[0].(*Option)
	if got, want := o.IsEmbedded, false; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Lines[0], " comment"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment.Lines[0], " inline"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Position.Line, 3; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Position.Line, 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment.Position.Line, 3; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

func TestAggregateSyntax(t *testing.T) {
	proto := `
// usage:
message Bar {
  // alternative aggregate syntax (uses TextFormat):
  int32 b = 2 [(foo_options) = {
    opt1: 123,
    opt2: "baz"
  }];
}
	`
	p := newParserOn(proto)
	pr, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	o := pr.Elements[0].(*Message)
	f := o.Elements[0].(*NormalField)
	if got, want := len(f.Options), 1; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	ac := f.Options[0].AggregatedConstants
	if got, want := len(ac), 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[0].Name, "opt1"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[1].Name, "opt2"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[0].Source, "123"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[1].Source, "baz"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Position.Line, 3; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Position.String(), "<input>:2:1"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Position.String(), "<input>:5:3"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[0].Position.Line, 6; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := ac[1].Position.Line, 7; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

func TestNonPrimitiveOptionComment(t *testing.T) {
	proto := `
// comment
option Help = { string_field: "value" }; // inline`
	p := newParserOn(proto)
	pr, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	o := pr.Elements[0].(*Option)
	if got, want := o.Comment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.Comment.Lines[0], " comment"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment != nil, true; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := o.InlineComment.Lines[0], " inline"; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
}

func TestFieldCustomOptions(t *testing.T) {
	proto := `foo.bar lots = 1 [foo={hello:1}, bar=2];`
	p := newParserOn(proto)
	f := newNormalField()
	err := f.parse(p)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := f.Type, "foo.bar"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Name, "lots"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(f.Options), 2; got != want {
		t.Fatalf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[0].Name, "foo"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[1].Name, "bar"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := f.Options[1].Constant.Source, "2"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

// issue #50
func TestNestedAggregateConstants(t *testing.T) {
	src := `syntax = "proto3";

	package baz;

	option (foo.bar) = {
	  woot: 100
	  foo {
		hello: 200
		hello2: 300
		bar {
			hello3: 400
		}
	  }
	};`
	p := newParserOn(src)
	proto, err := p.Parse()
	if err != nil {
		t.Error(err)
	}
	option := proto.Elements[2].(*Option)
	if got, want := option.Name, "(foo.bar)"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(option.AggregatedConstants), 4; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[0].Name, "woot"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[1].Name, "foo.hello"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[2].Name, "foo.hello2"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[3].Name, "foo.bar.hello3"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[1].Literal.SourceRepresentation(), "200"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[2].Literal.SourceRepresentation(), "300"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[3].Literal.SourceRepresentation(), "400"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestNestedAggregateConstantsColons(t *testing.T) {
	src := `syntax = "proto3";

	package baz;

	option (foo.bar) = {
	  woot: 100
	  foo: {
		hello: 200
		hello2: 300
		bar: {
			hello3: 400
		}
	  }
	};`
	p := newParserOn(src)
	proto, err := p.Parse()
	if err != nil {
		t.Error(err)
	}
	option := proto.Elements[2].(*Option)
	if got, want := option.Name, "(foo.bar)"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(option.AggregatedConstants), 4; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[0].Name, "woot"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[1].Name, "foo.hello"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[2].Name, "foo.hello2"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[3].Name, "foo.bar.hello3"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[1].Literal.SourceRepresentation(), "200"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[2].Literal.SourceRepresentation(), "300"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[3].Literal.SourceRepresentation(), "400"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}

func TestNestedAggregateConstantsColonsWithLineSeparation(t *testing.T) {
	src := `syntax = "proto3";

	package baz;

	option (foo.bar) = {
	  woot: 100
	  foo: {
		hello: 200
		hello2: 300
		bar:
			{ hello3: 400 }
	  }
	};`
	p := newParserOn(src)
	proto, err := p.Parse()
	if err != nil {
		t.Error(err)
	}
	option := proto.Elements[2].(*Option)
	if got, want := option.Name, "(foo.bar)"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := len(option.AggregatedConstants), 4; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[0].Name, "woot"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[1].Name, "foo.hello"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[2].Name, "foo.hello2"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[3].Name, "foo.bar.hello3"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[1].Literal.SourceRepresentation(), "200"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[2].Literal.SourceRepresentation(), "300"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
	if got, want := option.AggregatedConstants[3].Literal.SourceRepresentation(), "400"; got != want {
		t.Errorf("got [%v] want [%v]", got, want)
	}
}
<|endoftext|>