<commit_before>package gopush

import (
	"errors"
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"strings"

	"github.com/cryptix/goremutake"
)

// Interpreter is a Push interpreter.
type Interpreter struct {
	Stacks  map[string]*Stack
	Options Options
	Rand    *rand.Rand

	Definitions        map[string]Code
	listOfDefinitions  []string
	listOfInstructions []string

	numEvalPush       int
	quoteNextName     bool
	numNamesGenerated uint
}

// NewInterpreter returns a new Push Interpreter, configured with the provided Options.
func NewInterpreter(options Options) *Interpreter {

	if options.RandomSeed == 0 {
		options.RandomSeed = rand.Int63()
	}

	interpreter := &Interpreter{
		Stacks:             make(map[string]*Stack),
		Options:            options,
		Rand:               rand.New(rand.NewSource(options.RandomSeed)),
		Definitions:        make(map[string]Code),
		listOfDefinitions:  make([]string, 0),
		listOfInstructions: make([]string, 0),
		numEvalPush:        0,
		quoteNextName:      false,
		numNamesGenerated:  0,
	}

	// Setup stacks
	interpreter.RegisterStack("exec", newExecStack(interpreter))
	interpreter.RegisterStack("name", newNameStack(interpreter))
	interpreter.listOfInstructions = append(interpreter.listOfInstructions, "NAME-ERC")

	if _, ok := options.AllowedTypes["boolean"]; ok {
		interpreter.RegisterStack("boolean", newBooleanStack(interpreter))
	}

	if _, ok := options.AllowedTypes["code"]; ok {
		interpreter.RegisterStack("code", newCodeStack(interpreter))
	}

	if _, ok := options.AllowedTypes["float"]; ok {
		interpreter.RegisterStack("float", newFloatStack(interpreter))
		interpreter.listOfInstructions = append(interpreter.listOfInstructions, "FLOAT-ERC")
	}

	if _, ok := options.AllowedTypes["integer"]; ok {
		interpreter.RegisterStack("integer", newIntStack(interpreter))
		interpreter.listOfInstructions = append(interpreter.listOfInstructions, "INTEGER-ERC")
	}

	return interpreter
}

// RegisterStack registers the given stack under the given name. This
// automatically prunes instructions that are not in the set of allowed
// instructions and also makes the instructions of the stack available for
// CODE.RAND to generate. It will NOT overwrite already existing stacks.
func (i *Interpreter) RegisterStack(name string, s *Stack) {
	if _, ok := i.Stacks[name]; ok {
		return
	}

	i.Stacks[name] = s

	// Prune disallowed instructions
	for fn := range s.Functions {
		if _, ok := i.Options.AllowedInstructions[name+"."+fn]; !ok {
			delete(s.Functions, fn)
		}
	}

	// Add the Stack's functions to the list of functions
	for fn := range s.Functions {
		i.listOfInstructions = append(i.listOfInstructions, strings.ToUpper(name+"."+fn))
	}

	// Sort the instructions (otherwise runs aren't repeatable)
	sort.Strings(i.listOfInstructions)
}

func (i *Interpreter) randomInstruction() Code {
	var instr string

	n := i.Rand.Intn(len(i.listOfInstructions) + len(i.listOfDefinitions))

	if n < len(i.listOfInstructions) {
		instr = i.listOfInstructions[n]
	} else {
		instr = i.listOfDefinitions[n-len(i.listOfInstructions)]
	}

	switch instr {
	case "INTEGER-ERC":
		// Generate ephemeral random constant integer
		high := i.Options.MaxRandomInteger
		low := i.Options.MinRandomInteger
		instr = fmt.Sprint(i.Rand.Int63n(high+1-low) + low)

	case "FLOAT-ERC":
		// Generate ephemeral random constant float
		high := i.Options.MaxRandomFloat
		low := i.Options.MinRandomFloat
		instr = fmt.Sprint(i.Rand.Float64()*(high-low) + low)
		if !strings.Contains(instr, ".") {
			instr += ".0"
		}

	case "NAME-ERC":
		// Generate ephemeral random constant NAME
		if i.Rand.Float64() < i.Options.NewERCNameProbabilty || i.numNamesGenerated == 0 {
			// Generate a new random NAME
			instr = goremutake.Encode(i.numNamesGenerated)
			i.numNamesGenerated++
		} else {
			// Use a random, already generated NAME
			instr = goremutake.Encode(uint(i.Rand.Intn(int(i.numNamesGenerated))))
		}
	}

	return Code{Length: 1, Literal: instr}
}

func (i *Interpreter) StackOK(name string, mindepth int64) bool {
	s, ok := i.Stacks[name]
	if !ok {
		return false
	}

	if s.Len() < mindepth {
		return false
	}

	return true
}

func (i *Interpreter) define(name string, code Code) {
	if _, ok := i.Definitions[name]; !ok {
		i.listOfDefinitions = append(i.listOfDefinitions, name)
	}

	i.Definitions[name] = code
}

func (i *Interpreter) printInterpreterState() {
	fmt.Println("Step", i.numEvalPush)
	for k, v := range i.Stacks {
		fmt.Printf("%s:\n", k)
		for i := len(v.Stack) - 1; i >= 0; i-- {
			fmt.Printf("- %v\n", v.Stack[i])
		}
	}
	fmt.Println()
	fmt.Println()
}

func (i *Interpreter) runCode(program Code) (err error) {

	// Recover from a panic that could occur while executing an instruction.
	// Because it is more convenient for functions to not return an error,
	// the functions that want to return an error panic instead.
	defer func() {
		if perr := recover(); perr != nil {
			err = perr.(error)
		}
	}()

	i.Stacks["exec"].Push(program)

	for i.Stacks["exec"].Len() > 0 && i.numEvalPush < i.Options.EvalPushLimit {

		if i.Options.Tracing {
			i.printInterpreterState()
		}

		item := i.Stacks["exec"].Pop().(Code)
		i.numEvalPush++

		// If the item on top of the exec stack is a list, push it in
		// reverse order
		if item.Literal == "" {
			for j := len(item.List) - 1; j >= 0; j-- {
				i.Stacks["exec"].Push(item.List[j])
			}
			continue
		}

		// Try to parse the item on top of the exec stack as a literal
		if intlit, err := strconv.ParseInt(item.Literal, 10, 64); err == nil {
			if !i.StackOK("integer", 0) {
				return fmt.Errorf("found integer literal %v, but the integer stack is disabled", intlit)
			}
			i.Stacks["integer"].Push(intlit)
			continue
		}

		if floatlit, err := strconv.ParseFloat(item.Literal, 64); err == nil {
			if !i.StackOK("float", 0) {
				return fmt.Errorf("found float literal %v, but the float stack is disabled", floatlit)
			}
			i.Stacks["float"].Push(floatlit)
			continue
		}

		if boollit, err := strconv.ParseBool(item.Literal); err == nil {
			if !i.StackOK("boolean", 0) {
				return fmt.Errorf("found boolean literal %v, but the boolean stack is disabled", boollit)
			}
			i.Stacks["boolean"].Push(boollit)
			continue
		}

		// Try to parse the item on top of the exec stack as instruction
		if strings.Contains(item.Literal, ".") {
			stack := strings.ToLower(item.Literal[:strings.Index(item.Literal, ".")])
			operation := strings.ToLower(item.Literal[strings.Index(item.Literal, ".")+1:])

			s, ok := i.Stacks[stack]
			if !ok {
				return fmt.Errorf("unknown or disabled stack: %v", stack)
			}

			f, ok := s.Functions[operation]
			if !ok {
				return fmt.Errorf("unknown or disabled instruction %v.%v", stack, operation)
			}

			f()
			continue
		}

		// If the item is not an instruction, it must be a name, either
		// bound or unbound. If the quoteNextName flag is false, we can
		// check if the name is already bound.
		if !i.quoteNextName {
			if d, ok := i.Definitions[strings.ToLower(item.Literal)]; ok {
				// Name is already bound, push its value onto the exec stack
				i.Stacks["exec"].Push(d)
				continue
			}
		}

		// The name is not bound yet, so push it onto the name stack
		i.Stacks["name"].Push(strings.ToLower(item.Literal))
		i.quoteNextName = false
	}

	if i.numEvalPush >= i.Options.EvalPushLimit {
		return errors.New("the EvalPushLimit was exceeded")
	}

	return nil
}

// RunCode runs the given program (given as Code type) until the EvalPushLimit
// is reached
func (i *Interpreter) RunCode(c Code) error {
	if i.Options.TopLevelPushCode {
		if s, ok := i.Stacks["code"]; ok {
			s.Push(c)
		}
	}

	err := i.runCode(c)

	if i.Options.TopLevelPopCode {
		if s, ok := i.Stacks["code"]; ok {
			s.Pop()
		}
	}

	if i.Options.Tracing {
		i.printInterpreterState()
	}

	return err
}

// Run runs the given program written in the Push programming language until the
// EvalPushLimit is reached
func (i *Interpreter) Run(program string) error {
	c, err := ParseCode(program)
	if err != nil {
		return err
	}

	err = i.RunCode(c)

	return err
}
<commit_msg>Document Interpreter.StackOK<commit_after>package gopush

import (
	"errors"
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"strings"

	"github.com/cryptix/goremutake"
)

// Interpreter is a Push interpreter.
type Interpreter struct {
	Stacks  map[string]*Stack
	Options Options
	Rand    *rand.Rand

	Definitions        map[string]Code
	listOfDefinitions  []string
	listOfInstructions []string

	numEvalPush       int
	quoteNextName     bool
	numNamesGenerated uint
}

// NewInterpreter returns a new Push Interpreter, configured with the provided Options.
func NewInterpreter(options Options) *Interpreter {

	if options.RandomSeed == 0 {
		options.RandomSeed = rand.Int63()
	}

	interpreter := &Interpreter{
		Stacks:             make(map[string]*Stack),
		Options:            options,
		Rand:               rand.New(rand.NewSource(options.RandomSeed)),
		Definitions:        make(map[string]Code),
		listOfDefinitions:  make([]string, 0),
		listOfInstructions: make([]string, 0),
		numEvalPush:        0,
		quoteNextName:      false,
		numNamesGenerated:  0,
	}

	// Setup stacks
	interpreter.RegisterStack("exec", newExecStack(interpreter))
	interpreter.RegisterStack("name", newNameStack(interpreter))
	interpreter.listOfInstructions = append(interpreter.listOfInstructions, "NAME-ERC")

	if _, ok := options.AllowedTypes["boolean"]; ok {
		interpreter.RegisterStack("boolean", newBooleanStack(interpreter))
	}

	if _, ok := options.AllowedTypes["code"]; ok {
		interpreter.RegisterStack("code", newCodeStack(interpreter))
	}

	if _, ok := options.AllowedTypes["float"]; ok {
		interpreter.RegisterStack("float", newFloatStack(interpreter))
		interpreter.listOfInstructions = append(interpreter.listOfInstructions, "FLOAT-ERC")
	}

	if _, ok := options.AllowedTypes["integer"]; ok {
		interpreter.RegisterStack("integer", newIntStack(interpreter))
		interpreter.listOfInstructions = append(interpreter.listOfInstructions, "INTEGER-ERC")
	}

	return interpreter
}

// RegisterStack registers the given stack under the given name. This
// automatically prunes instructions that are not in the set of allowed
// instructions and also makes the instructions of the stack available for
// CODE.RAND to generate. It will NOT overwrite already existing stacks.
func (i *Interpreter) RegisterStack(name string, s *Stack) {
	if _, ok := i.Stacks[name]; ok {
		return
	}

	i.Stacks[name] = s

	// Prune disallowed instructions
	for fn := range s.Functions {
		if _, ok := i.Options.AllowedInstructions[name+"."+fn]; !ok {
			delete(s.Functions, fn)
		}
	}

	// Add the Stack's functions to the list of functions
	for fn := range s.Functions {
		i.listOfInstructions = append(i.listOfInstructions, strings.ToUpper(name+"."+fn))
	}

	// Sort the instructions (otherwise runs aren't repeatable)
	sort.Strings(i.listOfInstructions)
}

func (i *Interpreter) randomInstruction() Code {
	var instr string

	n := i.Rand.Intn(len(i.listOfInstructions) + len(i.listOfDefinitions))

	if n < len(i.listOfInstructions) {
		instr = i.listOfInstructions[n]
	} else {
		instr = i.listOfDefinitions[n-len(i.listOfInstructions)]
	}

	switch instr {
	case "INTEGER-ERC":
		// Generate ephemeral random constant integer
		high := i.Options.MaxRandomInteger
		low := i.Options.MinRandomInteger
		instr = fmt.Sprint(i.Rand.Int63n(high+1-low) + low)

	case "FLOAT-ERC":
		// Generate ephemeral random constant float
		high := i.Options.MaxRandomFloat
		low := i.Options.MinRandomFloat
		instr = fmt.Sprint(i.Rand.Float64()*(high-low) + low)
		if !strings.Contains(instr, ".") {
			instr += ".0"
		}

	case "NAME-ERC":
		// Generate ephemeral random constant NAME
		if i.Rand.Float64() < i.Options.NewERCNameProbabilty || i.numNamesGenerated == 0 {
			// Generate a new random NAME
			instr = goremutake.Encode(i.numNamesGenerated)
			i.numNamesGenerated++
		} else {
			// Use a random, already generated NAME
			instr = goremutake.Encode(uint(i.Rand.Intn(int(i.numNamesGenerated))))
		}
	}

	return Code{Length: 1, Literal: instr}
}

// StackOK verifies that the given stack exists and has at least `mindepth`
// elements on it. This is used in stack functions to check if enough operands
// are available to carry out an instruction.
func (i *Interpreter) StackOK(name string, mindepth int64) bool {
	s, ok := i.Stacks[name]
	if !ok {
		return false
	}

	if s.Len() < mindepth {
		return false
	}

	return true
}

func (i *Interpreter) define(name string, code Code) {
	if _, ok := i.Definitions[name]; !ok {
		i.listOfDefinitions = append(i.listOfDefinitions, name)
	}

	i.Definitions[name] = code
}

func (i *Interpreter) printInterpreterState() {
	fmt.Println("Step", i.numEvalPush)
	for k, v := range i.Stacks {
		fmt.Printf("%s:\n", k)
		for i := len(v.Stack) - 1; i >= 0; i-- {
			fmt.Printf("- %v\n", v.Stack[i])
		}
	}
	fmt.Println()
	fmt.Println()
}

func (i *Interpreter) runCode(program Code) (err error) {

	// Recover from a panic that could occur while executing an instruction.
	// Because it is more convenient for functions to not return an error,
	// the functions that want to return an error panic instead.
	defer func() {
		if perr := recover(); perr != nil {
			err = perr.(error)
		}
	}()

	i.Stacks["exec"].Push(program)

	for i.Stacks["exec"].Len() > 0 && i.numEvalPush < i.Options.EvalPushLimit {

		if i.Options.Tracing {
			i.printInterpreterState()
		}

		item := i.Stacks["exec"].Pop().(Code)
		i.numEvalPush++

		// If the item on top of the exec stack is a list, push it in
		// reverse order
		if item.Literal == "" {
			for j := len(item.List) - 1; j >= 0; j-- {
				i.Stacks["exec"].Push(item.List[j])
			}
			continue
		}

		// Try to parse the item on top of the exec stack as a literal
		if intlit, err := strconv.ParseInt(item.Literal, 10, 64); err == nil {
			if !i.StackOK("integer", 0) {
				return fmt.Errorf("found integer literal %v, but the integer stack is disabled", intlit)
			}
			i.Stacks["integer"].Push(intlit)
			continue
		}

		if floatlit, err := strconv.ParseFloat(item.Literal, 64); err == nil {
			if !i.StackOK("float", 0) {
				return fmt.Errorf("found float literal %v, but the float stack is disabled", floatlit)
			}
			i.Stacks["float"].Push(floatlit)
			continue
		}

		if boollit, err := strconv.ParseBool(item.Literal); err == nil {
			if !i.StackOK("boolean", 0) {
				return fmt.Errorf("found boolean literal %v, but the boolean stack is disabled", boollit)
			}
			i.Stacks["boolean"].Push(boollit)
			continue
		}

		// Try to parse the item on top of the exec stack as instruction
		if strings.Contains(item.Literal, ".") {
			stack := strings.ToLower(item.Literal[:strings.Index(item.Literal, ".")])
			operation := strings.ToLower(item.Literal[strings.Index(item.Literal, ".")+1:])

			s, ok := i.Stacks[stack]
			if !ok {
				return fmt.Errorf("unknown or disabled stack: %v", stack)
			}

			f, ok := s.Functions[operation]
			if !ok {
				return fmt.Errorf("unknown or disabled instruction %v.%v", stack, operation)
			}

			f()
			continue
		}

		// If the item is not an instruction, it must be a name, either
		// bound or unbound. If the quoteNextName flag is false, we can
		// check if the name is already bound.
		if !i.quoteNextName {
			if d, ok := i.Definitions[strings.ToLower(item.Literal)]; ok {
				// Name is already bound, push its value onto the exec stack
				i.Stacks["exec"].Push(d)
				continue
			}
		}

		// The name is not bound yet, so push it onto the name stack
		i.Stacks["name"].Push(strings.ToLower(item.Literal))
		i.quoteNextName = false
	}

	if i.numEvalPush >= i.Options.EvalPushLimit {
		return errors.New("the EvalPushLimit was exceeded")
	}

	return nil
}

// RunCode runs the given program (given as Code type) until the EvalPushLimit
// is reached
func (i *Interpreter) RunCode(c Code) error {
	if i.Options.TopLevelPushCode {
		if s, ok := i.Stacks["code"]; ok {
			s.Push(c)
		}
	}

	err := i.runCode(c)

	if i.Options.TopLevelPopCode {
		if s, ok := i.Stacks["code"]; ok {
			s.Pop()
		}
	}

	if i.Options.Tracing {
		i.printInterpreterState()
	}

	return err
}

// Run runs the given program written in the Push programming language until the
// EvalPushLimit is reached
func (i *Interpreter) Run(program string) error {
	c, err := ParseCode(program)
	if err != nil {
		return err
	}

	err = i.RunCode(c)

	return err
}
<|endoftext|>


<commit_before>// GoUblu github.com/jwoehr/goublu
// goublu launches ublu (https://github.com/jwoehr/ublu) a java-coded
// domain-specific language for remote programming of traditional IBM business
// systems. goublu means to supplement the weak console support of Java.
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
)

func main() {
	fmt.Println("GoUblu front end for Ublu")
	myCmds := []string{"-jar", "/opt/ublu/ublu.jar", "-g", "--"}
	ubluArgs := append(myCmds, os.Args[1:]...)
	cmd := exec.Command("java", ubluArgs...)
	stdin, _ := cmd.StdinPipe()
	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()
	outreader := bufio.NewReader(stdout)
	errreader := bufio.NewReader(stderr)
	/*	inreader  := bufio.NewReader(os.Stdin) */
	scanner := bufio.NewScanner(os.Stdin)

	go func() {
		var text string
		for {
			text, _ = outreader.ReadString('\n')
			fmt.Print(text)
		}
		defer stdout.Close()
	}()

	go func() {
		var text string
		for {
			text, _ = errreader.ReadString('\n')
			fmt.Print(text)
		}
		defer stderr.Close()
	}()
	/*
		go func() {
			var text string
			for {
				text, _ = inreader.ReadString('\n')
				io.WriteString(stdin, text)
			}
		}()
	*/
	go func() {
		for scanner.Scan() {
			io.WriteString(stdin, scanner.Text()+"\n")
		}
		if err := scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, "reading standard input:", err)
		}
	}()

	cmd.Run()
}
<commit_msg>Tried with a termbox EditBox<commit_after>// GoUblu github.com/jwoehr/goublu
// goublu launches ublu (https://github.com/jwoehr/ublu) a java-coded
// domain-specific language for remote programming of traditional IBM business
// systems. goublu means to supplement the weak console support of Java.

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"github.com/nsf/termbox-go"
	"github.com/jwoehr/goublu/console"
)

func main() {

	var edit_box console.EditBox

	fmt.Println("GoUblu front end for Ublu")
	myCmds := []string{"-jar", "/opt/ublu/ublu.jar", "-g", "--"}
	ubluArgs := append(myCmds, os.Args[1:]...)
	cmd := exec.Command("java", ubluArgs...)
	stdin, _ := cmd.StdinPipe()
	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()
	outreader := bufio.NewReader(stdout)
	errreader := bufio.NewReader(stderr)
	/*	inreader  := bufio.NewReader(os.Stdin) */
	/* scanner := bufio.NewScanner(os.Stdin) */

	go func() {
		var text string
		for {
			text, _ = outreader.ReadString('\n')
			fmt.Print(text)
		}
		defer stdout.Close()
	}()

	go func() {
		var text string
		for {
			text, _ = errreader.ReadString('\n')
			fmt.Print(text)
		}
		defer stderr.Close()
	}()
	/*
		go func() {
			var text string
			for {
				text, _ = inreader.ReadString('\n')
				io.WriteString(stdin, text)
			}
		}()
	*/
	/*
		go func() {
			for scanner.Scan() {
				io.WriteString(stdin, scanner.Text()+"\n")
			}
			if err := scanner.Err(); err != nil {
				fmt.Fprintln(os.Stderr, "reading standard input:", err)
			}
		}()
	*/
	go func() {
		err := termbox.Init()
		if err != nil {
			panic(err)
		}
		defer termbox.Close()
		termbox.SetInputMode(termbox.InputEsc)

		console.Redraw_all()
	mainloop:
		for {
			switch ev := termbox.PollEvent(); ev.Type {
			case termbox.EventKey:
				switch ev.Key {
				case termbox.KeyEsc:
					break mainloop
				case termbox.KeyArrowLeft, termbox.KeyCtrlB:
					edit_box.MoveCursorOneRuneBackward()
				case termbox.KeyArrowRight, termbox.KeyCtrlF:
					edit_box.MoveCursorOneRuneForward()
				case termbox.KeyBackspace, termbox.KeyBackspace2:
					edit_box.DeleteRuneBackward()
				case termbox.KeyDelete, termbox.KeyCtrlD:
					edit_box.DeleteRuneForward()
				case termbox.KeyTab:
					edit_box.InsertRune('\t')
				case termbox.KeySpace:
					edit_box.InsertRune(' ')
				case termbox.KeyCtrlK:
					edit_box.DeleteTheRestOfTheLine()
				case termbox.KeyHome, termbox.KeyCtrlA:
					edit_box.MoveCursorToBeginningOfTheLine()
				case termbox.KeyEnd, termbox.KeyCtrlE:
					edit_box.MoveCursorToEndOfTheLine()
				case termbox.KeyEnter:
					io.WriteString(stdin, string(edit_box.Text) + "\n")
					edit_box.Empty()
					edit_box.MoveCursorToBeginningOfTheLine()
					// edit_box.Draw()
				default:
					if ev.Ch != 0 {
						edit_box.InsertRune(ev.Ch)
					}
				}
			case termbox.EventError:
				panic(ev.Err)
			}
			console.Redraw_all()
		}
	}()

	cmd.Run()
}
<|endoftext|>


<commit_before>package httputil

import (
	"net/http"
	"time"
)

// SlowHandler wraps a http.HandlerFunc and calls cb if the request takes
// longer than max time to process.  The function runs to completion -- slow
// requests are not aborted.
func SlowHandler(fn http.HandlerFunc, max time.Duration, cb func(r *http.Request, t time.Duration)) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		t0 := time.Now()
		fn(w, req)
		t := time.Since(t0)
		if t > max {
			cb(req, t)
		}
	}
}
<commit_msg>Add TimeHandler<commit_after>package httputil

import (
	"net/http"
	"time"
)

// SlowHandler wraps a http.HandlerFunc and calls cb if the request takes
// longer than max time to process.  The function runs to completion -- slow
// requests are not aborted.
func SlowHandler(fn http.HandlerFunc, max time.Duration, cb func(r *http.Request, t time.Duration)) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		t0 := time.Now()
		fn(w, req)
		t := time.Since(t0)
		if t > max {
			cb(req, t)
		}
	}
}

// TimeHandler wraps a http.HandlerFunc and calls cb with the duration.
func TimeHandler(fn http.HandlerFunc, cb func(r *http.Request, t time.Duration)) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		t0 := time.Now()
		fn(w, req)
		t := time.Since(t0)
		cb(req, t)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"unicode"
)

// A token is a chunk of text from a statement with a type
type token struct {
	text string
	typ  tokenTyp
}

// A tokenTyp identifies what kind of token something is
type tokenTyp int

const (
	// A bare word is a unquoted key; like 'foo' in json.foo = 1;
	typBare tokenTyp = iota

	// Numeric key; like '2' in json[2] = "foo";
	typNumericKey

	// A quoted key; like 'foo bar' in json["foo bar"] = 2;
	typQuotedKey

	// Punctuation types
	typDot    // .
	typLBrace // [
	typRBrace // ]
	typEquals // =
	typSemi   // ;
	typComma  // ,

	// Value types
	typString      // "foo"
	typNumber      // 4
	typTrue        // true
	typFalse       // false
	typNull        // null
	typEmptyArray  // []
	typEmptyObject // {}

	// Ignored token
	typIgnored

	// Error token
	typError
)

// a sprintFn adds color to its input
type sprintFn func(...interface{}) string

// mapping of token types to the appropriate color sprintFn
var sprintFns = map[tokenTyp]sprintFn{
	typBare:        bareColor.SprintFunc(),
	typNumericKey:  numColor.SprintFunc(),
	typQuotedKey:   strColor.SprintFunc(),
	typLBrace:      braceColor.SprintFunc(),
	typRBrace:      braceColor.SprintFunc(),
	typString:      strColor.SprintFunc(),
	typNumber:      numColor.SprintFunc(),
	typTrue:        boolColor.SprintFunc(),
	typFalse:       boolColor.SprintFunc(),
	typNull:        boolColor.SprintFunc(),
	typEmptyArray:  braceColor.SprintFunc(),
	typEmptyObject: braceColor.SprintFunc(),
}

// isValue returns true if the token is a valid value type
func (t token) isValue() bool {
	switch t.typ {
	case typString, typNumber, typTrue, typFalse, typNull, typEmptyArray, typEmptyObject:
		return true
	default:
		return false
	}
}

// isPunct returns true if the token is a punctuation type
func (t token) isPunct() bool {
	switch t.typ {
	case typDot, typLBrace, typRBrace, typEquals, typSemi, typComma:
		return true
	default:
		return false
	}
}

// format returns the formatted version of the token text
func (t token) format() string {
	if t.typ == typEquals {
		return " " + t.text + " "
	}
	return t.text
}

// formatColor returns the colored formatted version of the token text
func (t token) formatColor() string {
	text := t.text
	if t.typ == typEquals {
		text = " " + text + " "
	}
	fn, ok := sprintFns[t.typ]
	if ok {
		return fn(text)
	}
	return text

}

// valueTokenFromInterface takes any valid value and
// returns a value token to represent it
func valueTokenFromInterface(v interface{}) token {
	switch vv := v.(type) {

	case map[string]interface{}:
		return token{"{}", typEmptyObject}
	case []interface{}:
		return token{"[]", typEmptyArray}
	case json.Number:
		return token{vv.String(), typNumber}
	case string:
		return token{quoteString(vv), typString}
	case bool:
		if vv {
			return token{"true", typTrue}
		}
		return token{"false", typFalse}
	case nil:
		return token{"null", typNull}
	default:
		return token{"", typError}
	}
}

// quoteString takes a string and returns a quoted and
// escaped string valid for use in gron output
func quoteString(s string) string {

	out := &bytes.Buffer{}
	// bytes.Buffer never returns errors on these methods.
	// errors are explicitly ignored to keep the linter
	// happy. A price worth paying so that the linter
	// remains useful.
	_ = out.WriteByte('"')

	for _, r := range s {

		if r == '\\' || r == '"' {
			_ = out.WriteByte('\\')
			_, _ = out.WriteRune(r)
			continue
		}

		// \u2028 and \u2029 are separator runes that are not valid
		// in javascript strings so they must be escaped.
		// See http://timelessrepo.com/json-isnt-a-javascript-subset
		if r == '\u2028' {
			_, _ = out.WriteString(`\u2028`)
			continue
		}
		if r == '\u2029' {
			_, _ = out.WriteString(`\u2029`)
			continue
		}

		// Any other control runes must be escaped
		if unicode.IsControl(r) {

			switch r {
			case '\b':
				_ = out.WriteByte('\\')
				_ = out.WriteByte('b')
			case '\f':
				_ = out.WriteByte('\\')
				_ = out.WriteByte('f')
			case '\n':
				_ = out.WriteByte('\\')
				_ = out.WriteByte('n')
			case '\r':
				_ = out.WriteByte('\\')
				_ = out.WriteByte('r')
			case '\t':
				_ = out.WriteByte('\\')
				_ = out.WriteByte('t')
			default:
				_, _ = out.WriteString(fmt.Sprintf(`\u%04X`, r))
			}

			continue
		}

		// Unescaped rune
		_, _ = out.WriteRune(r)
	}

	_ = out.WriteByte('"')
	return out.String()

}
<commit_msg>simplify quoteString<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"unicode"
)

// A token is a chunk of text from a statement with a type
type token struct {
	text string
	typ  tokenTyp
}

// A tokenTyp identifies what kind of token something is
type tokenTyp int

const (
	// A bare word is a unquoted key; like 'foo' in json.foo = 1;
	typBare tokenTyp = iota

	// Numeric key; like '2' in json[2] = "foo";
	typNumericKey

	// A quoted key; like 'foo bar' in json["foo bar"] = 2;
	typQuotedKey

	// Punctuation types
	typDot    // .
	typLBrace // [
	typRBrace // ]
	typEquals // =
	typSemi   // ;
	typComma  // ,

	// Value types
	typString      // "foo"
	typNumber      // 4
	typTrue        // true
	typFalse       // false
	typNull        // null
	typEmptyArray  // []
	typEmptyObject // {}

	// Ignored token
	typIgnored

	// Error token
	typError
)

// a sprintFn adds color to its input
type sprintFn func(...interface{}) string

// mapping of token types to the appropriate color sprintFn
var sprintFns = map[tokenTyp]sprintFn{
	typBare:        bareColor.SprintFunc(),
	typNumericKey:  numColor.SprintFunc(),
	typQuotedKey:   strColor.SprintFunc(),
	typLBrace:      braceColor.SprintFunc(),
	typRBrace:      braceColor.SprintFunc(),
	typString:      strColor.SprintFunc(),
	typNumber:      numColor.SprintFunc(),
	typTrue:        boolColor.SprintFunc(),
	typFalse:       boolColor.SprintFunc(),
	typNull:        boolColor.SprintFunc(),
	typEmptyArray:  braceColor.SprintFunc(),
	typEmptyObject: braceColor.SprintFunc(),
}

// isValue returns true if the token is a valid value type
func (t token) isValue() bool {
	switch t.typ {
	case typString, typNumber, typTrue, typFalse, typNull, typEmptyArray, typEmptyObject:
		return true
	default:
		return false
	}
}

// isPunct returns true if the token is a punctuation type
func (t token) isPunct() bool {
	switch t.typ {
	case typDot, typLBrace, typRBrace, typEquals, typSemi, typComma:
		return true
	default:
		return false
	}
}

// format returns the formatted version of the token text
func (t token) format() string {
	if t.typ == typEquals {
		return " " + t.text + " "
	}
	return t.text
}

// formatColor returns the colored formatted version of the token text
func (t token) formatColor() string {
	text := t.text
	if t.typ == typEquals {
		text = " " + text + " "
	}
	fn, ok := sprintFns[t.typ]
	if ok {
		return fn(text)
	}
	return text

}

// valueTokenFromInterface takes any valid value and
// returns a value token to represent it
func valueTokenFromInterface(v interface{}) token {
	switch vv := v.(type) {

	case map[string]interface{}:
		return token{"{}", typEmptyObject}
	case []interface{}:
		return token{"[]", typEmptyArray}
	case json.Number:
		return token{vv.String(), typNumber}
	case string:
		return token{quoteString(vv), typString}
	case bool:
		if vv {
			return token{"true", typTrue}
		}
		return token{"false", typFalse}
	case nil:
		return token{"null", typNull}
	default:
		return token{"", typError}
	}
}

// quoteString takes a string and returns a quoted and
// escaped string valid for use in gron output
func quoteString(s string) string {

	out := &bytes.Buffer{}
	// bytes.Buffer never returns errors on these methods.
	// errors are explicitly ignored to keep the linter
	// happy. A price worth paying so that the linter
	// remains useful.
	_ = out.WriteByte('"')

	for _, r := range s {
		switch r {
		case '\\':
			_, _ = out.WriteString(`\\`)
		case '"':
			_, _ = out.WriteString(`\"`)
		case '\b':
			_, _ = out.WriteString(`\b`)
		case '\f':
			_, _ = out.WriteString(`\f`)
		case '\n':
			_, _ = out.WriteString(`\n`)
		case '\r':
			_, _ = out.WriteString(`\r`)
		case '\t':
			_, _ = out.WriteString(`\t`)
		// \u2028 and \u2029 are separator runes that are not valid
		// in javascript strings so they must be escaped.
		// See http://timelessrepo.com/json-isnt-a-javascript-subset
		case '\u2028':
			_, _ = out.WriteString(`\u2028`)
		case '\u2029':
			_, _ = out.WriteString(`\u2029`)
		default:
			// Any other control runes must be escaped
			if unicode.IsControl(r) {
				_, _ = fmt.Fprintf(out, `\u%04X`, r)
			} else {
				// Unescaped rune
				_, _ = out.WriteRune(r)
			}
		}
	}

	_ = out.WriteByte('"')
	return out.String()

}
<|endoftext|>


<commit_before>package reference

import (
	"fmt"
	"io"
	"strconv"
	"strings"
)

// symbolicRefPathStmt parse a following statement master~2
// to split branch name and refrence path
type symbolicRefPathStmt struct {
	branchName string
	refPath    []int
}

// parser represents a parser.
type parser struct {
	s   *scanner
	buf struct {
		tok token
		lit string
		n   int
	}
}

// newParser returns a new instance of parser.
func newParser(r io.Reader) *parser {
	return &parser{s: newScanner(r)}
}

// ParseSymbolicReferencePath creates a symbolicRefPathStmt
func (p *parser) parseSymbolicReferencePath() (*symbolicRefPathStmt, error) {
	branchName, err := p.parseBranchName()

	if err != nil {
		return nil, err
	}

	refPath, err := p.parseRefPath()

	if err != nil {
		return nil, err
	}

	return &symbolicRefPathStmt{
		branchName: branchName,
		refPath:    refPath,
	}, nil
}

// parseFuncArg parse one function argument
func (p *parser) parseBranchName() (string, error) {
	var tok token
	var previousTok token
	var lit string
	var buf string
	var subBuf string

	for {
		tok, lit = p.scan()

		if tok == dot && buf == "" {
			return "", fmt.Errorf("branch name must not start with a dot")
		}

		if tok == colon {
			return "", fmt.Errorf("branch name must not contains any colon")
		}

		if tok == slash {
			return "", fmt.Errorf("branch name must not end with a slash")
		}

		if tok == dot && previousTok == dot {
			return "", fmt.Errorf("branch name must not contains any double dot")
		}

		if tok == space {
			return "", fmt.Errorf("branch name contains a space character")
		}

		if tok == control {
			return "", fmt.Errorf("branch name contains a control character")
		}

		if len(buf) > 4 {
			subBuf = strings.ToLower(buf[len(buf)-5 : len(buf)])
		}

		if (tok == tilde || tok == caret || tok == eof) && subBuf == ".lock" {
			return "", fmt.Errorf("branch name cannot end with .lock")
		}

		if tok == tilde || tok == caret {
			p.unscan()

			return buf, nil
		}

		if tok == eof {
			return buf, nil
		}

		buf += lit
		previousTok = tok
	}
}

func (p *parser) parseRefPath() ([]int, error) {
	var tok token
	var buf = []int{}

	for {
		tok, _ = p.scan()

		if tok == caret {
			nTok, nLit := p.scan()

			if nTok != number {
				p.unscan()
				buf = append(buf, 1)
				continue
			}

			level, err := strconv.Atoi(nLit)

			if err != nil {
				return buf, err
			}

			if level != 2 && level != 1 {
				return buf, fmt.Errorf("level associated to a caret must be 1 or 2")
			}

			buf = append(buf, level)
		}

		if tok == tilde {
			nTok, nLit := p.scan()

			if nTok != number {
				p.unscan()
				buf = append(buf, 1)
				continue
			}

			level, err := strconv.Atoi(nLit)

			if err != nil {
				return buf, err
			}

			for i := 0; i < level; i++ {
				buf = append(buf, 1)
			}
		}

		if tok == eof {
			return buf, nil
		}

		if tok != caret && tok != tilde {
			return buf, fmt.Errorf("must be a caret or a tilde and may be followed with a number")
		}
	}
}

// scan returns the next token from the underlying scanner.
// If a token has been unscanned then read that instead.
func (p *parser) scan() (tok token, lit string) {
	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.tok, p.buf.lit
	}

	tok, lit = p.s.scan()

	p.buf.tok, p.buf.lit = tok, lit
	return
}

// unscan pushes the previously read token back onto the buffer.
func (p *parser) unscan() { p.buf.n = 1 }
<commit_msg>ref(reference) : should omit second index in slice<commit_after>package reference

import (
	"fmt"
	"io"
	"strconv"
	"strings"
)

// symbolicRefPathStmt parse a following statement master~2
// to split branch name and refrence path
type symbolicRefPathStmt struct {
	branchName string
	refPath    []int
}

// parser represents a parser.
type parser struct {
	s   *scanner
	buf struct {
		tok token
		lit string
		n   int
	}
}

// newParser returns a new instance of parser.
func newParser(r io.Reader) *parser {
	return &parser{s: newScanner(r)}
}

// ParseSymbolicReferencePath creates a symbolicRefPathStmt
func (p *parser) parseSymbolicReferencePath() (*symbolicRefPathStmt, error) {
	branchName, err := p.parseBranchName()

	if err != nil {
		return nil, err
	}

	refPath, err := p.parseRefPath()

	if err != nil {
		return nil, err
	}

	return &symbolicRefPathStmt{
		branchName: branchName,
		refPath:    refPath,
	}, nil
}

// parseFuncArg parse one function argument
func (p *parser) parseBranchName() (string, error) {
	var tok token
	var previousTok token
	var lit string
	var buf string
	var subBuf string

	for {
		tok, lit = p.scan()

		if tok == dot && buf == "" {
			return "", fmt.Errorf("branch name must not start with a dot")
		}

		if tok == colon {
			return "", fmt.Errorf("branch name must not contains any colon")
		}

		if tok == slash {
			return "", fmt.Errorf("branch name must not end with a slash")
		}

		if tok == dot && previousTok == dot {
			return "", fmt.Errorf("branch name must not contains any double dot")
		}

		if tok == space {
			return "", fmt.Errorf("branch name contains a space character")
		}

		if tok == control {
			return "", fmt.Errorf("branch name contains a control character")
		}

		if len(buf) > 4 {
			subBuf = strings.ToLower(buf[len(buf)-5:])
		}

		if (tok == tilde || tok == caret || tok == eof) && subBuf == ".lock" {
			return "", fmt.Errorf("branch name cannot end with .lock")
		}

		if tok == tilde || tok == caret {
			p.unscan()

			return buf, nil
		}

		if tok == eof {
			return buf, nil
		}

		buf += lit
		previousTok = tok
	}
}

func (p *parser) parseRefPath() ([]int, error) {
	var tok token
	var buf = []int{}

	for {
		tok, _ = p.scan()

		if tok == caret {
			nTok, nLit := p.scan()

			if nTok != number {
				p.unscan()
				buf = append(buf, 1)
				continue
			}

			level, err := strconv.Atoi(nLit)

			if err != nil {
				return buf, err
			}

			if level != 2 && level != 1 {
				return buf, fmt.Errorf("level associated to a caret must be 1 or 2")
			}

			buf = append(buf, level)
		}

		if tok == tilde {
			nTok, nLit := p.scan()

			if nTok != number {
				p.unscan()
				buf = append(buf, 1)
				continue
			}

			level, err := strconv.Atoi(nLit)

			if err != nil {
				return buf, err
			}

			for i := 0; i < level; i++ {
				buf = append(buf, 1)
			}
		}

		if tok == eof {
			return buf, nil
		}

		if tok != caret && tok != tilde {
			return buf, fmt.Errorf("must be a caret or a tilde and may be followed with a number")
		}
	}
}

// scan returns the next token from the underlying scanner.
// If a token has been unscanned then read that instead.
func (p *parser) scan() (tok token, lit string) {
	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.tok, p.buf.lit
	}

	tok, lit = p.s.scan()

	p.buf.tok, p.buf.lit = tok, lit
	return
}

// unscan pushes the previously read token back onto the buffer.
func (p *parser) unscan() { p.buf.n = 1 }
<|endoftext|>


<commit_before>// Copyright 2021 The Oto 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.

//go:build !android && !darwin && !js && !windows
// +build !android,!darwin,!js,!windows

package oto

// #cgo pkg-config: alsa
//
// #include <alsa/asoundlib.h>
import "C"

import (
	"fmt"
	"strings"
	"sync"
	"time"
	"unsafe"
)

type context struct {
	sampleRate      int
	channelNum      int
	bitDepthInBytes int

	suspended bool

	handle        *C.snd_pcm_t
	supportsPause bool

	cond *sync.Cond

	players *players
	err     atomicError
}

var theContext *context

func alsaError(name string, err C.int) error {
	return fmt.Errorf("oto: ALSA error at %s: %s", name, C.GoString(C.snd_strerror(err)))
}

func deviceCandidates() []string {
	const getAllDevices = -1

	cPCMInterfaceName := C.CString("pcm")
	defer C.free(unsafe.Pointer(cPCMInterfaceName))

	var hints *unsafe.Pointer
	err := C.snd_device_name_hint(getAllDevices, cPCMInterfaceName, &hints)
	if err != 0 {
		return []string{"default"}
	}
	defer C.snd_device_name_free_hint(hints)

	var devices []string

	cIoHintName := C.CString("IOID")
	defer C.free(unsafe.Pointer(cIoHintName))
	cNameHintName := C.CString("NAME")
	defer C.free(unsafe.Pointer(cNameHintName))

	for it := hints; *it != nil; it = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(it)) + unsafe.Sizeof(uintptr(0)))) {
		io := C.snd_device_name_get_hint(*it, cIoHintName)
		defer func() {
			if io != nil {
				C.free(unsafe.Pointer(io))
			}
		}()
		if C.GoString(io) == "Input" {
			continue
		}

		name := C.snd_device_name_get_hint(*it, cNameHintName)
		defer func() {
			if name != nil {
				C.free(unsafe.Pointer(name))
			}
		}()
		if name == nil {
			continue
		}
		goName := C.GoString(name)
		if goName == "null" {
			continue
		}
		if goName == "default" {
			continue
		}
		devices = append(devices, goName)
	}

	devices = append([]string{"default"}, devices...)

	return devices
}

func newContext(sampleRate, channelNum, bitDepthInBytes int) (*context, chan struct{}, error) {
	ready := make(chan struct{})
	close(ready)

	c := &context{
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
		cond:            sync.NewCond(&sync.Mutex{}),
		players:         newPlayers(),
	}
	theContext = c

	// Open a default ALSA audio device for blocking stream playback
	type openError struct {
		device string
		err    C.int
	}
	var openErrs []openError
	var found bool

	for _, name := range deviceCandidates() {
		cname := C.CString(name)
		defer C.free(unsafe.Pointer(cname))
		if err := C.snd_pcm_open(&c.handle, cname, C.SND_PCM_STREAM_PLAYBACK, 0); err < 0 {
			openErrs = append(openErrs, openError{
				device: name,
				err:    err,
			})
			continue
		}
		found = true
		break
	}
	if !found {
		var msgs []string
		for _, e := range openErrs {
			msgs = append(msgs, fmt.Sprintf("%q: %s", e.device, C.GoString(C.snd_strerror(e.err))))
		}
		return nil, nil, fmt.Errorf("oto: ALSA error at snd_pcm_open: %s", strings.Join(msgs, ", "))
	}

	periodSize := C.snd_pcm_uframes_t(1024)
	bufferSize := periodSize * 2
	if err := c.alsaPcmHwParams(sampleRate, channelNum, &bufferSize, &periodSize); err != nil {
		return nil, nil, err
	}

	go func() {
		buf32 := make([]float32, int(periodSize)*c.channelNum)
		for {
			if !c.readAndWrite(buf32) {
				return
			}
		}
	}()

	return c, ready, nil
}

func (c *context) alsaPcmHwParams(sampleRate, channelNum int, bufferSize, periodSize *C.snd_pcm_uframes_t) error {
	var params *C.snd_pcm_hw_params_t
	C.snd_pcm_hw_params_malloc(&params)
	defer C.free(unsafe.Pointer(params))

	if err := C.snd_pcm_hw_params_any(c.handle, params); err < 0 {
		return alsaError("snd_pcm_hw_params_any", err)
	}
	if err := C.snd_pcm_hw_params_set_access(c.handle, params, C.SND_PCM_ACCESS_RW_INTERLEAVED); err < 0 {
		return alsaError("snd_pcm_hw_params_set_access", err)
	}
	if err := C.snd_pcm_hw_params_set_format(c.handle, params, C.SND_PCM_FORMAT_FLOAT_LE); err < 0 {
		return alsaError("snd_pcm_hw_params_set_format", err)
	}
	if err := C.snd_pcm_hw_params_set_channels(c.handle, params, C.unsigned(channelNum)); err < 0 {
		return alsaError("snd_pcm_hw_params_set_channels", err)
	}
	if err := C.snd_pcm_hw_params_set_rate_resample(c.handle, params, 1); err < 0 {
		return alsaError("snd_pcm_hw_params_set_rate_resample", err)
	}
	sr := C.unsigned(sampleRate)
	if err := C.snd_pcm_hw_params_set_rate_near(c.handle, params, &sr, nil); err < 0 {
		return alsaError("snd_pcm_hw_params_set_rate_near", err)
	}
	if err := C.snd_pcm_hw_params_set_buffer_size_near(c.handle, params, bufferSize); err < 0 {
		return alsaError("snd_pcm_hw_params_set_buffer_size_near", err)
	}
	if err := C.snd_pcm_hw_params_set_period_size_near(c.handle, params, periodSize, nil); err < 0 {
		return alsaError("snd_pcm_hw_params_set_period_size_near", err)
	}
	if err := C.snd_pcm_hw_params(c.handle, params); err < 0 {
		return alsaError("snd_pcm_hw_params", err)
	}
	c.supportsPause = C.snd_pcm_hw_params_can_pause(params) == 1
	return nil
}

func (c *context) readAndWrite(buf32 []float32) bool {
	c.cond.L.Lock()
	defer c.cond.L.Unlock()

	for c.suspended && c.err.Load() == nil {
		c.cond.Wait()
	}
	if c.err.Load() != nil {
		return false
	}

	c.players.read(buf32)

	for len(buf32) > 0 {
		n := C.snd_pcm_writei(c.handle, unsafe.Pointer(&buf32[0]), C.snd_pcm_uframes_t(len(buf32)/c.channelNum))
		if n < 0 {
			n = C.long(C.snd_pcm_recover(c.handle, C.int(n), 1))
		}
		if n < 0 {
			c.err.TryStore(alsaError("snd_pcm_writei or snd_pcm_recover", C.int(n)))
			return false
		}
		buf32 = buf32[int(n)*c.channelNum:]
	}
	return true
}

func (c *context) Suspend() error {
	c.cond.L.Lock()
	defer c.cond.L.Unlock()

	if err := c.err.Load(); err != nil {
		return err.(error)
	}

	c.suspended = true
	if c.supportsPause {
		if err := C.snd_pcm_pause(c.handle, 1); err < 0 {
			return alsaError("snd_pcm_pause", err)
		}
		return nil
	}

	if err := C.snd_pcm_drop(c.handle); err < 0 {
		return alsaError("snd_pcm_drop", err)
	}
	return nil
}

func (c *context) Resume() error {
	c.cond.L.Lock()
	defer c.cond.L.Unlock()

	if err := c.err.Load(); err != nil {
		return err.(error)
	}

	defer func() {
		c.suspended = false
		c.cond.Signal()
	}()

	if c.supportsPause {
		if err := C.snd_pcm_pause(c.handle, 0); err < 0 {
			return alsaError("snd_pcm_pause", err)
		}
		return nil
	}

try:
	if err := C.snd_pcm_resume(c.handle); err < 0 {
		if err == -C.EAGAIN {
			time.Sleep(100 * time.Millisecond)
			goto try
		}
		if err == -C.ENOSYS {
			if err := C.snd_pcm_prepare(c.handle); err < 0 {
				return alsaError("snd_pcm_prepare", err)
			}
			return nil
		}
		return alsaError("snd_pcm_resume", err)
	}
	return nil
}

func (c *context) Err() error {
	if err := c.err.Load(); err != nil {
		return err.(error)
	}
	return nil
}
<commit_msg>unix: add plug:default as a candidate<commit_after>// Copyright 2021 The Oto 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.

//go:build !android && !darwin && !js && !windows
// +build !android,!darwin,!js,!windows

package oto

// #cgo pkg-config: alsa
//
// #include <alsa/asoundlib.h>
import "C"

import (
	"fmt"
	"strings"
	"sync"
	"time"
	"unsafe"
)

type context struct {
	sampleRate      int
	channelNum      int
	bitDepthInBytes int

	suspended bool

	handle        *C.snd_pcm_t
	supportsPause bool

	cond *sync.Cond

	players *players
	err     atomicError
}

var theContext *context

func alsaError(name string, err C.int) error {
	return fmt.Errorf("oto: ALSA error at %s: %s", name, C.GoString(C.snd_strerror(err)))
}

func deviceCandidates() []string {
	const getAllDevices = -1

	cPCMInterfaceName := C.CString("pcm")
	defer C.free(unsafe.Pointer(cPCMInterfaceName))

	var hints *unsafe.Pointer
	err := C.snd_device_name_hint(getAllDevices, cPCMInterfaceName, &hints)
	if err != 0 {
		return []string{"default", "plug:default"}
	}
	defer C.snd_device_name_free_hint(hints)

	var devices []string

	cIoHintName := C.CString("IOID")
	defer C.free(unsafe.Pointer(cIoHintName))
	cNameHintName := C.CString("NAME")
	defer C.free(unsafe.Pointer(cNameHintName))

	for it := hints; *it != nil; it = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(it)) + unsafe.Sizeof(uintptr(0)))) {
		io := C.snd_device_name_get_hint(*it, cIoHintName)
		defer func() {
			if io != nil {
				C.free(unsafe.Pointer(io))
			}
		}()
		if C.GoString(io) == "Input" {
			continue
		}

		name := C.snd_device_name_get_hint(*it, cNameHintName)
		defer func() {
			if name != nil {
				C.free(unsafe.Pointer(name))
			}
		}()
		if name == nil {
			continue
		}
		goName := C.GoString(name)
		if goName == "null" {
			continue
		}
		if goName == "default" {
			continue
		}
		devices = append(devices, goName)
	}

	devices = append([]string{"default", "plug:default"}, devices...)

	return devices
}

func newContext(sampleRate, channelNum, bitDepthInBytes int) (*context, chan struct{}, error) {
	ready := make(chan struct{})
	close(ready)

	c := &context{
		sampleRate:      sampleRate,
		channelNum:      channelNum,
		bitDepthInBytes: bitDepthInBytes,
		cond:            sync.NewCond(&sync.Mutex{}),
		players:         newPlayers(),
	}
	theContext = c

	// Open a default ALSA audio device for blocking stream playback
	type openError struct {
		device string
		err    C.int
	}
	var openErrs []openError
	var found bool

	for _, name := range deviceCandidates() {
		cname := C.CString(name)
		defer C.free(unsafe.Pointer(cname))
		if err := C.snd_pcm_open(&c.handle, cname, C.SND_PCM_STREAM_PLAYBACK, 0); err < 0 {
			openErrs = append(openErrs, openError{
				device: name,
				err:    err,
			})
			continue
		}
		found = true
		break
	}
	if !found {
		var msgs []string
		for _, e := range openErrs {
			msgs = append(msgs, fmt.Sprintf("%q: %s", e.device, C.GoString(C.snd_strerror(e.err))))
		}
		return nil, nil, fmt.Errorf("oto: ALSA error at snd_pcm_open: %s", strings.Join(msgs, ", "))
	}

	periodSize := C.snd_pcm_uframes_t(1024)
	bufferSize := periodSize * 2
	if err := c.alsaPcmHwParams(sampleRate, channelNum, &bufferSize, &periodSize); err != nil {
		return nil, nil, err
	}

	go func() {
		buf32 := make([]float32, int(periodSize)*c.channelNum)
		for {
			if !c.readAndWrite(buf32) {
				return
			}
		}
	}()

	return c, ready, nil
}

func (c *context) alsaPcmHwParams(sampleRate, channelNum int, bufferSize, periodSize *C.snd_pcm_uframes_t) error {
	var params *C.snd_pcm_hw_params_t
	C.snd_pcm_hw_params_malloc(&params)
	defer C.free(unsafe.Pointer(params))

	if err := C.snd_pcm_hw_params_any(c.handle, params); err < 0 {
		return alsaError("snd_pcm_hw_params_any", err)
	}
	if err := C.snd_pcm_hw_params_set_access(c.handle, params, C.SND_PCM_ACCESS_RW_INTERLEAVED); err < 0 {
		return alsaError("snd_pcm_hw_params_set_access", err)
	}
	if err := C.snd_pcm_hw_params_set_format(c.handle, params, C.SND_PCM_FORMAT_FLOAT_LE); err < 0 {
		return alsaError("snd_pcm_hw_params_set_format", err)
	}
	if err := C.snd_pcm_hw_params_set_channels(c.handle, params, C.unsigned(channelNum)); err < 0 {
		return alsaError("snd_pcm_hw_params_set_channels", err)
	}
	if err := C.snd_pcm_hw_params_set_rate_resample(c.handle, params, 1); err < 0 {
		return alsaError("snd_pcm_hw_params_set_rate_resample", err)
	}
	sr := C.unsigned(sampleRate)
	if err := C.snd_pcm_hw_params_set_rate_near(c.handle, params, &sr, nil); err < 0 {
		return alsaError("snd_pcm_hw_params_set_rate_near", err)
	}
	if err := C.snd_pcm_hw_params_set_buffer_size_near(c.handle, params, bufferSize); err < 0 {
		return alsaError("snd_pcm_hw_params_set_buffer_size_near", err)
	}
	if err := C.snd_pcm_hw_params_set_period_size_near(c.handle, params, periodSize, nil); err < 0 {
		return alsaError("snd_pcm_hw_params_set_period_size_near", err)
	}
	if err := C.snd_pcm_hw_params(c.handle, params); err < 0 {
		return alsaError("snd_pcm_hw_params", err)
	}
	c.supportsPause = C.snd_pcm_hw_params_can_pause(params) == 1
	return nil
}

func (c *context) readAndWrite(buf32 []float32) bool {
	c.cond.L.Lock()
	defer c.cond.L.Unlock()

	for c.suspended && c.err.Load() == nil {
		c.cond.Wait()
	}
	if c.err.Load() != nil {
		return false
	}

	c.players.read(buf32)

	for len(buf32) > 0 {
		n := C.snd_pcm_writei(c.handle, unsafe.Pointer(&buf32[0]), C.snd_pcm_uframes_t(len(buf32)/c.channelNum))
		if n < 0 {
			n = C.long(C.snd_pcm_recover(c.handle, C.int(n), 1))
		}
		if n < 0 {
			c.err.TryStore(alsaError("snd_pcm_writei or snd_pcm_recover", C.int(n)))
			return false
		}
		buf32 = buf32[int(n)*c.channelNum:]
	}
	return true
}

func (c *context) Suspend() error {
	c.cond.L.Lock()
	defer c.cond.L.Unlock()

	if err := c.err.Load(); err != nil {
		return err.(error)
	}

	c.suspended = true
	if c.supportsPause {
		if err := C.snd_pcm_pause(c.handle, 1); err < 0 {
			return alsaError("snd_pcm_pause", err)
		}
		return nil
	}

	if err := C.snd_pcm_drop(c.handle); err < 0 {
		return alsaError("snd_pcm_drop", err)
	}
	return nil
}

func (c *context) Resume() error {
	c.cond.L.Lock()
	defer c.cond.L.Unlock()

	if err := c.err.Load(); err != nil {
		return err.(error)
	}

	defer func() {
		c.suspended = false
		c.cond.Signal()
	}()

	if c.supportsPause {
		if err := C.snd_pcm_pause(c.handle, 0); err < 0 {
			return alsaError("snd_pcm_pause", err)
		}
		return nil
	}

try:
	if err := C.snd_pcm_resume(c.handle); err < 0 {
		if err == -C.EAGAIN {
			time.Sleep(100 * time.Millisecond)
			goto try
		}
		if err == -C.ENOSYS {
			if err := C.snd_pcm_prepare(c.handle); err < 0 {
				return alsaError("snd_pcm_prepare", err)
			}
			return nil
		}
		return alsaError("snd_pcm_resume", err)
	}
	return nil
}

func (c *context) Err() error {
	if err := c.err.Load(); err != nil {
		return err.(error)
	}
	return nil
}
<|endoftext|>


<commit_before>package gopx

import "testing"

func TestParse(t *testing.T) {
	var err error
	var g *Gpx
	g, err = ParseFile("sample/sample.gpx")

	if err != nil {
		t.Error("Error parsing GPX file: ", err)
	}

	if g == nil {
		t.Error("Expected new Gpx")
	}
}

func TestParseMetadata(t *testing.T) {
	var g *Gpx
	g, _ = ParseFile("sample/sample.gpx")

	timestampA := g.Metadata.Timestamp
	timestampE := "2016-12-23T15:12:25Z"
	if timestampA != timestampE {
		t.Errorf("timestamp expected: %s, actual: %s", timestampE, timestampA)
	}
}

func TestParseTracks(t *testing.T) {
	var g *Gpx
	g, _ = ParseFile("sample/sample.gpx")

	tracknameA := g.Tracks[0].Name
	tracknameE := "Werk-woon"
	if tracknameA != tracknameE {
		t.Errorf("Trackname expected: %s, actual: %s", tracknameE, tracknameA)
	}
}

func TestParsePoints(t *testing.T) {
	var g *Gpx
	g, _ = ParseFile("sample/sample.gpx")

	wayEleA := g.Tracks[0].Segments[0].Points[0].Elevation
	wayEleE := -2.3
	if wayEleE != wayEleA {
		t.Errorf("Number of tracks expected: %f, actual: %f", wayEleE, wayEleA)
	}
}
<commit_msg>Added unittest<commit_after>package gopx

import "testing"

func TestParse(t *testing.T) {
	var err error
	var g *Gpx
	g, err = ParseFile("sample/sample.gpx")

	if err != nil {
		t.Error("Error parsing GPX file: ", err)
	}

	if g == nil {
		t.Error("Expected new Gpx")
	}
}

func TestParseMetadata(t *testing.T) {
	var g *Gpx
	g, _ = ParseFile("sample/sample.gpx")

	timestampA := g.Metadata.Timestamp
	timestampE := "2016-12-23T15:12:25Z"
	if timestampA != timestampE {
		t.Errorf("timestamp expected: %s, actual: %s", timestampE, timestampA)
	}
}

func TestParseTracks(t *testing.T) {
	var g *Gpx
	g, _ = ParseFile("sample/sample.gpx")

	tracknameA := g.Tracks[0].Name
	tracknameE := "Werk-woon"
	if tracknameA != tracknameE {
		t.Errorf("Trackname expected: %s, actual: %s", tracknameE, tracknameA)
	}
}

func TestParsePoints(t *testing.T) {
	var g *Gpx
	g, _ = ParseFile("sample/sample.gpx")

	wayEleA := g.Tracks[0].Segments[0].Points[0].Elevation
	wayEleE := -2.3
	if wayEleE != wayEleA {
		t.Errorf("Number of tracks expected: %f, actual: %f", wayEleE, wayEleA)
	}
}

func TestParseInvalidFile(t *testing.T) {
	var errActual error
	_, errActual = ParseFile("sample/does_not_exists.gpx")

	errExpected := "open sample/does_not_exists.gpx: no such file or directory"
	if errActual.Error() != errExpected {
		t.Errorf("Expected error: %s but got: %s", errExpected, errActual)
	}
}
<|endoftext|>


<commit_before>// Package fs provides file system handleFuncs that can be used with kite
// library
package fs

import (
	"errors"
	"log"
	"os"
	"path"
	"sync"

	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite"
	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite/dnode"
	"github.com/koding/klient/Godeps/_workspace/src/gopkg.in/fsnotify.v1"
)

var (
	once sync. // watcher variables
	Once
	newPaths, oldPaths = make(chan string), make(chan string)

	// Limit of watching folders
	watchCallbacks = make(map[string]func(fsnotify.Event), 100)
	mu             sync.Mutex // protects watchCallbacks
)

func ReadDirectory(r *kite.Request) (interface{}, error) {
	var params struct {
		Path     string
		OnChange dnode.Function
	}

	if r.Args == nil {
		return nil, errors.New("arguments are not passed")
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		log.Println("params", params)
		return nil, errors.New("{ path: [string], onChange: [function]}")
	}

	response := make(map[string]interface{})

	if params.OnChange.IsValid() {
		onceBody := func() { startWatcher() }
		go once.Do(onceBody)

		var eventType string
		var fileEntry *FileEntry

		changer := func(ev fsnotify.Event) {
			switch ev.Op {
			case fsnotify.Create:
				eventType = "added"
				fileEntry, _ = getInfo(ev.Name)
			case fsnotify.Remove, fsnotify.Rename:
				eventType = "removed"
				fileEntry = NewFileEntry(path.Base(ev.Name), ev.Name)
			}

			event := map[string]interface{}{
				"event": eventType,
				"file":  fileEntry,
			}

			// send back the result to the client
			params.OnChange.Call(event)
			return
		}

		mu.Lock()
		_, ok := watchCallbacks[params.Path]
		if !ok {
			// notify new paths to the watcher
			newPaths <- params.Path
			watchCallbacks[params.Path] = changer
		}
		mu.Unlock()

		removePath := func() {
			mu.Lock()
			delete(watchCallbacks, params.Path)
			mu.Unlock()

			oldPaths <- params.Path
		}

		// remove the path when the remote client disconnects
		r.Client.OnDisconnect(removePath)

		// this callback is called whenever we receive a 'stopWatching' from the client
		response["stopWatching"] = dnode.Callback(func(r *dnode.Partial) {
			removePath()
		})
	}

	files, err := readDirectory(params.Path)
	if err != nil {
		return nil, err
	}

	response["files"] = files
	return response, nil
}

func startWatcher() {
	var err error
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		for {
			select {
			case p := <-newPaths:
				err := watcher.Add(p)
				if err != nil {
					log.Println("watch path adding", err)
				}
			case p := <-oldPaths:
				err := watcher.Remove(p)
				if err != nil {
					log.Println("watch remove adding", err)
				}
			}
		}
	}()

	for {
		select {
		case event := <-watcher.Events:

			mu.Lock()
			f, ok := watchCallbacks[path.Dir(event.Name)]
			mu.Unlock()

			if !ok {
				continue
			}

			f(event)

		case err := <-watcher.Errors:
			log.Println("watcher error:", err)
		}
	}

}

func Glob(r *kite.Request) (interface{}, error) {
	var params struct {
		Pattern string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Pattern == "" {
		return nil, errors.New("{ pattern: [string] }")
	}

	return glob(params.Pattern)
}

func ReadFile(r *kite.Request) (interface{}, error) {
	var params struct {
		Path string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string] }")
	}

	return readFile(params.Path)
}

type writeFileParams struct {
	Path           string
	Content        []byte
	DoNotOverwrite bool
	Append         bool
}

func WriteFile(r *kite.Request) (interface{}, error) {
	var params writeFileParams
	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string] }")
	}

	return writeFile(params.Path, params.Content, params.DoNotOverwrite, params.Append)
}

func UniquePath(r *kite.Request) (interface{}, error) {
	var params struct {
		Path string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string] }")
	}

	return uniquePath(params.Path)
}

func GetInfo(r *kite.Request) (interface{}, error) {
	var params struct {
		Path string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string] }")
	}

	return getInfo(params.Path)
}

type setPermissionsParams struct {
	Path      string
	Mode      os.FileMode
	Recursive bool
}

func SetPermissions(r *kite.Request) (interface{}, error) {
	var params setPermissionsParams

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string], mode: [integer], recursive: [bool] }")
	}

	err := setPermissions(params.Path, params.Mode, params.Recursive)
	if err != nil {
		return nil, err
	}

	return true, nil
}

func Remove(r *kite.Request) (interface{}, error) {
	var params struct {
		Path      string
		Recursive bool
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string], recursive: [bool] }")
	}

	if err := remove(params.Path, params.Recursive); err != nil {
		return nil, err
	}

	return true, nil
}

func Rename(r *kite.Request) (interface{}, error) {
	var params struct {
		OldPath string
		NewPath string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.OldPath == "" || params.NewPath == "" {
		return nil, errors.New("{ oldPath: [string], newPath: [string] }")
	}

	err := rename(params.OldPath, params.NewPath)
	if err != nil {
		return nil, err
	}

	return true, nil
}

func CreateDirectory(r *kite.Request) (interface{}, error) {
	var params struct {
		Path      string
		Recursive bool
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string], recursive: [bool] }")
	}

	err := createDirectory(params.Path, params.Recursive)
	if err != nil {
		return nil, err
	}

	return true, nil
}

func Move(r *kite.Request) (interface{}, error) {
	var params struct {
		OldPath string
		NewPath string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.OldPath == "" || params.NewPath == "" {
		return nil, errors.New("{ oldPath: [string], newPath: [string] }")
	}

	err := rename(params.OldPath, params.NewPath)
	if err != nil {
		return nil, err
	}

	return true, nil
}

func Copy(r *kite.Request) (interface{}, error) {
	var params struct {
		SrcPath string
		DstPath string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.SrcPath == "" || params.DstPath == "" {
		return nil, errors.New("{ srcPath: [string], dstPath: [string] }")
	}

	err := cp(params.SrcPath, params.DstPath)
	if err != nil {
		return nil, err
	}

	return true, nil
}
<commit_msg>fs: fix indentation<commit_after>// Package fs provides file system handleFuncs that can be used with kite
// library
package fs

import (
	"errors"
	"log"
	"os"
	"path"
	"sync"

	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite"
	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite/dnode"
	"github.com/koding/klient/Godeps/_workspace/src/gopkg.in/fsnotify.v1"
)

var (
	once               sync.Once // watcher variables
	newPaths, oldPaths = make(chan string), make(chan string)

	// Limit of watching folders
	watchCallbacks = make(map[string]func(fsnotify.Event), 100)
	mu             sync.Mutex // protects watchCallbacks
)

func ReadDirectory(r *kite.Request) (interface{}, error) {
	var params struct {
		Path     string
		OnChange dnode.Function
	}

	if r.Args == nil {
		return nil, errors.New("arguments are not passed")
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		log.Println("params", params)
		return nil, errors.New("{ path: [string], onChange: [function]}")
	}

	response := make(map[string]interface{})

	if params.OnChange.IsValid() {
		onceBody := func() { startWatcher() }
		go once.Do(onceBody)

		var eventType string
		var fileEntry *FileEntry

		changer := func(ev fsnotify.Event) {
			switch ev.Op {
			case fsnotify.Create:
				eventType = "added"
				fileEntry, _ = getInfo(ev.Name)
			case fsnotify.Remove, fsnotify.Rename:
				eventType = "removed"
				fileEntry = NewFileEntry(path.Base(ev.Name), ev.Name)
			}

			event := map[string]interface{}{
				"event": eventType,
				"file":  fileEntry,
			}

			// send back the result to the client
			params.OnChange.Call(event)
			return
		}

		mu.Lock()
		_, ok := watchCallbacks[params.Path]
		if !ok {
			// notify new paths to the watcher
			newPaths <- params.Path
			watchCallbacks[params.Path] = changer
		}
		mu.Unlock()

		removePath := func() {
			mu.Lock()
			delete(watchCallbacks, params.Path)
			mu.Unlock()

			oldPaths <- params.Path
		}

		// remove the path when the remote client disconnects
		r.Client.OnDisconnect(removePath)

		// this callback is called whenever we receive a 'stopWatching' from the client
		response["stopWatching"] = dnode.Callback(func(r *dnode.Partial) {
			removePath()
		})
	}

	files, err := readDirectory(params.Path)
	if err != nil {
		return nil, err
	}

	response["files"] = files
	return response, nil
}

func startWatcher() {
	var err error
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		for {
			select {
			case p := <-newPaths:
				err := watcher.Add(p)
				if err != nil {
					log.Println("watch path adding", err)
				}
			case p := <-oldPaths:
				err := watcher.Remove(p)
				if err != nil {
					log.Println("watch remove adding", err)
				}
			}
		}
	}()

	for {
		select {
		case event := <-watcher.Events:

			mu.Lock()
			f, ok := watchCallbacks[path.Dir(event.Name)]
			mu.Unlock()

			if !ok {
				continue
			}

			f(event)

		case err := <-watcher.Errors:
			log.Println("watcher error:", err)
		}
	}

}

func Glob(r *kite.Request) (interface{}, error) {
	var params struct {
		Pattern string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Pattern == "" {
		return nil, errors.New("{ pattern: [string] }")
	}

	return glob(params.Pattern)
}

func ReadFile(r *kite.Request) (interface{}, error) {
	var params struct {
		Path string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string] }")
	}

	return readFile(params.Path)
}

type writeFileParams struct {
	Path           string
	Content        []byte
	DoNotOverwrite bool
	Append         bool
}

func WriteFile(r *kite.Request) (interface{}, error) {
	var params writeFileParams
	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string] }")
	}

	return writeFile(params.Path, params.Content, params.DoNotOverwrite, params.Append)
}

func UniquePath(r *kite.Request) (interface{}, error) {
	var params struct {
		Path string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string] }")
	}

	return uniquePath(params.Path)
}

func GetInfo(r *kite.Request) (interface{}, error) {
	var params struct {
		Path string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string] }")
	}

	return getInfo(params.Path)
}

type setPermissionsParams struct {
	Path      string
	Mode      os.FileMode
	Recursive bool
}

func SetPermissions(r *kite.Request) (interface{}, error) {
	var params setPermissionsParams

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string], mode: [integer], recursive: [bool] }")
	}

	err := setPermissions(params.Path, params.Mode, params.Recursive)
	if err != nil {
		return nil, err
	}

	return true, nil
}

func Remove(r *kite.Request) (interface{}, error) {
	var params struct {
		Path      string
		Recursive bool
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string], recursive: [bool] }")
	}

	if err := remove(params.Path, params.Recursive); err != nil {
		return nil, err
	}

	return true, nil
}

func Rename(r *kite.Request) (interface{}, error) {
	var params struct {
		OldPath string
		NewPath string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.OldPath == "" || params.NewPath == "" {
		return nil, errors.New("{ oldPath: [string], newPath: [string] }")
	}

	err := rename(params.OldPath, params.NewPath)
	if err != nil {
		return nil, err
	}

	return true, nil
}

func CreateDirectory(r *kite.Request) (interface{}, error) {
	var params struct {
		Path      string
		Recursive bool
	}

	if r.Args.One().Unmarshal(&params) != nil || params.Path == "" {
		return nil, errors.New("{ path: [string], recursive: [bool] }")
	}

	err := createDirectory(params.Path, params.Recursive)
	if err != nil {
		return nil, err
	}

	return true, nil
}

func Move(r *kite.Request) (interface{}, error) {
	var params struct {
		OldPath string
		NewPath string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.OldPath == "" || params.NewPath == "" {
		return nil, errors.New("{ oldPath: [string], newPath: [string] }")
	}

	err := rename(params.OldPath, params.NewPath)
	if err != nil {
		return nil, err
	}

	return true, nil
}

func Copy(r *kite.Request) (interface{}, error) {
	var params struct {
		SrcPath string
		DstPath string
	}

	if r.Args.One().Unmarshal(&params) != nil || params.SrcPath == "" || params.DstPath == "" {
		return nil, errors.New("{ srcPath: [string], dstPath: [string] }")
	}

	err := cp(params.SrcPath, params.DstPath)
	if err != nil {
		return nil, err
	}

	return true, nil
}
<|endoftext|>


<commit_before>package replicaset

import (
	"fmt"
	"testing"
	"time"

	jc "github.com/juju/testing/checkers"
	"labix.org/v2/mgo"
	gc "launchpad.net/gocheck"

	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/utils"
)

const rsName = "juju"

func TestPackage(t *testing.T) {
	gc.TestingT(t)
}

type MongoSuite struct {
	coretesting.BaseSuite
	root *coretesting.MgoInstance
}

func newServer(c *gc.C) *coretesting.MgoInstance {
	inst := &coretesting.MgoInstance{Params: []string{"--replSet", rsName}}
	err := inst.Start(true)
	c.Assert(err, gc.IsNil)

	session, err := inst.DialDirect()
	if err != nil {
		inst.Destroy()
		c.Fatalf("error dialing mongo server: %v", err.Error())
	}
	defer session.Close()

	session.SetMode(mgo.Monotonic, true)
	if err = session.Ping(); err != nil {
		inst.Destroy()
		c.Fatalf("error pinging mongo server: %v", err.Error())
	}
	return inst
}

var _ = gc.Suite(&MongoSuite{})

func (s *MongoSuite) SetUpTest(c *gc.C) {
	s.BaseSuite.SetUpTest(c)
	s.root = newServer(c)
	s.dialAndTestInitiate(c)
}

func (s *MongoSuite) TearDownTest(c *gc.C) {
	s.root.Destroy()
	s.BaseSuite.TearDownTest(c)
}

var initialTags = map[string]string{"foo": "bar"}

func (s *MongoSuite) dialAndTestInitiate(c *gc.C) {
	session := s.root.MustDialDirect()
	defer session.Close()

	mode := session.Mode()
	err := Initiate(session, s.root.Addr(), rsName, initialTags)
	c.Assert(err, gc.IsNil)

	// make sure we haven't messed with the session's mode
	c.Assert(session.Mode(), gc.Equals, mode)

	// Ids start at 1 for us, so we can differentiate between set and unset
	expectedMembers := []Member{Member{Id: 1, Address: s.root.Addr(), Tags: initialTags}}

	// need to set mode to strong so that we wait for the write to succeed
	// before reading and thus ensure that we're getting consistent reads.
	session.SetMode(mgo.Strong, false)

	mems, err := CurrentMembers(session)
	c.Assert(err, gc.IsNil)
	c.Assert(mems, jc.DeepEquals, expectedMembers)

	// now add some data so we get a more real-life test
	loadData(session, c)
}

func (s *MongoSuite) TestInitiateWaitsForStatus(c *gc.C) {
	s.root.Destroy()

	// create a new server that hasn't been initiated
	s.root = newServer(c)

	session := s.root.MustDialDirect()
	defer session.Close()

	i := 0
	mockStatus := func(session *mgo.Session) (*Status, error) {
		status := &Status{}
		if i >= 20 {
			return status, nil
		}
		i += 1
		return status, fmt.Errorf("bang!")
	}

	s.PatchValue(&getCurrentStatus, mockStatus)
	Initiate(session, s.root.Addr(), rsName, initialTags)
	c.Assert(i, gc.Equals, 20)
}

func loadData(session *mgo.Session, c *gc.C) {
	type foo struct {
		Name    string
		Address string
		Count   int
	}

	for col := 0; col < 10; col++ {
		foos := make([]foo, 10000)
		for n := range foos {
			foos[n] = foo{
				Name:    fmt.Sprintf("name_%d_%d", col, n),
				Address: fmt.Sprintf("address_%d_%d", col, n),
				Count:   n * (col + 1),
			}
		}

		err := session.DB("testing").C(fmt.Sprintf("data%d", col)).Insert(foos)
		c.Assert(err, gc.IsNil)
	}
}

func attemptLoop(c *gc.C, strategy utils.AttemptStrategy, desc string, f func() error) {
	var err error
	start := time.Now()
	attemptCount := 0
	for attempt := strategy.Start(); attempt.Next(); {
		attemptCount += 1
		if err = f(); err == nil || !attempt.HasNext() {
			break
		}
		c.Logf("%s failed: %v", desc, err)
	}
	c.Logf("%s: %d attempts in %s", desc, attemptCount, time.Since(start))
	c.Assert(err, gc.IsNil)
}

func (s *MongoSuite) TestAddRemoveSet(c *gc.C) {
	session := s.root.MustDial()
	defer session.Close()

	members := make([]Member, 0, 5)

	// Add should be idempotent, so re-adding root here shouldn't result in
	// two copies of root in the replica set
	members = append(members, Member{Address: s.root.Addr(), Tags: initialTags})

	instances := make([]*coretesting.MgoInstance, 5)
	instances[0] = s.root
	for i := 1; i < len(instances); i++ {
		inst := newServer(c)
		instances[i] = inst
		defer inst.Destroy()
		defer func() {
			err := Remove(session, inst.Addr())
			c.Assert(err, gc.IsNil)
		}()
		key := fmt.Sprintf("key%d", i)
		val := fmt.Sprintf("val%d", i)
		tags := map[string]string{key: val}
		members = append(members, Member{Address: inst.Addr(), Tags: tags})
	}

	// We allow for up to 2m per operation, since Add, Set, etc. call
	// replSetReconfig which may cause primary renegotiation. According
	// to the Mongo docs, "typically this is 10-20 seconds, but could be
	// as long as a minute or more."
	//
	// Note that the delay is set at 500ms to cater for relatively quick
	// operations without thrashing on those that take longer.
	strategy := utils.AttemptStrategy{Total: time.Minute * 2, Delay: time.Millisecond * 500}
	attemptLoop(c, strategy, "Add()", func() error {
		return Add(session, members...)
	})

	expectedMembers := make([]Member, len(members))
	for i, m := range members {
		// Ids should start at 1 (for the root) and go up
		m.Id = i + 1
		expectedMembers[i] = m
	}

	var cfg *Config
	attemptLoop(c, strategy, "CurrentConfig()", func() error {
		var err error
		cfg, err = CurrentConfig(session)
		return err
	})
	c.Assert(cfg.Name, gc.Equals, rsName)
	// 2 since we already changed it once
	c.Assert(cfg.Version, gc.Equals, 2)

	mems := cfg.Members
	c.Assert(mems, jc.DeepEquals, expectedMembers)

	// Now remove the last two Members...
	attemptLoop(c, strategy, "Remove()", func() error {
		return Remove(session, members[3].Address, members[4].Address)
	})
	expectedMembers = expectedMembers[0:3]

	// ... and confirm that CurrentMembers reflects the removal.
	attemptLoop(c, strategy, "CurrentMembers()", func() error {
		var err error
		mems, err = CurrentMembers(session)
		return err
	})
	c.Assert(mems, jc.DeepEquals, expectedMembers)

	// now let's mix it up and set the new members to a mix of the previous
	// plus the new arbiter
	mems = []Member{members[3], mems[2], mems[0], members[4]}
	attemptLoop(c, strategy, "Set()", func() error {
		err := Set(session, mems)
		if err != nil {
			c.Logf("current session mode: %v", session.Mode())
			session.Refresh()
		}
		return err
	})

	attemptLoop(c, strategy, "Ping()", func() error {
		// can dial whichever replica address here, mongo will figure it out
		if session != nil {
			session.Close()
		}
		session = instances[0].MustDialDirect()
		return session.Ping()
	})

	// any new members will get an id of max(other_ids...)+1
	expectedMembers = []Member{members[3], expectedMembers[2], expectedMembers[0], members[4]}
	expectedMembers[0].Id = 4
	expectedMembers[3].Id = 5

	attemptLoop(c, strategy, "CurrentMembers()", func() error {
		var err error
		mems, err = CurrentMembers(session)
		return err
	})
	c.Assert(mems, jc.DeepEquals, expectedMembers)
}

func (s *MongoSuite) TestIsMaster(c *gc.C) {
	session := s.root.MustDial()
	defer session.Close()

	expected := IsMasterResults{
		// The following fields hold information about the specific mongodb node.
		IsMaster:  true,
		Secondary: false,
		Arbiter:   false,
		Address:   s.root.Addr(),
		LocalTime: time.Time{},

		// The following fields hold information about the replica set.
		ReplicaSetName: rsName,
		Addresses:      []string{s.root.Addr()},
		Arbiters:       nil,
		PrimaryAddress: s.root.Addr(),
	}

	res, err := IsMaster(session)
	c.Assert(err, gc.IsNil)
	c.Check(closeEnough(res.LocalTime, time.Now()), gc.Equals, true)
	res.LocalTime = time.Time{}
	c.Check(*res, jc.DeepEquals, expected)
}

func (s *MongoSuite) TestMasterHostPort(c *gc.C) {
	session := s.root.MustDial()
	defer session.Close()

	expected := s.root.Addr()
	result, err := MasterHostPort(session)

	c.Logf("TestMasterHostPort expected: %v, got: %v", expected, result)
	c.Assert(err, gc.IsNil)
	c.Assert(result, gc.Equals, expected)
}

func (s *MongoSuite) TestMasterHostPortOnUnconfiguredReplicaSet(c *gc.C) {
	inst := &coretesting.MgoInstance{}
	err := inst.Start(true)
	c.Assert(err, gc.IsNil)
	defer inst.Destroy()
	session := inst.MustDial()
	hp, err := MasterHostPort(session)
	c.Assert(err, gc.Equals, ErrMasterNotConfigured)
	c.Assert(hp, gc.Equals, "")
}

func (s *MongoSuite) TestCurrentStatus(c *gc.C) {
	session := s.root.MustDial()
	defer session.Close()

	inst1 := newServer(c)
	defer inst1.Destroy()
	defer Remove(session, inst1.Addr())

	inst2 := newServer(c)
	defer inst2.Destroy()
	defer Remove(session, inst2.Addr())

	var err error
	strategy := utils.AttemptStrategy{Total: time.Minute * 2, Delay: time.Millisecond * 500}
	attempt := strategy.Start()
	for attempt.Next() {
		err = Add(session, Member{Address: inst1.Addr()}, Member{Address: inst2.Addr()})
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expected := &Status{
		Name: rsName,
		Members: []MemberStatus{{
			Id:      1,
			Address: s.root.Addr(),
			Self:    true,
			ErrMsg:  "",
			Healthy: true,
			State:   PrimaryState,
		}, {
			Id:      2,
			Address: inst1.Addr(),
			Self:    false,
			ErrMsg:  "",
			Healthy: true,
			State:   SecondaryState,
		}, {
			Id:      3,
			Address: inst2.Addr(),
			Self:    false,
			ErrMsg:  "",
			Healthy: true,
			State:   SecondaryState,
		}},
	}

	strategy.Total = time.Second * 90
	attempt = strategy.Start()
	var res *Status
	for attempt.Next() {
		var err error
		res, err = CurrentStatus(session)
		if err != nil {
			if !attempt.HasNext() {
				c.Errorf("Couldn't get status before timeout, got err: %v", err)
				return
			} else {
				// try again
				continue
			}
		}

		if res.Members[0].State == PrimaryState &&
			res.Members[1].State == SecondaryState &&
			res.Members[2].State == SecondaryState {
			break
		}
		if !attempt.HasNext() {
			c.Errorf("Servers did not get into final state before timeout.  Status: %#v", res)
			return
		}
	}

	for x, _ := range res.Members {
		// non-empty uptime and ping
		c.Check(res.Members[x].Uptime, gc.Not(gc.Equals), 0)

		// ping is always going to be zero since we're on localhost
		// so we can't really test it right now

		// now overwrite Uptime so it won't throw off DeepEquals
		res.Members[x].Uptime = 0
	}
	c.Check(res, jc.DeepEquals, expected)
}

func closeEnough(expected, obtained time.Time) bool {
	t := obtained.Sub(expected)
	return (-500*time.Millisecond) < t && t < (500*time.Millisecond)
}
<commit_msg>Further test improvement<commit_after>package replicaset

import (
	"fmt"
	"testing"
	"time"

	jc "github.com/juju/testing/checkers"
	"labix.org/v2/mgo"
	gc "launchpad.net/gocheck"

	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/utils"
)

const rsName = "juju"

func TestPackage(t *testing.T) {
	gc.TestingT(t)
}

type MongoSuite struct {
	coretesting.BaseSuite
	root *coretesting.MgoInstance
}

func newServer(c *gc.C) *coretesting.MgoInstance {
	inst := &coretesting.MgoInstance{Params: []string{"--replSet", rsName}}
	err := inst.Start(true)
	c.Assert(err, gc.IsNil)

	session, err := inst.DialDirect()
	if err != nil {
		inst.Destroy()
		c.Fatalf("error dialing mongo server: %v", err.Error())
	}
	defer session.Close()

	session.SetMode(mgo.Monotonic, true)
	if err = session.Ping(); err != nil {
		inst.Destroy()
		c.Fatalf("error pinging mongo server: %v", err.Error())
	}
	return inst
}

var _ = gc.Suite(&MongoSuite{})

func (s *MongoSuite) SetUpTest(c *gc.C) {
	s.BaseSuite.SetUpTest(c)
	s.root = newServer(c)
	s.dialAndTestInitiate(c)
}

func (s *MongoSuite) TearDownTest(c *gc.C) {
	s.root.Destroy()
	s.BaseSuite.TearDownTest(c)
}

var initialTags = map[string]string{"foo": "bar"}

func (s *MongoSuite) dialAndTestInitiate(c *gc.C) {
	session := s.root.MustDialDirect()
	defer session.Close()

	mode := session.Mode()
	err := Initiate(session, s.root.Addr(), rsName, initialTags)
	c.Assert(err, gc.IsNil)

	// make sure we haven't messed with the session's mode
	c.Assert(session.Mode(), gc.Equals, mode)

	// Ids start at 1 for us, so we can differentiate between set and unset
	expectedMembers := []Member{Member{Id: 1, Address: s.root.Addr(), Tags: initialTags}}

	// need to set mode to strong so that we wait for the write to succeed
	// before reading and thus ensure that we're getting consistent reads.
	session.SetMode(mgo.Strong, false)

	mems, err := CurrentMembers(session)
	c.Assert(err, gc.IsNil)
	c.Assert(mems, jc.DeepEquals, expectedMembers)

	// now add some data so we get a more real-life test
	loadData(session, c)
}

func (s *MongoSuite) TestInitiateWaitsForStatus(c *gc.C) {
	s.root.Destroy()

	// create a new server that hasn't been initiated
	s.root = newServer(c)
	session := s.root.MustDialDirect()
	defer session.Close()

	i := 0
	mockStatus := func(session *mgo.Session) (*Status, error) {
		status := &Status{}
		var err error
		if i >= 20 {
			err = fmt.Errorf("bang!")
		} else {
			i += 1
		}
		return status, err
	}

	s.PatchValue(&getCurrentStatus, mockStatus)
	Initiate(session, s.root.Addr(), rsName, initialTags)
	c.Assert(i, gc.Equals, 20)
}

func loadData(session *mgo.Session, c *gc.C) {
	type foo struct {
		Name    string
		Address string
		Count   int
	}

	for col := 0; col < 10; col++ {
		foos := make([]foo, 10000)
		for n := range foos {
			foos[n] = foo{
				Name:    fmt.Sprintf("name_%d_%d", col, n),
				Address: fmt.Sprintf("address_%d_%d", col, n),
				Count:   n * (col + 1),
			}
		}

		err := session.DB("testing").C(fmt.Sprintf("data%d", col)).Insert(foos)
		c.Assert(err, gc.IsNil)
	}
}

func attemptLoop(c *gc.C, strategy utils.AttemptStrategy, desc string, f func() error) {
	var err error
	start := time.Now()
	attemptCount := 0
	for attempt := strategy.Start(); attempt.Next(); {
		attemptCount += 1
		if err = f(); err == nil || !attempt.HasNext() {
			break
		}
		c.Logf("%s failed: %v", desc, err)
	}
	c.Logf("%s: %d attempts in %s", desc, attemptCount, time.Since(start))
	c.Assert(err, gc.IsNil)
}

func (s *MongoSuite) TestAddRemoveSet(c *gc.C) {
	session := s.root.MustDial()
	defer session.Close()

	members := make([]Member, 0, 5)

	// Add should be idempotent, so re-adding root here shouldn't result in
	// two copies of root in the replica set
	members = append(members, Member{Address: s.root.Addr(), Tags: initialTags})

	instances := make([]*coretesting.MgoInstance, 5)
	instances[0] = s.root
	for i := 1; i < len(instances); i++ {
		inst := newServer(c)
		instances[i] = inst
		defer inst.Destroy()
		defer func() {
			err := Remove(session, inst.Addr())
			c.Assert(err, gc.IsNil)
		}()
		key := fmt.Sprintf("key%d", i)
		val := fmt.Sprintf("val%d", i)
		tags := map[string]string{key: val}
		members = append(members, Member{Address: inst.Addr(), Tags: tags})
	}

	// We allow for up to 2m per operation, since Add, Set, etc. call
	// replSetReconfig which may cause primary renegotiation. According
	// to the Mongo docs, "typically this is 10-20 seconds, but could be
	// as long as a minute or more."
	//
	// Note that the delay is set at 500ms to cater for relatively quick
	// operations without thrashing on those that take longer.
	strategy := utils.AttemptStrategy{Total: time.Minute * 2, Delay: time.Millisecond * 500}
	attemptLoop(c, strategy, "Add()", func() error {
		return Add(session, members...)
	})

	expectedMembers := make([]Member, len(members))
	for i, m := range members {
		// Ids should start at 1 (for the root) and go up
		m.Id = i + 1
		expectedMembers[i] = m
	}

	var cfg *Config
	attemptLoop(c, strategy, "CurrentConfig()", func() error {
		var err error
		cfg, err = CurrentConfig(session)
		return err
	})
	c.Assert(cfg.Name, gc.Equals, rsName)
	// 2 since we already changed it once
	c.Assert(cfg.Version, gc.Equals, 2)

	mems := cfg.Members
	c.Assert(mems, jc.DeepEquals, expectedMembers)

	// Now remove the last two Members...
	attemptLoop(c, strategy, "Remove()", func() error {
		return Remove(session, members[3].Address, members[4].Address)
	})
	expectedMembers = expectedMembers[0:3]

	// ... and confirm that CurrentMembers reflects the removal.
	attemptLoop(c, strategy, "CurrentMembers()", func() error {
		var err error
		mems, err = CurrentMembers(session)
		return err
	})
	c.Assert(mems, jc.DeepEquals, expectedMembers)

	// now let's mix it up and set the new members to a mix of the previous
	// plus the new arbiter
	mems = []Member{members[3], mems[2], mems[0], members[4]}
	attemptLoop(c, strategy, "Set()", func() error {
		err := Set(session, mems)
		if err != nil {
			c.Logf("current session mode: %v", session.Mode())
			session.Refresh()
		}
		return err
	})

	attemptLoop(c, strategy, "Ping()", func() error {
		// can dial whichever replica address here, mongo will figure it out
		if session != nil {
			session.Close()
		}
		session = instances[0].MustDialDirect()
		return session.Ping()
	})

	// any new members will get an id of max(other_ids...)+1
	expectedMembers = []Member{members[3], expectedMembers[2], expectedMembers[0], members[4]}
	expectedMembers[0].Id = 4
	expectedMembers[3].Id = 5

	attemptLoop(c, strategy, "CurrentMembers()", func() error {
		var err error
		mems, err = CurrentMembers(session)
		return err
	})
	c.Assert(mems, jc.DeepEquals, expectedMembers)
}

func (s *MongoSuite) TestIsMaster(c *gc.C) {
	session := s.root.MustDial()
	defer session.Close()

	expected := IsMasterResults{
		// The following fields hold information about the specific mongodb node.
		IsMaster:  true,
		Secondary: false,
		Arbiter:   false,
		Address:   s.root.Addr(),
		LocalTime: time.Time{},

		// The following fields hold information about the replica set.
		ReplicaSetName: rsName,
		Addresses:      []string{s.root.Addr()},
		Arbiters:       nil,
		PrimaryAddress: s.root.Addr(),
	}

	res, err := IsMaster(session)
	c.Assert(err, gc.IsNil)
	c.Check(closeEnough(res.LocalTime, time.Now()), gc.Equals, true)
	res.LocalTime = time.Time{}
	c.Check(*res, jc.DeepEquals, expected)
}

func (s *MongoSuite) TestMasterHostPort(c *gc.C) {
	session := s.root.MustDial()
	defer session.Close()

	expected := s.root.Addr()
	result, err := MasterHostPort(session)

	c.Logf("TestMasterHostPort expected: %v, got: %v", expected, result)
	c.Assert(err, gc.IsNil)
	c.Assert(result, gc.Equals, expected)
}

func (s *MongoSuite) TestMasterHostPortOnUnconfiguredReplicaSet(c *gc.C) {
	inst := &coretesting.MgoInstance{}
	err := inst.Start(true)
	c.Assert(err, gc.IsNil)
	defer inst.Destroy()
	session := inst.MustDial()
	hp, err := MasterHostPort(session)
	c.Assert(err, gc.Equals, ErrMasterNotConfigured)
	c.Assert(hp, gc.Equals, "")
}

func (s *MongoSuite) TestCurrentStatus(c *gc.C) {
	session := s.root.MustDial()
	defer session.Close()

	inst1 := newServer(c)
	defer inst1.Destroy()
	defer Remove(session, inst1.Addr())

	inst2 := newServer(c)
	defer inst2.Destroy()
	defer Remove(session, inst2.Addr())

	var err error
	strategy := utils.AttemptStrategy{Total: time.Minute * 2, Delay: time.Millisecond * 500}
	attempt := strategy.Start()
	for attempt.Next() {
		err = Add(session, Member{Address: inst1.Addr()}, Member{Address: inst2.Addr()})
		if err == nil || !attempt.HasNext() {
			break
		}
	}
	c.Assert(err, gc.IsNil)

	expected := &Status{
		Name: rsName,
		Members: []MemberStatus{{
			Id:      1,
			Address: s.root.Addr(),
			Self:    true,
			ErrMsg:  "",
			Healthy: true,
			State:   PrimaryState,
		}, {
			Id:      2,
			Address: inst1.Addr(),
			Self:    false,
			ErrMsg:  "",
			Healthy: true,
			State:   SecondaryState,
		}, {
			Id:      3,
			Address: inst2.Addr(),
			Self:    false,
			ErrMsg:  "",
			Healthy: true,
			State:   SecondaryState,
		}},
	}

	strategy.Total = time.Second * 90
	attempt = strategy.Start()
	var res *Status
	for attempt.Next() {
		var err error
		res, err = CurrentStatus(session)
		if err != nil {
			if !attempt.HasNext() {
				c.Errorf("Couldn't get status before timeout, got err: %v", err)
				return
			} else {
				// try again
				continue
			}
		}

		if res.Members[0].State == PrimaryState &&
			res.Members[1].State == SecondaryState &&
			res.Members[2].State == SecondaryState {
			break
		}
		if !attempt.HasNext() {
			c.Errorf("Servers did not get into final state before timeout.  Status: %#v", res)
			return
		}
	}

	for x, _ := range res.Members {
		// non-empty uptime and ping
		c.Check(res.Members[x].Uptime, gc.Not(gc.Equals), 0)

		// ping is always going to be zero since we're on localhost
		// so we can't really test it right now

		// now overwrite Uptime so it won't throw off DeepEquals
		res.Members[x].Uptime = 0
	}
	c.Check(res, jc.DeepEquals, expected)
}

func closeEnough(expected, obtained time.Time) bool {
	t := obtained.Sub(expected)
	return (-500*time.Millisecond) < t && t < (500*time.Millisecond)
}
<|endoftext|>