<commit_before>/*
Package msg creates a simple messaging package with flexible formatting that
can be used for stdout and/or logging.

Here is an example use.

    import (
      "jlinoff/utils/msg"
      "io"
    )

    // My package logger.
    var log *msg.Object

    // Initialize it at startup.
    func init() {
      // Only write to stdout.
      w := []io.Writer{os.Stdout}

      // The name of my package. It is only used %pkg is specified in the
      // format string.
      n := "MyPackage"

      // Format string. Note that i could use %utc instead of %time to get
      // UTC time.
      f := `%pkg %(-27)time %(-7)type %file %line - %msg`

      // Time format string, only used if %time or %utc are specified in the
      // the format string.
      t := `2006-01-02 15:05:05.000 MST`

      // Create the message object.
      // Note that this is the same as this because I used the defaults.
      //     msg.NewMsg("MyPackage", "", "", []io.Writer{})
      l, e := msg.NewMsg(n, f, t, w)
      if err != nil { panic(err) }
      log = l
    }

    func test() {
      log.Debug("message of type %v", "debug")
      log.Info("info message")
      log.Warning("warning")

      // Now print messages to stdout and to a log while in this scope.
      fp := os.Create("log.txt")
      log.Writers = append(log.Writers, fp)

      // This stuff will go to stdout and the log file.
      log.Info("both")
      log.ErrNoExit("bad stuff happened but i can recover!")

      // Clean up by removing the file from the writers and then
      // closing it.
      log.Writers = log.Writers[:len(log.Writers)-1]
      fp.Close()
  }
*/
package msg

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"strconv"
	"time"
)

// Interface defines logger functions.
type Interface interface {
	Debug(f string, a ...interface{})
	Info(f string, a ...interface{})
	Warn(f string, a ...interface{})
	Err(f string, a ...interface{})
	ErrNoExit(f string, a ...interface{})

	DebugWithLevel(l int, f string, a ...interface{})
	InfoWithLevel(l int, f string, a ...interface{})
	WarnWithLevel(l int, f string, a ...interface{})
	ErrWithLevel(l int, f string, a ...interface{})
	ErrNoExitWithLevel(l int, f string, a ...interface{})
}

// Object defines the logger.
type Object struct {
	// Name is the package name. It is accessed in the format string by %pkg.
	Name string

	// DebugEnabled enables debug messages if true.
	// It is true by default.
	DebugEnabled bool

	// InfoEnabled enables info messages if true.
	// It is true by default.
	InfoEnabled bool

	// WarningEnabled enables warning messages if true.
	// It is true by default.
	WarningEnabled bool

	// ErrorExitCode is the exit code to use for the Error function.
	// The default is 1.
	ErrorExitCode int

	// Writers for the message output.
	// If no writers are specified, messages go to os.Stdout.
	Writers []io.Writer

	// TimeFormat is the format of the prefix timestamp.
	// See time.Format for details.
	// The default format is: "2006-01-02 15:05:05.000 MST"
	TimeFormat string

	// Format is the template for the output. It has the following specifiers.
	//
	//   %file is the caller file name
	//   %func is the function name
	//   %line is the line number
	//   %msg  is the actual message
	//   %pkg  is the package name
	//   %time is the time format in the current locale
	//   %utc is the time format in the UTC locale
	//   %type is the msg type: DEBUG, INFO, WARNING, ERROR
	//   %% is a single % character
	//
	// You can explicitly format each field by specifying the formatting
	// options in parentheses.
	//
	//   %(-28)time
	//
	// Any other text is left verbatim.
	//
	// The default format is.
	//   "%(-27)time %(-7)type" %file %line - %msg
	Format string

	// outputFormat created by NewMsgObject and used to generate a message.
	outputFormat string

	// outputFlds created by NewMsgObject and used to specify the fields.
	outputFlds []string
}

// NewMsg makes a message object.
//   n - package name
//   f - format string, set to "" to get the default.
//   t - time stamp format, set to "" to get the default
//   w - the list of writers, if empty all messages go to stdout
func NewMsg(n string, f string, t string, w []io.Writer) (obj *Object, err error) {
	obj = new(Object)
	obj.Name = n
	obj.DebugEnabled = true
	obj.InfoEnabled = true
	obj.WarningEnabled = true
	obj.ErrorExitCode = 1

	if len(w) == 0 {
		obj.Writers = append(obj.Writers, os.Stdout)
	} else {
		obj.Writers = w
	}

	// Set the time format. If it is empty, set the default.
	if t == "" {
		obj.TimeFormat = "2006-01-02 15:05:05.000 MST"
	} else {
		obj.TimeFormat = t
	}

	// Set the format. If it is empty use the default.
	if f == "" {
		obj.Format = `%(-27)time %(-7)type %file %line - %msg`
	} else {
		obj.Format = f
	}

	// Parse the format.
	ofmt, oflds, err := ParseFormatString(f)
	obj.outputFormat = ofmt
	obj.outputFlds = oflds

	return
}

/*
Debug prints a debug message obtaining the callers filename, function and
line number.

Example:
      msg.Debug("%v = %v", key, value)
*/
func (o Object) Debug(f string, a ...interface{}) {
	if o.DebugEnabled {
		o.Print("DEBUG", 2, f, a...)
	}
}

/*
DebugWithLevel prints a debug message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug().

Example:
      msg.DebugWithLevel(2, "%v = %v", key, value)
*/
func (o Object) DebugWithLevel(l int, f string, a ...interface{}) {
	if o.DebugEnabled {
		o.Print("DEBUG", l, f, a...)
	}
}

/*
Info prints an info message obtaining the callers filename, function and
line number.

Example:
      msg.Info("%v = %v", key, value)
*/
func (o Object) Info(f string, a ...interface{}) {
	if o.InfoEnabled {
		o.Print("INFO", 2, f, a...)
	}
}

/*
InfoWithLevel prints an info message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug().

Example:
      msg.InfoWithLevel(2, "%v = %v", key, value)
*/
func (o Object) InfoWithLevel(l int, f string, a ...interface{}) {
	if o.InfoEnabled {
		o.Print("INFO", l, f, a...)
	}
}

/*
Warn prints a warning message obtaining the callers filename, function and
line number.

Example:
      msg.Warn("%v = %v", key, value)
*/
func (o Object) Warn(f string, a ...interface{}) {
	if o.WarningEnabled {
		o.Print("WARNING", 2, f, a...)
	}
}

/*
WarnWithLevel prints a warning message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug().

Example:
      msg.WarnWithLevel(2, "%v = %v", key, value)
*/
func (o Object) WarnWithLevel(l int, f string, a ...interface{}) {
	if o.WarningEnabled {
		o.Print("WARNING", 2, f, a...)
	}
}

/*
Err prints an error message obtaining the callers filename, function and
line number and exits. It cannot be disabled.

Example:
      msg.Err("%v = %v", key, value)
*/
func (o Object) Err(f string, a ...interface{}) {
	o.Print("ERROR", 2, f, a...)
	os.Exit(o.ErrorExitCode)
}

/*
ErrWithLevel prints an error message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug() and exits. It cannot be disabled.

Example:
      msg.ErrWithLevel(2, "%v = %v", key, value)
*/
func (o Object) ErrWithLevel(l int, f string, a ...interface{}) {
	o.Print("ERROR", 2, f, a...)
	os.Exit(o.ErrorExitCode)
}

/*
ErrNoExit prints an error message obtaining the callers filename, function and
line number. It does not exit and cannot be disabled.

Example:
      msg.ErrNoExit("%v = %v", key, value)
*/
func (o Object) ErrNoExit(f string, a ...interface{}) {
	o.Print("ERROR", 2, f, a...)
}

/*
ErrNoExitWithLevel prints an error message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug(). It does not exit and cannot be disabled.

Example:
      msg.ErrNoExitWithLevel(2, "%v = %v", key, value)
*/
func (o Object) ErrNoExitWithLevel(l int, f string, a ...interface{}) {
	o.Print("ERROR", 2, f, a...)
}

/*
Print is the basis of all message printers. It normally would not be called
directly.

t - is the type, normally one of DEBUG, INFO, WARNING or ERROR
l - is the caller level: 0 is this function, 1 is the caller, 2 is the callers caller and so on
f - format string
a - argument list
*/
func (o Object) Print(t string, l int, f string, a ...interface{}) {
	pc, fname, lineno, _ := runtime.Caller(l)
	fct := runtime.FuncForPC(pc).Name()
	fname = path.Base(fname[0 : len(fname)-3]) // strip off ".go"

	// The variables map for the format string.
	m := map[string]string{
		"file": fname,
		"func": fct,
		"line": strconv.Itoa(lineno),
		"msg":  fmt.Sprintf(f, a...),
		"pkg":  o.Name,
		"time": time.Now().Truncate(time.Millisecond).Format(o.TimeFormat),
		"utc":  time.Now().UTC().Truncate(time.Millisecond).Format(o.TimeFormat),
		"type": t,
	}

	// Collect the field values.
	var flds []interface{}
	for _, k := range o.outputFlds {
		if v, ok := m[k]; ok {
			flds = append(flds, v)
		} else {
			// This is, essentially, an assert. It should never happen.
			fmt.Fprintf(os.Stderr, "ERROR: unexpected condition, invalid specification id '%v'\n", k)
			os.Exit(1)
		}
	}

	// Create the formatted output string.
	s := fmt.Sprintf(o.outputFormat, flds...) + "\n"

	// Output it for each writer.
	for _, w := range o.Writers {
		fmt.Fprintf(w, s)
	}
}

/*
ParseFormatString transforms a format template to a format string
and the list of fields to print in each message.

It is meant to be used internally by NewMsg().

Here is an example transformation:

      input = "MYSTUFF %(-27)time %(-7)type %file %line - %msg"

      // TRANSFORM
      ofmt  = "MYSTUFF %-27v %-7v %v %v - %v"
      oids  = ["time", "type", "type", "file", "line", "msg"]
*/
func ParseFormatString(input string) (ofmt string, oids []string, err error) {
	ofmtb := []byte{}
	valid := []string{"file", "func", "line", "msg", "pkg", "time", "type", "utc"}
	ics := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-$")

	// Define the parse states.
	//    normal - capture each byte
	//    spec   - capture a specification of the form %<id> or %(<fmt>)<id>.
	type state int
	const (
		normal state = iota
		spec
	)
	s := normal
	ib := []byte(input)
	for i := 0; i < len(ib); i++ {
		b := ib[i]
		switch s {
		case normal:
			// normal state, this is all of the stuff in the
			// template that is not part of a specification.
			if b == '%' {
				s = spec
			} else {
				ofmtb = append(ofmtb, b)
			}
		case spec:
			s = normal // after parsing the spec go back to the normal state

			// specification state, parse specifications of the form:
			//   %(<fmt>)<id>
			//   %<id>
			beg := i - 1
			if b == '(' {
				// This is a format specification. Capture it.
				// If ')' is not found, report an error.
				j := i // ib[j] == '('
				for ; i < len(ib) && ib[i] != ')'; i++ {
				}
				if i >= len(ib) {
					err = fmt.Errorf("missing ')' for '%v'", string(ib[beg:]))
					return
				}
				ofmtb = append(ofmtb, '%')
				ofmtb = append(ofmtb, ib[j+1:i]...)
				ofmtb = append(ofmtb, 'v')
				i++ // point past the ')'
			} else {
				ofmtb = append(ofmtb, []byte("%v")[:]...)
			}

			// Now parse out the id.
			id := ""
			for _, v := range valid {
				ba := []byte(v)
				if bytes.HasPrefix(ib[i:], ba) {
					// We MAY have a match.
					// for example '%line' matches but '%linex' does not.
					i += len(ba)
					if i < len(ib) {
						bs := []byte{ib[i]}
						if bytes.Contains([]byte(ics), bs) {
							ofmt = string(ofmtb)
							ba = append(ba, ib[i])
							err = fmt.Errorf("unrecognized specification id '%v'", string(ba))
							return
						}
					}
					id = string(ba)
					i--
					break
				}
			}
			if id == "" {
				ofmt = string(ofmtb)
				err = fmt.Errorf("specification syntax error '%v'", string(ib[beg:]))
				return
			}
			oids = append(oids, id)
		}
	}
	ofmt = string(ofmtb)
	return
}
<commit_msg>Fixed help format for Print<commit_after>/*
Package msg creates a simple messaging package with flexible formatting that
can be used for stdout and/or logging.

Here is an example use.

    import (
      "jlinoff/utils/msg"
      "io"
    )

    // My package logger.
    var log *msg.Object

    // Initialize it at startup.
    func init() {
      // Only write to stdout.
      w := []io.Writer{os.Stdout}

      // The name of my package. It is only used %pkg is specified in the
      // format string.
      n := "MyPackage"

      // Format string. Note that i could use %utc instead of %time to get
      // UTC time.
      f := `%pkg %(-27)time %(-7)type %file %line - %msg`

      // Time format string, only used if %time or %utc are specified in the
      // the format string.
      t := `2006-01-02 15:05:05.000 MST`

      // Create the message object.
      // Note that this is the same as this because I used the defaults.
      //     msg.NewMsg("MyPackage", "", "", []io.Writer{})
      l, e := msg.NewMsg(n, f, t, w)
      if err != nil { panic(err) }
      log = l
    }

    func test() {
      log.Debug("message of type %v", "debug")
      log.Info("info message")
      log.Warning("warning")

      // Now print messages to stdout and to a log while in this scope.
      fp := os.Create("log.txt")
      log.Writers = append(log.Writers, fp)

      // This stuff will go to stdout and the log file.
      log.Info("both")
      log.ErrNoExit("bad stuff happened but i can recover!")

      // Clean up by removing the file from the writers and then
      // closing it.
      log.Writers = log.Writers[:len(log.Writers)-1]
      fp.Close()
  }
*/
package msg

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"strconv"
	"time"
)

// Interface defines logger functions.
type Interface interface {
	Debug(f string, a ...interface{})
	Info(f string, a ...interface{})
	Warn(f string, a ...interface{})
	Err(f string, a ...interface{})
	ErrNoExit(f string, a ...interface{})

	DebugWithLevel(l int, f string, a ...interface{})
	InfoWithLevel(l int, f string, a ...interface{})
	WarnWithLevel(l int, f string, a ...interface{})
	ErrWithLevel(l int, f string, a ...interface{})
	ErrNoExitWithLevel(l int, f string, a ...interface{})
}

// Object defines the logger.
type Object struct {
	// Name is the package name. It is accessed in the format string by %pkg.
	Name string

	// DebugEnabled enables debug messages if true.
	// It is true by default.
	DebugEnabled bool

	// InfoEnabled enables info messages if true.
	// It is true by default.
	InfoEnabled bool

	// WarningEnabled enables warning messages if true.
	// It is true by default.
	WarningEnabled bool

	// ErrorExitCode is the exit code to use for the Error function.
	// The default is 1.
	ErrorExitCode int

	// Writers for the message output.
	// If no writers are specified, messages go to os.Stdout.
	Writers []io.Writer

	// TimeFormat is the format of the prefix timestamp.
	// See time.Format for details.
	// The default format is: "2006-01-02 15:05:05.000 MST"
	TimeFormat string

	// Format is the template for the output. It has the following specifiers.
	//
	//   %file is the caller file name
	//   %func is the function name
	//   %line is the line number
	//   %msg  is the actual message
	//   %pkg  is the package name
	//   %time is the time format in the current locale
	//   %utc is the time format in the UTC locale
	//   %type is the msg type: DEBUG, INFO, WARNING, ERROR
	//   %% is a single % character
	//
	// You can explicitly format each field by specifying the formatting
	// options in parentheses.
	//
	//   %(-28)time
	//
	// Any other text is left verbatim.
	//
	// The default format is.
	//   "%(-27)time %(-7)type" %file %line - %msg
	Format string

	// outputFormat created by NewMsgObject and used to generate a message.
	outputFormat string

	// outputFlds created by NewMsgObject and used to specify the fields.
	outputFlds []string
}

// NewMsg makes a message object.
//   n - package name
//   f - format string, set to "" to get the default.
//   t - time stamp format, set to "" to get the default
//   w - the list of writers, if empty all messages go to stdout
func NewMsg(n string, f string, t string, w []io.Writer) (obj *Object, err error) {
	obj = new(Object)
	obj.Name = n
	obj.DebugEnabled = true
	obj.InfoEnabled = true
	obj.WarningEnabled = true
	obj.ErrorExitCode = 1

	if len(w) == 0 {
		obj.Writers = append(obj.Writers, os.Stdout)
	} else {
		obj.Writers = w
	}

	// Set the time format. If it is empty, set the default.
	if t == "" {
		obj.TimeFormat = "2006-01-02 15:05:05.000 MST"
	} else {
		obj.TimeFormat = t
	}

	// Set the format. If it is empty use the default.
	if f == "" {
		obj.Format = `%(-27)time %(-7)type %file %line - %msg`
	} else {
		obj.Format = f
	}

	// Parse the format.
	ofmt, oflds, err := ParseFormatString(f)
	obj.outputFormat = ofmt
	obj.outputFlds = oflds

	return
}

/*
Debug prints a debug message obtaining the callers filename, function and
line number.

Example:
      msg.Debug("%v = %v", key, value)
*/
func (o Object) Debug(f string, a ...interface{}) {
	if o.DebugEnabled {
		o.Print("DEBUG", 2, f, a...)
	}
}

/*
DebugWithLevel prints a debug message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug().

Example:
      msg.DebugWithLevel(2, "%v = %v", key, value)
*/
func (o Object) DebugWithLevel(l int, f string, a ...interface{}) {
	if o.DebugEnabled {
		o.Print("DEBUG", l, f, a...)
	}
}

/*
Info prints an info message obtaining the callers filename, function and
line number.

Example:
      msg.Info("%v = %v", key, value)
*/
func (o Object) Info(f string, a ...interface{}) {
	if o.InfoEnabled {
		o.Print("INFO", 2, f, a...)
	}
}

/*
InfoWithLevel prints an info message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug().

Example:
      msg.InfoWithLevel(2, "%v = %v", key, value)
*/
func (o Object) InfoWithLevel(l int, f string, a ...interface{}) {
	if o.InfoEnabled {
		o.Print("INFO", l, f, a...)
	}
}

/*
Warn prints a warning message obtaining the callers filename, function and
line number.

Example:
      msg.Warn("%v = %v", key, value)
*/
func (o Object) Warn(f string, a ...interface{}) {
	if o.WarningEnabled {
		o.Print("WARNING", 2, f, a...)
	}
}

/*
WarnWithLevel prints a warning message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug().

Example:
      msg.WarnWithLevel(2, "%v = %v", key, value)
*/
func (o Object) WarnWithLevel(l int, f string, a ...interface{}) {
	if o.WarningEnabled {
		o.Print("WARNING", 2, f, a...)
	}
}

/*
Err prints an error message obtaining the callers filename, function and
line number and exits. It cannot be disabled.

Example:
      msg.Err("%v = %v", key, value)
*/
func (o Object) Err(f string, a ...interface{}) {
	o.Print("ERROR", 2, f, a...)
	os.Exit(o.ErrorExitCode)
}

/*
ErrWithLevel prints an error message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug() and exits. It cannot be disabled.

Example:
      msg.ErrWithLevel(2, "%v = %v", key, value)
*/
func (o Object) ErrWithLevel(l int, f string, a ...interface{}) {
	o.Print("ERROR", 2, f, a...)
	os.Exit(o.ErrorExitCode)
}

/*
ErrNoExit prints an error message obtaining the callers filename, function and
line number. It does not exit and cannot be disabled.

Example:
      msg.ErrNoExit("%v = %v", key, value)
*/
func (o Object) ErrNoExit(f string, a ...interface{}) {
	o.Print("ERROR", 2, f, a...)
}

/*
ErrNoExitWithLevel prints an error message obtaining the filename, function and
line number from the caller specified by level "l". l=2 is the same
as Debug(). It does not exit and cannot be disabled.

Example:
      msg.ErrNoExitWithLevel(2, "%v = %v", key, value)
*/
func (o Object) ErrNoExitWithLevel(l int, f string, a ...interface{}) {
	o.Print("ERROR", 2, f, a...)
}

/*
Print is the basis of all message printers. It normally would not be called
directly.

      t - is the type, normally one of DEBUG, INFO, WARNING or ERROR
      l - is the caller level: 0 is this function, 1 is the caller, 2 is the callers caller and so on
      f - format string
      a - argument list
*/
func (o Object) Print(t string, l int, f string, a ...interface{}) {
	pc, fname, lineno, _ := runtime.Caller(l)
	fct := runtime.FuncForPC(pc).Name()
	fname = path.Base(fname[0 : len(fname)-3]) // strip off ".go"

	// The variables map for the format string.
	m := map[string]string{
		"file": fname,
		"func": fct,
		"line": strconv.Itoa(lineno),
		"msg":  fmt.Sprintf(f, a...),
		"pkg":  o.Name,
		"time": time.Now().Truncate(time.Millisecond).Format(o.TimeFormat),
		"utc":  time.Now().UTC().Truncate(time.Millisecond).Format(o.TimeFormat),
		"type": t,
	}

	// Collect the field values.
	var flds []interface{}
	for _, k := range o.outputFlds {
		if v, ok := m[k]; ok {
			flds = append(flds, v)
		} else {
			// This is, essentially, an assert. It should never happen.
			fmt.Fprintf(os.Stderr, "ERROR: unexpected condition, invalid specification id '%v'\n", k)
			os.Exit(1)
		}
	}

	// Create the formatted output string.
	s := fmt.Sprintf(o.outputFormat, flds...) + "\n"

	// Output it for each writer.
	for _, w := range o.Writers {
		fmt.Fprintf(w, s)
	}
}

/*
ParseFormatString transforms a format template to a format string
and the list of fields to print in each message.

It is meant to be used internally by NewMsg().

Here is an example transformation:

      input = "MYSTUFF %(-27)time %(-7)type %file %line - %msg"

      // TRANSFORM
      ofmt  = "MYSTUFF %-27v %-7v %v %v - %v"
      oids  = ["time", "type", "type", "file", "line", "msg"]
*/
func ParseFormatString(input string) (ofmt string, oids []string, err error) {
	ofmtb := []byte{}
	valid := []string{"file", "func", "line", "msg", "pkg", "time", "type", "utc"}
	ics := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-$")

	// Define the parse states.
	//    normal - capture each byte
	//    spec   - capture a specification of the form %<id> or %(<fmt>)<id>.
	type state int
	const (
		normal state = iota
		spec
	)
	s := normal
	ib := []byte(input)
	for i := 0; i < len(ib); i++ {
		b := ib[i]
		switch s {
		case normal:
			// normal state, this is all of the stuff in the
			// template that is not part of a specification.
			if b == '%' {
				s = spec
			} else {
				ofmtb = append(ofmtb, b)
			}
		case spec:
			s = normal // after parsing the spec go back to the normal state

			// specification state, parse specifications of the form:
			//   %(<fmt>)<id>
			//   %<id>
			beg := i - 1
			if b == '(' {
				// This is a format specification. Capture it.
				// If ')' is not found, report an error.
				j := i // ib[j] == '('
				for ; i < len(ib) && ib[i] != ')'; i++ {
				}
				if i >= len(ib) {
					err = fmt.Errorf("missing ')' for '%v'", string(ib[beg:]))
					return
				}
				ofmtb = append(ofmtb, '%')
				ofmtb = append(ofmtb, ib[j+1:i]...)
				ofmtb = append(ofmtb, 'v')
				i++ // point past the ')'
			} else {
				ofmtb = append(ofmtb, []byte("%v")[:]...)
			}

			// Now parse out the id.
			id := ""
			for _, v := range valid {
				ba := []byte(v)
				if bytes.HasPrefix(ib[i:], ba) {
					// We MAY have a match.
					// for example '%line' matches but '%linex' does not.
					i += len(ba)
					if i < len(ib) {
						bs := []byte{ib[i]}
						if bytes.Contains([]byte(ics), bs) {
							ofmt = string(ofmtb)
							ba = append(ba, ib[i])
							err = fmt.Errorf("unrecognized specification id '%v'", string(ba))
							return
						}
					}
					id = string(ba)
					i--
					break
				}
			}
			if id == "" {
				ofmt = string(ofmtb)
				err = fmt.Errorf("specification syntax error '%v'", string(ib[beg:]))
				return
			}
			oids = append(oids, id)
		}
	}
	ofmt = string(ofmtb)
	return
}
<|endoftext|>


<commit_before>package tftest

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	tfjson "github.com/hashicorp/terraform-json"
)

// WorkingDir represents a distinct working directory that can be used for
// running tests. Each test should construct its own WorkingDir by calling
// NewWorkingDir or RequireNewWorkingDir on its package's singleton
// tftest.Helper.
type WorkingDir struct {
	h *Helper

	// baseDir is the root of the working directory tree
	baseDir string

	// baseArgs is arguments that should be appended to all commands
	baseArgs []string

	// configDir contains the singular config file generated for each test
	configDir string
}

// Close deletes the directories and files created to represent the receiving
// working directory. After this method is called, the working directory object
// is invalid and may no longer be used.
func (wd *WorkingDir) Close() error {
	return os.RemoveAll(wd.baseDir)
}

// SetConfig sets a new configuration for the working directory.
//
// This must be called at least once before any call to Init, Plan, Apply, or
// Destroy to establish the configuration. Any previously-set configuration is
// discarded and any saved plan is cleared.
func (wd *WorkingDir) SetConfig(cfg string) error {
	// Each call to SetConfig creates a new directory under our baseDir.
	// We create them within so that our final cleanup step will delete them
	// automatically without any additional tracking.
	configDir, err := ioutil.TempDir(wd.baseDir, "config")
	if err != nil {
		return err
	}
	configFilename := filepath.Join(configDir, "terraform_plugin_test.tf")
	err = ioutil.WriteFile(configFilename, []byte(cfg), 0700)
	if err != nil {
		return err
	}

	wd.configDir = configDir

	// Changing configuration invalidates any saved plan.
	err = wd.ClearPlan()
	if err != nil {
		return err
	}
	return nil
}

// RequireSetConfig is a variant of SetConfig that will fail the test via the
// given TestControl if the configuration cannot be set.
func (wd *WorkingDir) RequireSetConfig(t TestControl, cfg string) {
	t.Helper()
	if err := wd.SetConfig(cfg); err != nil {
		t := testingT{t}
		t.Fatalf("failed to set config: %s", err)
	}
}

// ClearState deletes any Terraform state present in the working directory.
//
// Any remote objects tracked by the state are not destroyed first, so this
// will leave them dangling in the remote system.
func (wd *WorkingDir) ClearState() error {
	err := os.Remove(filepath.Join(wd.baseDir, "terraform.tfstate"))
	if os.IsNotExist(err) {
		return nil
	}
	return err
}

// RequireClearState is a variant of ClearState that will fail the test via the
// given TestControl if the state cannot be cleared.
func (wd *WorkingDir) RequireClearState(t TestControl) {
	t.Helper()
	if err := wd.ClearState(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to clear state: %s", err)
	}
}

// ClearPlan deletes any saved plan present in the working directory.
func (wd *WorkingDir) ClearPlan() error {
	err := os.Remove(wd.planFilename())
	if os.IsNotExist(err) {
		return nil
	}
	return err
}

// RequireClearPlan is a variant of ClearPlan that will fail the test via the
// given TestControl if the plan cannot be cleared.
func (wd *WorkingDir) RequireClearPlan(t TestControl) {
	t.Helper()
	if err := wd.ClearPlan(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to clear plan: %s", err)
	}
}

func (wd *WorkingDir) init(pluginDir string) error {
	args := []string{"init"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-plugin-dir="+pluginDir, wd.configDir)
	return wd.runTerraform(args...)
}

// Init runs "terraform init" for the given working directory, forcing Terraform
// to use the current version of the plugin under test.
func (wd *WorkingDir) Init() error {
	if wd.configDir == "" {
		return fmt.Errorf("must call SetConfig before Init")
	}
	return wd.init(wd.h.PluginDir())
}

// RequireInit is a variant of Init that will fail the test via the given
// TestControl if init fails.
func (wd *WorkingDir) RequireInit(t TestControl) {
	t.Helper()
	if err := wd.Init(); err != nil {
		t := testingT{t}
		t.Fatalf("init failed: %s", err)
	}
}

// InitPrevious runs "terraform init" for the given working directory, forcing
// Terraform to use the previous version of the plugin under test.
//
// This method will panic if no previous plugin version is available. Use
// HasPreviousVersion or RequirePreviousVersion on the test helper singleton
// to check this first.
func (wd *WorkingDir) InitPrevious() error {
	if wd.configDir == "" {
		return fmt.Errorf("must call SetConfig before InitPrevious")
	}
	return wd.init(wd.h.PreviousPluginDir())
}

// RequireInitPrevious is a variant of InitPrevious that will fail the test
// via the given TestControl if init fails.
func (wd *WorkingDir) RequireInitPrevious(t TestControl) {
	t.Helper()
	if err := wd.InitPrevious(); err != nil {
		t := testingT{t}
		t.Fatalf("init failed: %s", err)
	}
}

func (wd *WorkingDir) planFilename() string {
	return filepath.Join(wd.baseDir, "tfplan")
}

// CreatePlan runs "terraform plan" to create a saved plan file, which if successful
// will then be used for the next call to Apply.
func (wd *WorkingDir) CreatePlan() error {
	args := []string{"plan"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-out=tfplan", wd.configDir)
	return wd.runTerraform(args...)
}

// RequireCreatePlan is a variant of CreatePlan that will fail the test via
// the given TestControl if plan creation fails.
func (wd *WorkingDir) RequireCreatePlan(t TestControl) {
	t.Helper()
	if err := wd.CreatePlan(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to create plan: %s", err)
	}
}

// Apply runs "terraform apply". If CreatePlan has previously completed
// successfully and the saved plan has not been cleared in the meantime then
// this will apply the saved plan. Otherwise, it will implicitly create a new
// plan and apply it.
func (wd *WorkingDir) Apply() error {
	args := []string{"apply"}
	args = append(args, wd.baseArgs...)

	if wd.HasSavedPlan() {
		args = append(args, "tfplan")
	} else {
		args = append(args, "-auto-approve")
		args = append(args, wd.configDir)
	}

	return wd.runTerraform(args...)
}

// RequireApply is a variant of Apply that will fail the test via
// the given TestControl if the apply operation fails.
func (wd *WorkingDir) RequireApply(t TestControl) {
	t.Helper()
	if err := wd.Apply(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to apply: %s", err)
	}
}

// Destroy runs "terraform destroy". It does not consider or modify any saved
// plan, and is primarily for cleaning up at the end of a test run.
//
// If destroy fails then remote objects might still exist, and continue to
// exist after a particular test is concluded.
func (wd *WorkingDir) Destroy() error {
	args := []string{"destroy"}
	args = append(args, wd.baseArgs...)

	args = append(args, "-auto-approve", wd.configDir)
	return wd.runTerraform(args...)
}

// RequireDestroy is a variant of Destroy that will fail the test via
// the given TestControl if the destroy operation fails.
//
// If destroy fails then remote objects might still exist, and continue to
// exist after a particular test is concluded.
func (wd *WorkingDir) RequireDestroy(t TestControl) {
	t.Helper()
	if err := wd.Destroy(); err != nil {
		t := testingT{t}
		t.Logf("WARNING: destroy failed, so remote objects may still exist and be subject to billing")
		t.Fatalf("failed to destroy: %s", err)
	}
}

// HasSavedPlan returns true if there is a saved plan in the working directory. If
// so, a subsequent call to Apply will apply that saved plan.
func (wd *WorkingDir) HasSavedPlan() bool {
	_, err := os.Stat(wd.planFilename())
	return err == nil
}

// SavedPlan returns an object describing the current saved plan file, if any.
//
// If no plan is saved or if the plan file cannot be read, SavedPlan returns
// an error.
func (wd *WorkingDir) SavedPlan() (*tfjson.Plan, error) {
	if !wd.HasSavedPlan() {
		return nil, fmt.Errorf("there is no current saved plan")
	}

	var ret tfjson.Plan
	args := []string{"show"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-json", wd.planFilename())
	err := wd.runTerraformJSON(&ret, args...)
	if err != nil {
		return nil, err
	}
	if err := ret.Validate(); err != nil {
		return nil, err
	}
	return &ret, nil
}

// RequireSavedPlan is a variant of SavedPlan that will fail the test via
// the given TestControl if the plan cannot be read.
func (wd *WorkingDir) RequireSavedPlan(t TestControl) *tfjson.Plan {
	t.Helper()
	ret, err := wd.SavedPlan()
	if err != nil {
		t := testingT{t}
		t.Fatalf("failed to read saved plan: %s", err)
	}
	return ret
}

// State returns an object describing the current state.
//
// If the state cannot be read, State returns an error.
func (wd *WorkingDir) State() (*tfjson.State, error) {
	var ret tfjson.State
	args := []string{"show"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-json")
	err := wd.runTerraformJSON(&ret, args...)
	if err != nil {
		return nil, err
	}
	if err := ret.Validate(); err != nil {
		return nil, err
	}
	return &ret, nil
}

// RequireState is a variant of State that will fail the test via
// the given TestControl if the state cannot be read.
func (wd *WorkingDir) RequireState(t TestControl) *tfjson.State {
	t.Helper()
	ret, err := wd.State()
	if err != nil {
		t := testingT{t}
		t.Fatalf("failed to read state plan: %s", err)
	}
	return ret
}

// Import runs terraform import
func (wd *WorkingDir) Import(resource, id string) error {
	args := []string{"import"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-config="+wd.configDir, resource, id)
	return wd.runTerraform(args...)
}

// RequireImport is a variant of Import that will fail the test via
// the given TestControl if the import is non successful.
func (wd *WorkingDir) RequireImport(t TestControl, resource, id string) {
	t.Helper()
	if err := wd.Import(resource, id); err != nil {
		t := testingT{t}
		t.Fatalf("failed to import: %s", err)
	}
}

// Refresh runs terraform refresh
func (wd *WorkingDir) Refresh() error {
	args := []string{"refresh"}
	args = append(args, wd.baseArgs...)
	return wd.runTerraform(args...)
}

// RequireRefresh is a variant of Refresh that will fail the test via
// the given TestControl if the refresh is non successful.
func (wd *WorkingDir) RequireRefresh(t TestControl) {
	t.Helper()
	if err := wd.Refresh(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to refresh: %s", err)
	}
}
<commit_msg>Avoid implicit refreshing<commit_after>package tftest

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	tfjson "github.com/hashicorp/terraform-json"
)

// WorkingDir represents a distinct working directory that can be used for
// running tests. Each test should construct its own WorkingDir by calling
// NewWorkingDir or RequireNewWorkingDir on its package's singleton
// tftest.Helper.
type WorkingDir struct {
	h *Helper

	// baseDir is the root of the working directory tree
	baseDir string

	// baseArgs is arguments that should be appended to all commands
	baseArgs []string

	// configDir contains the singular config file generated for each test
	configDir string
}

// Close deletes the directories and files created to represent the receiving
// working directory. After this method is called, the working directory object
// is invalid and may no longer be used.
func (wd *WorkingDir) Close() error {
	return os.RemoveAll(wd.baseDir)
}

// SetConfig sets a new configuration for the working directory.
//
// This must be called at least once before any call to Init, Plan, Apply, or
// Destroy to establish the configuration. Any previously-set configuration is
// discarded and any saved plan is cleared.
func (wd *WorkingDir) SetConfig(cfg string) error {
	// Each call to SetConfig creates a new directory under our baseDir.
	// We create them within so that our final cleanup step will delete them
	// automatically without any additional tracking.
	configDir, err := ioutil.TempDir(wd.baseDir, "config")
	if err != nil {
		return err
	}
	configFilename := filepath.Join(configDir, "terraform_plugin_test.tf")
	err = ioutil.WriteFile(configFilename, []byte(cfg), 0700)
	if err != nil {
		return err
	}

	wd.configDir = configDir

	// Changing configuration invalidates any saved plan.
	err = wd.ClearPlan()
	if err != nil {
		return err
	}
	return nil
}

// RequireSetConfig is a variant of SetConfig that will fail the test via the
// given TestControl if the configuration cannot be set.
func (wd *WorkingDir) RequireSetConfig(t TestControl, cfg string) {
	t.Helper()
	if err := wd.SetConfig(cfg); err != nil {
		t := testingT{t}
		t.Fatalf("failed to set config: %s", err)
	}
}

// ClearState deletes any Terraform state present in the working directory.
//
// Any remote objects tracked by the state are not destroyed first, so this
// will leave them dangling in the remote system.
func (wd *WorkingDir) ClearState() error {
	err := os.Remove(filepath.Join(wd.baseDir, "terraform.tfstate"))
	if os.IsNotExist(err) {
		return nil
	}
	return err
}

// RequireClearState is a variant of ClearState that will fail the test via the
// given TestControl if the state cannot be cleared.
func (wd *WorkingDir) RequireClearState(t TestControl) {
	t.Helper()
	if err := wd.ClearState(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to clear state: %s", err)
	}
}

// ClearPlan deletes any saved plan present in the working directory.
func (wd *WorkingDir) ClearPlan() error {
	err := os.Remove(wd.planFilename())
	if os.IsNotExist(err) {
		return nil
	}
	return err
}

// RequireClearPlan is a variant of ClearPlan that will fail the test via the
// given TestControl if the plan cannot be cleared.
func (wd *WorkingDir) RequireClearPlan(t TestControl) {
	t.Helper()
	if err := wd.ClearPlan(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to clear plan: %s", err)
	}
}

func (wd *WorkingDir) init(pluginDir string) error {
	args := []string{"init"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-plugin-dir="+pluginDir, wd.configDir)
	return wd.runTerraform(args...)
}

// Init runs "terraform init" for the given working directory, forcing Terraform
// to use the current version of the plugin under test.
func (wd *WorkingDir) Init() error {
	if wd.configDir == "" {
		return fmt.Errorf("must call SetConfig before Init")
	}
	return wd.init(wd.h.PluginDir())
}

// RequireInit is a variant of Init that will fail the test via the given
// TestControl if init fails.
func (wd *WorkingDir) RequireInit(t TestControl) {
	t.Helper()
	if err := wd.Init(); err != nil {
		t := testingT{t}
		t.Fatalf("init failed: %s", err)
	}
}

// InitPrevious runs "terraform init" for the given working directory, forcing
// Terraform to use the previous version of the plugin under test.
//
// This method will panic if no previous plugin version is available. Use
// HasPreviousVersion or RequirePreviousVersion on the test helper singleton
// to check this first.
func (wd *WorkingDir) InitPrevious() error {
	if wd.configDir == "" {
		return fmt.Errorf("must call SetConfig before InitPrevious")
	}
	return wd.init(wd.h.PreviousPluginDir())
}

// RequireInitPrevious is a variant of InitPrevious that will fail the test
// via the given TestControl if init fails.
func (wd *WorkingDir) RequireInitPrevious(t TestControl) {
	t.Helper()
	if err := wd.InitPrevious(); err != nil {
		t := testingT{t}
		t.Fatalf("init failed: %s", err)
	}
}

func (wd *WorkingDir) planFilename() string {
	return filepath.Join(wd.baseDir, "tfplan")
}

// CreatePlan runs "terraform plan" to create a saved plan file, which if successful
// will then be used for the next call to Apply.
func (wd *WorkingDir) CreatePlan() error {
	args := []string{"plan", "-refresh=false"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-out=tfplan", wd.configDir)
	return wd.runTerraform(args...)
}

// RequireCreatePlan is a variant of CreatePlan that will fail the test via
// the given TestControl if plan creation fails.
func (wd *WorkingDir) RequireCreatePlan(t TestControl) {
	t.Helper()
	if err := wd.CreatePlan(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to create plan: %s", err)
	}
}

// Apply runs "terraform apply". If CreatePlan has previously completed
// successfully and the saved plan has not been cleared in the meantime then
// this will apply the saved plan. Otherwise, it will implicitly create a new
// plan and apply it.
func (wd *WorkingDir) Apply() error {
	args := []string{"apply", "-refresh=false"}
	args = append(args, wd.baseArgs...)

	if wd.HasSavedPlan() {
		args = append(args, "tfplan")
	} else {
		args = append(args, "-auto-approve")
		args = append(args, wd.configDir)
	}

	return wd.runTerraform(args...)
}

// RequireApply is a variant of Apply that will fail the test via
// the given TestControl if the apply operation fails.
func (wd *WorkingDir) RequireApply(t TestControl) {
	t.Helper()
	if err := wd.Apply(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to apply: %s", err)
	}
}

// Destroy runs "terraform destroy". It does not consider or modify any saved
// plan, and is primarily for cleaning up at the end of a test run.
//
// If destroy fails then remote objects might still exist, and continue to
// exist after a particular test is concluded.
func (wd *WorkingDir) Destroy() error {
	args := []string{"destroy", "-refresh=false"}
	args = append(args, wd.baseArgs...)

	args = append(args, "-auto-approve", wd.configDir)
	return wd.runTerraform(args...)
}

// RequireDestroy is a variant of Destroy that will fail the test via
// the given TestControl if the destroy operation fails.
//
// If destroy fails then remote objects might still exist, and continue to
// exist after a particular test is concluded.
func (wd *WorkingDir) RequireDestroy(t TestControl) {
	t.Helper()
	if err := wd.Destroy(); err != nil {
		t := testingT{t}
		t.Logf("WARNING: destroy failed, so remote objects may still exist and be subject to billing")
		t.Fatalf("failed to destroy: %s", err)
	}
}

// HasSavedPlan returns true if there is a saved plan in the working directory. If
// so, a subsequent call to Apply will apply that saved plan.
func (wd *WorkingDir) HasSavedPlan() bool {
	_, err := os.Stat(wd.planFilename())
	return err == nil
}

// SavedPlan returns an object describing the current saved plan file, if any.
//
// If no plan is saved or if the plan file cannot be read, SavedPlan returns
// an error.
func (wd *WorkingDir) SavedPlan() (*tfjson.Plan, error) {
	if !wd.HasSavedPlan() {
		return nil, fmt.Errorf("there is no current saved plan")
	}

	var ret tfjson.Plan
	args := []string{"show"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-json", wd.planFilename())
	err := wd.runTerraformJSON(&ret, args...)
	if err != nil {
		return nil, err
	}
	if err := ret.Validate(); err != nil {
		return nil, err
	}
	return &ret, nil
}

// RequireSavedPlan is a variant of SavedPlan that will fail the test via
// the given TestControl if the plan cannot be read.
func (wd *WorkingDir) RequireSavedPlan(t TestControl) *tfjson.Plan {
	t.Helper()
	ret, err := wd.SavedPlan()
	if err != nil {
		t := testingT{t}
		t.Fatalf("failed to read saved plan: %s", err)
	}
	return ret
}

// State returns an object describing the current state.
//
// If the state cannot be read, State returns an error.
func (wd *WorkingDir) State() (*tfjson.State, error) {
	var ret tfjson.State
	args := []string{"show"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-json")
	err := wd.runTerraformJSON(&ret, args...)
	if err != nil {
		return nil, err
	}
	if err := ret.Validate(); err != nil {
		return nil, err
	}
	return &ret, nil
}

// RequireState is a variant of State that will fail the test via
// the given TestControl if the state cannot be read.
func (wd *WorkingDir) RequireState(t TestControl) *tfjson.State {
	t.Helper()
	ret, err := wd.State()
	if err != nil {
		t := testingT{t}
		t.Fatalf("failed to read state plan: %s", err)
	}
	return ret
}

// Import runs terraform import
func (wd *WorkingDir) Import(resource, id string) error {
	args := []string{"import"}
	args = append(args, wd.baseArgs...)
	args = append(args, "-config="+wd.configDir, resource, id)
	return wd.runTerraform(args...)
}

// RequireImport is a variant of Import that will fail the test via
// the given TestControl if the import is non successful.
func (wd *WorkingDir) RequireImport(t TestControl, resource, id string) {
	t.Helper()
	if err := wd.Import(resource, id); err != nil {
		t := testingT{t}
		t.Fatalf("failed to import: %s", err)
	}
}

// Refresh runs terraform refresh
func (wd *WorkingDir) Refresh() error {
	args := []string{"refresh"}
	args = append(args, wd.baseArgs...)
	return wd.runTerraform(args...)
}

// RequireRefresh is a variant of Refresh that will fail the test via
// the given TestControl if the refresh is non successful.
func (wd *WorkingDir) RequireRefresh(t TestControl) {
	t.Helper()
	if err := wd.Refresh(); err != nil {
		t := testingT{t}
		t.Fatalf("failed to refresh: %s", err)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2012 the Minima Authors under the MIT license. See AUTHORS for the list of authors.

package world

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"runtime"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"code.google.com/p/min-game/geom"
)

// MaxElevation is the number of distinct
// elevations, numbered 0..MaxElevation.
const MaxElevation = 19

// TileSize is the size of a world tile in pixels.
var TileSize = geom.Pt(32, 32)

// World is the main container for the world
// representation of minima.
type World struct {
	// Pixels is a Torus in the dimension of the world in pixels.
	Pixels geom.Torus

	// Could use a Torus for W and X, below, but it seems
	// needless because they will always be nice and simple
	// ints.

	// W and H are the width and height of the world's
	// location grid.
	W, H int

	// locs is the grid of world locations.
	locs []Loc

	// X0 and Y0 are the start location.
	X0, Y0 int
}

// A Loc is a cell in the grid that represents the world
type Loc struct {
	// Terrain is the type of this locations terrain.
	Terrain *TerrainType

	// X and Y are the coordinates of this location
	X, Y int

	// Elevation is the elevation of the ground or the
	// surface of a body of water.
	Elevation int

	// Depth is the depth of water at this location.
	Depth int
}

// Height returns the elevation of the location minus
// its depth.  This is effectively the elevation of the ground
// or the elevation of the floor of a body of water.
func (l Loc) Height() int {
	return l.Elevation - l.Depth
}

// Point returns the point representing the upper-left corner
// of this location.
func (l Loc) Point() geom.Point {
	return geom.Pt(float64(l.X)*TileSize.X, float64(l.Y)*TileSize.Y)
}

// TerrainType holds information on a given type of terrain.
type TerrainType struct {
	// Char is the character representing this terrain type.
	Char string

	// Name is a human readable name of the terrain type.
	Name string
}

// Terrain is an array with the canonical terrain
// representations, indexed by the terrain type's
// unique character.
var Terrain = []TerrainType{
	'g': {"g", "Grass"},
	'm': {"m", "Mountain"},
	'w': {"w", "Water"},
	'l': {"l", "Lava"},
	'd': {"d", "Desert"},
	'f': {"f", "Tree"},
	'i': {"i", "Glacier"},
}

// New returns a world of the given dimensions.
func New(w, h int) *World {
	const maxInt = int(^uint(0) >> 1)
	if w <= 0 || h <= 0 {
		panic("World dimensions must be positive")
	}
	if maxInt/w < h {
		panic("The world dimensions are too big")
	}
	return &World{
		Pixels: geom.Torus{
			W: float64(w) * TileSize.X,
			H: float64(h) * TileSize.Y,
		},
		W:    w,
		H:    h,
		locs: makeLocs(w, h),
	}
}

// makeLocs returns an array of locations with
// initialized X and Y fields.
func makeLocs(w, h int) []Loc {
	locs := make([]Loc, w*h)
	for x := 0; x < w; x++ {
		for y := 0; y < h; y++ {
			l := &locs[x*h+y]
			l.X = x
			l.Y = y
		}
	}
	return locs
}

// At returns the location at the given world coordinate.
func (w *World) At(x, y int) *Loc {
	x, y = w.Wrap(x, y)
	return &w.locs[x*w.H+y]
}

// Wrap returns an x,y within the ranges 0–width-1 and
// 0–height-1.  This effectively maps world coordinates
// to a normalized point on a grid, making the world a
// torus shape.
func (w *World) Wrap(x, y int) (int, int) {
	return wrap(x, w.W), wrap(y, w.H)
}

func (w *World) Tile(p geom.Point) (int, int) {
	return int(math.Floor(p.X / TileSize.X)),
		int(math.Floor(p.Y / TileSize.Y))
}

// wrap returns the value of n wrapped around if it goes
// above bound-1 or below zero.
func wrap(n, bound int) int {
	if n >= 0 && n < bound {
		return n
	}

	if bound <= 0 {
		panic("Bad bound in wrap")
	}
	n %= bound
	if n < 0 {
		n = bound + n
		if n < 0 {
			panic("A value wrapped to a negative")
		}
	}
	return n
}

// LocsWithType returns a slice of pointers to all of the
// locations with any of the given types.
func (w *World) LocsWithType(types string) []*Loc {
	var locs []*Loc
	for i, loc := range w.locs {
		if strings.Contains(types, loc.Terrain.Char) {
			locs = append(locs, &w.locs[i])
		}
	}
	return locs
}

// Write writes a world.  If the writer is not buffered then
// it is wrapped in a buffered writer, so the caller does not
// need to worry about buffering writes.
func (w *World) Write(out io.Writer) error {
	var err error
	if _, err = fmt.Fprintln(out, "#", runtime.GOOS, runtime.GOARCH); err != nil {
		return err
	}
	if _, err = fmt.Fprintln(out, w.W, w.H); err != nil {
		return err
	}
	var n int
	for i := 0; i < len(w.locs); i += n {
		l := w.locs[i]
		if l.Terrain == nil {
			panic("Nil terrain")
		}

		for n = 1; i+n < len(w.locs); n++ {
			l2 := w.locs[i+n]
			if l.Terrain != l2.Terrain || l.Elevation != l2.Elevation || l.Depth != l2.Depth {
				break
			}
		}
		if n > 1 {
			fmt.Fprintln(out, n)
		}
		if _, err = fmt.Fprintf(out, "%s %d %d\n", l.Terrain.Char, l.Elevation, l.Depth); err != nil {
			return err
		}
	}
	_, err = fmt.Fprintln(out, w.X0, w.Y0)
	return err
}

// Read reads a world.  If an error is encountered then
// the error is returned.
func Read(in *bufio.Reader) (*World, error) {
	var err error
	var line string
	if line, err = readLine(in); err != nil {
		return nil, err
	}
	var width, height int
	if _, err = fmt.Sscanln(line, &width, &height); err != nil {
		fmt.Fprintln(os.Stderr, "failed to scan", line)
		return nil, err
	}

	w := New(width, height)

	var el, dp int
	var ch rune
	var repeat int

	for i := range w.locs {
		if repeat > 0 {
			w.locs[i].Terrain = &Terrain[ch]
			w.locs[i].Elevation = el
			w.locs[i].Depth = dp
			repeat--
			continue
		}

		if line, err = readLine(in); err != nil {
			return nil, err
		}

		r, _ := utf8.DecodeRuneInString(line)
		if unicode.IsDigit(r) {
			repeat, err = strconv.Atoi(line)
			if err != nil {
				return nil, fmt.Errorf("Bad run-length encoding line=[%s]: %s", line, err)
			}
			if line, err = readLine(in); err != nil {
				return nil, err
			}
			repeat-- // we are about to read and set the 1st occurrence
		}

		if _, err = fmt.Sscanf(line, "%c %d %d", &ch, &el, &dp); err != nil {
			return nil, fmt.Errorf("Failed to scan line [%s]: %s", line, err)
		}
		if el < 0 || el > MaxElevation {
			return nil, fmt.Errorf("Location %d: elevation %d is out of bounds", i, el)
		}
		if dp > el {
			return nil, fmt.Errorf("Location %d: depth is greater than elevation", i)
		}
		if int(ch) >= len(Terrain) || Terrain[ch].Char == "" {
			return nil, fmt.Errorf("Location %d: invalid terrain: %c", i, ch)
		}
		w.locs[i].Terrain = &Terrain[ch]
		w.locs[i].Elevation = el
		w.locs[i].Depth = dp
	}

	if line, err = readLine(in); err != nil {
		return nil, err
	}
	_, err = fmt.Sscanln(line, &w.X0, &w.Y0)
	if err != nil {
		err = fmt.Errorf("Failed to scan initial location [%s]: %s", line, err)
	}
	return w, err
}

// ReadLine returns the next non-comment line.  On error
// the empty string and error are returned.
func readLine(in *bufio.Reader) (string, error) {
	for {
		bytes, prefix, err := in.ReadLine()
		if prefix {
			err = errors.New("Line is too long")
		}
		if err != nil {
			return "", err
		}
		if bytes[0] != '#' {
			return string(bytes), nil
		}
	}
	panic("Unreachable")
}
<commit_msg>Fix world.go's wrap.<commit_after>// Copyright © 2012 the Minima Authors under the MIT license. See AUTHORS for the list of authors.

package world

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"runtime"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"code.google.com/p/min-game/geom"
)

// MaxElevation is the number of distinct
// elevations, numbered 0..MaxElevation.
const MaxElevation = 19

// TileSize is the size of a world tile in pixels.
var TileSize = geom.Pt(32, 32)

// World is the main container for the world
// representation of minima.
type World struct {
	// Pixels is a Torus in the dimension of the world in pixels.
	Pixels geom.Torus

	// Could use a Torus for W and X, below, but it seems
	// needless because they will always be nice and simple
	// ints.

	// W and H are the width and height of the world's
	// location grid.
	W, H int

	// locs is the grid of world locations.
	locs []Loc

	// X0 and Y0 are the start location.
	X0, Y0 int
}

// A Loc is a cell in the grid that represents the world
type Loc struct {
	// Terrain is the type of this locations terrain.
	Terrain *TerrainType

	// X and Y are the coordinates of this location
	X, Y int

	// Elevation is the elevation of the ground or the
	// surface of a body of water.
	Elevation int

	// Depth is the depth of water at this location.
	Depth int
}

// Height returns the elevation of the location minus
// its depth.  This is effectively the elevation of the ground
// or the elevation of the floor of a body of water.
func (l Loc) Height() int {
	return l.Elevation - l.Depth
}

// Point returns the point representing the upper-left corner
// of this location.
func (l Loc) Point() geom.Point {
	return geom.Pt(float64(l.X)*TileSize.X, float64(l.Y)*TileSize.Y)
}

// TerrainType holds information on a given type of terrain.
type TerrainType struct {
	// Char is the character representing this terrain type.
	Char string

	// Name is a human readable name of the terrain type.
	Name string
}

// Terrain is an array with the canonical terrain
// representations, indexed by the terrain type's
// unique character.
var Terrain = []TerrainType{
	'g': {"g", "Grass"},
	'm': {"m", "Mountain"},
	'w': {"w", "Water"},
	'l': {"l", "Lava"},
	'd': {"d", "Desert"},
	'f': {"f", "Tree"},
	'i': {"i", "Glacier"},
}

// New returns a world of the given dimensions.
func New(w, h int) *World {
	const maxInt = int(^uint(0) >> 1)
	if w <= 0 || h <= 0 {
		panic("World dimensions must be positive")
	}
	if maxInt/w < h {
		panic("The world dimensions are too big")
	}
	return &World{
		Pixels: geom.Torus{
			W: float64(w) * TileSize.X,
			H: float64(h) * TileSize.Y,
		},
		W:    w,
		H:    h,
		locs: makeLocs(w, h),
	}
}

// makeLocs returns an array of locations with
// initialized X and Y fields.
func makeLocs(w, h int) []Loc {
	locs := make([]Loc, w*h)
	for x := 0; x < w; x++ {
		for y := 0; y < h; y++ {
			l := &locs[x*h+y]
			l.X = x
			l.Y = y
		}
	}
	return locs
}

// At returns the location at the given world coordinate.
func (w *World) At(x, y int) *Loc {
	x, y = w.Wrap(x, y)
	return &w.locs[x*w.H+y]
}

// Wrap returns an x,y within the ranges 0–width-1 and
// 0–height-1.  This effectively maps world coordinates
// to a normalized point on a grid, making the world a
// torus shape.
func (w *World) Wrap(x, y int) (int, int) {
	return wrap(x, w.W), wrap(y, w.H)
}

func (w *World) Tile(p geom.Point) (int, int) {
	return int(math.Floor(p.X / TileSize.X)),
		int(math.Floor(p.Y / TileSize.Y))
}

// wrap returns the value of n wrapped around if it goes
// above bound-1 or below zero.
func wrap(n, bound int) int {
	if bound <= 0 {
		panic("Bad bound in wrap")
	}

	if n >= 0 && n < bound {
		return n
	}

	n %= bound
	if n < 0 {
		n = bound + n
	}
	return n
}

// LocsWithType returns a slice of pointers to all of the
// locations with any of the given types.
func (w *World) LocsWithType(types string) []*Loc {
	var locs []*Loc
	for i, loc := range w.locs {
		if strings.Contains(types, loc.Terrain.Char) {
			locs = append(locs, &w.locs[i])
		}
	}
	return locs
}

// Write writes a world.  If the writer is not buffered then
// it is wrapped in a buffered writer, so the caller does not
// need to worry about buffering writes.
func (w *World) Write(out io.Writer) error {
	var err error
	if _, err = fmt.Fprintln(out, "#", runtime.GOOS, runtime.GOARCH); err != nil {
		return err
	}
	if _, err = fmt.Fprintln(out, w.W, w.H); err != nil {
		return err
	}
	var n int
	for i := 0; i < len(w.locs); i += n {
		l := w.locs[i]
		if l.Terrain == nil {
			panic("Nil terrain")
		}

		for n = 1; i+n < len(w.locs); n++ {
			l2 := w.locs[i+n]
			if l.Terrain != l2.Terrain || l.Elevation != l2.Elevation || l.Depth != l2.Depth {
				break
			}
		}
		if n > 1 {
			fmt.Fprintln(out, n)
		}
		if _, err = fmt.Fprintf(out, "%s %d %d\n", l.Terrain.Char, l.Elevation, l.Depth); err != nil {
			return err
		}
	}
	_, err = fmt.Fprintln(out, w.X0, w.Y0)
	return err
}

// Read reads a world.  If an error is encountered then
// the error is returned.
func Read(in *bufio.Reader) (*World, error) {
	var err error
	var line string
	if line, err = readLine(in); err != nil {
		return nil, err
	}
	var width, height int
	if _, err = fmt.Sscanln(line, &width, &height); err != nil {
		fmt.Fprintln(os.Stderr, "failed to scan", line)
		return nil, err
	}

	w := New(width, height)

	var el, dp int
	var ch rune
	var repeat int

	for i := range w.locs {
		if repeat > 0 {
			w.locs[i].Terrain = &Terrain[ch]
			w.locs[i].Elevation = el
			w.locs[i].Depth = dp
			repeat--
			continue
		}

		if line, err = readLine(in); err != nil {
			return nil, err
		}

		r, _ := utf8.DecodeRuneInString(line)
		if unicode.IsDigit(r) {
			repeat, err = strconv.Atoi(line)
			if err != nil {
				return nil, fmt.Errorf("Bad run-length encoding line=[%s]: %s", line, err)
			}
			if line, err = readLine(in); err != nil {
				return nil, err
			}
			repeat-- // we are about to read and set the 1st occurrence
		}

		if _, err = fmt.Sscanf(line, "%c %d %d", &ch, &el, &dp); err != nil {
			return nil, fmt.Errorf("Failed to scan line [%s]: %s", line, err)
		}
		if el < 0 || el > MaxElevation {
			return nil, fmt.Errorf("Location %d: elevation %d is out of bounds", i, el)
		}
		if dp > el {
			return nil, fmt.Errorf("Location %d: depth is greater than elevation", i)
		}
		if int(ch) >= len(Terrain) || Terrain[ch].Char == "" {
			return nil, fmt.Errorf("Location %d: invalid terrain: %c", i, ch)
		}
		w.locs[i].Terrain = &Terrain[ch]
		w.locs[i].Elevation = el
		w.locs[i].Depth = dp
	}

	if line, err = readLine(in); err != nil {
		return nil, err
	}
	_, err = fmt.Sscanln(line, &w.X0, &w.Y0)
	if err != nil {
		err = fmt.Errorf("Failed to scan initial location [%s]: %s", line, err)
	}
	return w, err
}

// ReadLine returns the next non-comment line.  On error
// the empty string and error are returned.
func readLine(in *bufio.Reader) (string, error) {
	for {
		bytes, prefix, err := in.ReadLine()
		if prefix {
			err = errors.New("Line is too long")
		}
		if err != nil {
			return "", err
		}
		if bytes[0] != '#' {
			return string(bytes), nil
		}
	}
	panic("Unreachable")
}
<|endoftext|>


<commit_before>package delmo

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

func writeFiles(name, config, compose string, t *testing.T) (string, string) {
	tmpDir, err := ioutil.TempDir("", name)
	if err != nil {
		t.Fatal("Couldn't create temp dir", err)
	}

	configFile := fmt.Sprintf("%s/%s", tmpDir, "delmo.yml")
	err = ioutil.WriteFile(configFile, []byte(config), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write config", err)
	}
	t.Logf("Written config file %s", configFile)

	composeFile := fmt.Sprintf("%s/%s", tmpDir, "docker-compose.yml")
	err = ioutil.WriteFile(composeFile, []byte(compose), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write compose", err)
	}
	t.Logf("Written compose files %s", composeFile)

	return tmpDir, configFile
}

func TestConfig_Load(t *testing.T) {
	t.Parallel()

	config := `
system:
  name: test
  file: docker-compose.yml

tests:
- name: simple
  spec:
  - {stop: [redis]}
  - {start: [redis]}`

	compose := `
version: '2'
services:
  redis:
    image: redis`

	tmpDir, configFile := writeFiles("TestSuite_Load", config, compose, t)
	defer os.Remove(tmpDir)

	suite, err := LoadConfig(configFile)
	if err != nil {
		t.Fatal("Load Suite Failed!", err)
	}

	if want, got := "test", suite.System.Name; want != got {
		t.Errorf("Name not correct. Want: %s, got: %s", want, got)
	}

	service, ok := suite.System.Services["redis"]
	if !ok {
		t.Errorf("System did not load compose file correctly. Missing service %s", "redis")
	}
	if want, got := "redis", service.Image; want != got {
		t.Errorf("Image not set correctly. Want: %s, got: %s", want, got)
	}
	if want, got := 2, len(suite.Tests[0].Spec); want != got {
		t.Errorf("Spec not parsed correctly. Want: %d, got: %d", want, got)
	}
}
<commit_msg>Wording in config_test<commit_after>package delmo

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

func writeFiles(name, config, compose string, t *testing.T) (string, string) {
	tmpDir, err := ioutil.TempDir("", name)
	if err != nil {
		t.Fatal("Couldn't create temp dir", err)
	}

	configFile := fmt.Sprintf("%s/%s", tmpDir, "delmo.yml")
	err = ioutil.WriteFile(configFile, []byte(config), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write config", err)
	}
	t.Logf("Written config file %s", configFile)

	composeFile := fmt.Sprintf("%s/%s", tmpDir, "docker-compose.yml")
	err = ioutil.WriteFile(composeFile, []byte(compose), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write compose", err)
	}
	t.Logf("Written compose files %s", composeFile)

	return tmpDir, configFile
}

func TestConfig_Load(t *testing.T) {
	t.Parallel()

	config := `
system:
  name: test
  file: docker-compose.yml

tests:
- name: simple
  spec:
  - {stop: [redis]}
  - {start: [redis]}`

	compose := `
version: '2'
services:
  redis:
    image: redis
    build: redis`

	tmpDir, configFile := writeFiles("TestSuite_Load", config, compose, t)
	defer os.Remove(tmpDir)

	suite, err := LoadConfig(configFile)
	if err != nil {
		t.Fatal("Load Suite Failed!", err)
	}

	if want, got := "test", suite.System.Name; want != got {
		t.Errorf("Name not correct. Want: %s, got: %s", want, got)
	}

	service, ok := suite.System.Services["redis"]
	if !ok {
		t.Errorf("Compose file not read correctly. Missing service %s", "redis")
	}
	if want, got := "redis", service.Image; want != got {
		t.Errorf("Image not set correctly. Want: %s, got: %s", want, got)
	}
	if want, got := 2, len(suite.Tests[0].Spec); want != got {
		t.Errorf("Spec not parsed correctly. Want: %d, got: %d", want, got)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"strings"
	"testing"

	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/lease"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestMultiReadProxy(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// A ReadProxy that wraps another, calling CheckInvariants before and after
// each action.
type checkingReadProxy struct {
	Wrapped lease.ReadProxy
}

func (crp *checkingReadProxy) Size() (size int64) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	size = crp.Wrapped.Size()
	return
}

func (crp *checkingReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	n, err = crp.Wrapped.ReadAt(ctx, p, off)
	return
}

func (crp *checkingReadProxy) Upgrade(
	ctx context.Context) (rwl lease.ReadWriteLease, err error) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	rwl, err = crp.Wrapped.Upgrade(ctx)
	return
}

func (crp *checkingReadProxy) Destroy() {
	crp.Wrapped.CheckInvariants()
	crp.Wrapped.Destroy()
}

func (crp *checkingReadProxy) CheckInvariants() {
	crp.Wrapped.CheckInvariants()
}

// A range to read, the contents we expect to get back, and an error we expect
// to be included as a substring in the returned error, if any.
type readAtTestCase struct {
	start            int64
	limit            int64
	expectedErr      error
	expectedContents string
}

func runReadAtTestCases(
	rp lease.ReadProxy,
	cases []readAtTestCase) {
	for i, tc := range cases {
		desc := fmt.Sprintf("Test case %d: [%d, %d)", i, tc.start, tc.limit)

		AssertLe(tc.start, tc.limit)
		buf := make([]byte, tc.limit-tc.start)

		n, err := rp.ReadAt(context.Background(), buf, tc.start)
		AssertEq(tc.expectedContents, string(buf[:n]), "%s", desc)

		if tc.expectedErr == nil {
			AssertEq(nil, err, "%s", desc)
		} else {
			AssertThat(err, Error(HasSubstr(tc.expectedErr.Error())))
		}
	}
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

// Canned contents returned by the refreshers.
var refresherContents = []string{
	"taco",
	"burrito",
	"enchilada",
}

type MultiReadProxyTest struct {
	ctx context.Context

	// Canned errors returned by the refreshers.
	refresherErrors []error

	leaser lease.FileLeaser
	proxy  *checkingReadProxy
}

var _ SetUpInterface = &MultiReadProxyTest{}
var _ TearDownInterface = &MultiReadProxyTest{}

func init() { RegisterTestSuite(&MultiReadProxyTest{}) }

func (t *MultiReadProxyTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
	t.leaser = lease.NewFileLeaser("", math.MaxInt64)
	t.refresherErrors = make([]error, len(refresherContents))

	// Create the proxy.
	t.proxy = &checkingReadProxy{
		Wrapped: lease.NewMultiReadProxy(
			t.leaser,
			t.makeRefreshers(),
			nil),
	}
}

func (t *MultiReadProxyTest) TearDown() {
	// Make sure nothing goes crazy.
	t.proxy.Destroy()
}

func (t *MultiReadProxyTest) makeRefreshers() (refreshers []lease.Refresher) {
	for i := range refresherContents {
		iCopy := i
		contents := refresherContents[i]

		r := &funcRefresher{
			N: int64(len(contents)),
			F: func(ctx context.Context) (rc io.ReadCloser, err error) {
				rc = ioutil.NopCloser(strings.NewReader(contents))
				err = t.refresherErrors[iCopy]
				return
			},
		}

		refreshers = append(refreshers, r)
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *MultiReadProxyTest) SizeZero_NoRefreshers() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) SizeZero_WithRefreshers() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Size() {
	var expected int64
	for _, contents := range refresherContents {
		expected += int64(len(contents))
	}

	ExpectEq(expected, t.proxy.Size())
}

func (t *MultiReadProxyTest) ReadAt_NegativeOffset() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) ReadAt_OneRefresherReturnsError() {
	AssertThat(
		refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	AssertEq(4, len(refresherContents[0]))
	AssertEq(7, len(refresherContents[1]))
	AssertEq(9, len(refresherContents[2]))

	// Configure an error for the middle read lease.
	someErr := errors.New("foobar")
	t.refresherErrors[1] = someErr

	// Test cases.
	// error we expect to see, if any.
	testCases := []readAtTestCase{
		// First read lease only.
		readAtTestCase{0, 0, nil, ""},
		readAtTestCase{0, 1, nil, "t"},
		readAtTestCase{0, 4, nil, "taco"},
		readAtTestCase{1, 4, nil, "aco"},
		readAtTestCase{4, 4, nil, ""},

		// First and second read leases.
		readAtTestCase{0, 5, someErr, "taco"},
		readAtTestCase{1, 11, someErr, "aco"},

		// All read leases.
		readAtTestCase{0, 20, someErr, "taco"},
		readAtTestCase{1, 20, someErr, "aco"},
		readAtTestCase{1, 100, someErr, "aco"},

		// Second read lease only.
		readAtTestCase{4, 4, nil, ""},
		readAtTestCase{4, 5, someErr, ""},
		readAtTestCase{4, 11, someErr, ""},

		// Second and third read leases.
		readAtTestCase{4, 12, someErr, ""},
		readAtTestCase{4, 20, someErr, ""},
		readAtTestCase{5, 100, someErr, ""},

		// Third read lease only.
		readAtTestCase{11, 20, nil, "enchilada"},
		readAtTestCase{11, 100, io.EOF, "enchilada"},
		readAtTestCase{12, 20, nil, "nchilada"},
		readAtTestCase{19, 20, nil, "a"},
		readAtTestCase{20, 20, io.EOF, ""},

		// Past end.
		readAtTestCase{21, 21, io.EOF, ""},
		readAtTestCase{21, 22, io.EOF, ""},
		readAtTestCase{21, 100, io.EOF, ""},
		readAtTestCase{100, 1000, io.EOF, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_AllSuccessful() {
	AssertThat(
		refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	AssertEq(4, len(refresherContents[0]))
	AssertEq(7, len(refresherContents[1]))
	AssertEq(9, len(refresherContents[2]))

	// Various ranges to read, the contents we expect to get back, and the
	// error we expect to see, if any.
	testCases := []readAtTestCase{
		// First read lease only.
		readAtTestCase{0, 0, nil, ""},
		readAtTestCase{0, 1, nil, "t"},
		readAtTestCase{0, 4, nil, "taco"},
		readAtTestCase{1, 4, nil, "aco"},
		readAtTestCase{4, 4, nil, ""},

		// First and second read leases.
		readAtTestCase{0, 5, nil, "tacob"},
		readAtTestCase{1, 11, nil, "acoburrito"},

		// All read leases.
		readAtTestCase{0, 20, nil, "tacoburritoenchilada"},
		readAtTestCase{1, 19, nil, "acoburritoenchilad"},
		readAtTestCase{3, 17, nil, "oburritoenchil"},

		// Second read lease only.
		readAtTestCase{4, 4, nil, ""},
		readAtTestCase{4, 5, nil, "b"},
		readAtTestCase{4, 11, nil, "burrito"},

		// Second and third read leases.
		readAtTestCase{4, 12, nil, "burritoe"},
		readAtTestCase{4, 20, nil, "burritoenchilada"},
		readAtTestCase{5, 100, io.EOF, "urritoenchilada"},

		// Third read lease only.
		readAtTestCase{11, 20, nil, "enchilada"},
		readAtTestCase{11, 100, io.EOF, "enchilada"},
		readAtTestCase{12, 20, nil, "nchilada"},
		readAtTestCase{19, 20, nil, "a"},
		readAtTestCase{20, 20, io.EOF, ""},

		// Past end.
		readAtTestCase{21, 21, io.EOF, ""},
		readAtTestCase{21, 22, io.EOF, ""},
		readAtTestCase{21, 100, io.EOF, ""},
		readAtTestCase{100, 1000, io.EOF, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_ContentAlreadyCached() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_OneRefresherReturnsError() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_AllSuccessful() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_ContentAlreadyCached() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) InitialReadLeaseValid() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) InitialReadLeaseRevoked() {
	AssertTrue(false, "TODO")
}
<commit_msg>Made error matching more flexible.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"strings"
	"testing"

	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/lease"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestMultiReadProxy(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// A ReadProxy that wraps another, calling CheckInvariants before and after
// each action.
type checkingReadProxy struct {
	Wrapped lease.ReadProxy
}

func (crp *checkingReadProxy) Size() (size int64) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	size = crp.Wrapped.Size()
	return
}

func (crp *checkingReadProxy) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	n, err = crp.Wrapped.ReadAt(ctx, p, off)
	return
}

func (crp *checkingReadProxy) Upgrade(
	ctx context.Context) (rwl lease.ReadWriteLease, err error) {
	crp.Wrapped.CheckInvariants()
	defer crp.Wrapped.CheckInvariants()

	rwl, err = crp.Wrapped.Upgrade(ctx)
	return
}

func (crp *checkingReadProxy) Destroy() {
	crp.Wrapped.CheckInvariants()
	crp.Wrapped.Destroy()
}

func (crp *checkingReadProxy) CheckInvariants() {
	crp.Wrapped.CheckInvariants()
}

// A range to read, the contents we expect to get back, and a matcher for the
// returned error. Special case: a nil matcher means Equals(nil).
type readAtTestCase struct {
	start            int64
	limit            int64
	errMatcher       Matcher
	expectedContents string
}

func runReadAtTestCases(
	rp lease.ReadProxy,
	cases []readAtTestCase) {
	for i, tc := range cases {
		desc := fmt.Sprintf("Test case %d: [%d, %d)", i, tc.start, tc.limit)

		AssertLe(tc.start, tc.limit)
		buf := make([]byte, tc.limit-tc.start)

		n, err := rp.ReadAt(context.Background(), buf, tc.start)
		AssertEq(tc.expectedContents, string(buf[:n]), "%s", desc)

		if tc.errMatcher == nil {
			AssertEq(nil, err, "%s", desc)
		} else {
			ExpectThat(err, tc.errMatcher, desc)
		}
	}
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

// Canned contents returned by the refreshers.
var refresherContents = []string{
	"taco",
	"burrito",
	"enchilada",
}

type MultiReadProxyTest struct {
	ctx context.Context

	// Canned errors returned by the refreshers.
	refresherErrors []error

	leaser lease.FileLeaser
	proxy  *checkingReadProxy
}

var _ SetUpInterface = &MultiReadProxyTest{}
var _ TearDownInterface = &MultiReadProxyTest{}

func init() { RegisterTestSuite(&MultiReadProxyTest{}) }

func (t *MultiReadProxyTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
	t.leaser = lease.NewFileLeaser("", math.MaxInt64)
	t.refresherErrors = make([]error, len(refresherContents))

	// Create the proxy.
	t.proxy = &checkingReadProxy{
		Wrapped: lease.NewMultiReadProxy(
			t.leaser,
			t.makeRefreshers(),
			nil),
	}
}

func (t *MultiReadProxyTest) TearDown() {
	// Make sure nothing goes crazy.
	t.proxy.Destroy()
}

func (t *MultiReadProxyTest) makeRefreshers() (refreshers []lease.Refresher) {
	for i := range refresherContents {
		iCopy := i
		contents := refresherContents[i]

		r := &funcRefresher{
			N: int64(len(contents)),
			F: func(ctx context.Context) (rc io.ReadCloser, err error) {
				rc = ioutil.NopCloser(strings.NewReader(contents))
				err = t.refresherErrors[iCopy]
				return
			},
		}

		refreshers = append(refreshers, r)
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *MultiReadProxyTest) SizeZero_NoRefreshers() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) SizeZero_WithRefreshers() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Size() {
	var expected int64
	for _, contents := range refresherContents {
		expected += int64(len(contents))
	}

	ExpectEq(expected, t.proxy.Size())
}

func (t *MultiReadProxyTest) ReadAt_NegativeOffset() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) ReadAt_OneRefresherReturnsError() {
	AssertThat(
		refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	AssertEq(4, len(refresherContents[0]))
	AssertEq(7, len(refresherContents[1]))
	AssertEq(9, len(refresherContents[2]))

	// Configure an error for the middle read lease.
	someErr := errors.New("foobar")
	t.refresherErrors[1] = someErr

	// Test cases.
	someErrMatcher := Error(HasSubstr(someErr.Error()))
	eofMatcher := Equals(io.EOF)

	testCases := []readAtTestCase{
		// First read lease only.
		readAtTestCase{0, 0, nil, ""},
		readAtTestCase{0, 1, nil, "t"},
		readAtTestCase{0, 4, nil, "taco"},
		readAtTestCase{1, 4, nil, "aco"},
		readAtTestCase{4, 4, nil, ""},

		// First and second read leases.
		readAtTestCase{0, 5, someErrMatcher, "taco"},
		readAtTestCase{1, 11, someErrMatcher, "aco"},

		// All read leases.
		readAtTestCase{0, 20, someErrMatcher, "taco"},
		readAtTestCase{1, 20, someErrMatcher, "aco"},
		readAtTestCase{1, 100, someErrMatcher, "aco"},

		// Second read lease only.
		readAtTestCase{4, 4, nil, ""},
		readAtTestCase{4, 5, someErrMatcher, ""},
		readAtTestCase{4, 11, someErrMatcher, ""},

		// Second and third read leases.
		readAtTestCase{4, 12, someErrMatcher, ""},
		readAtTestCase{4, 20, someErrMatcher, ""},
		readAtTestCase{5, 100, someErrMatcher, ""},

		// Third read lease only.
		readAtTestCase{11, 20, nil, "enchilada"},
		readAtTestCase{11, 100, eofMatcher, "enchilada"},
		readAtTestCase{12, 20, nil, "nchilada"},
		readAtTestCase{19, 20, nil, "a"},
		readAtTestCase{20, 20, eofMatcher, ""},

		// Past end.
		readAtTestCase{21, 21, eofMatcher, ""},
		readAtTestCase{21, 22, eofMatcher, ""},
		readAtTestCase{21, 100, eofMatcher, ""},
		readAtTestCase{100, 1000, eofMatcher, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_AllSuccessful() {
	AssertThat(
		refresherContents,
		ElementsAre(
			"taco",
			"burrito",
			"enchilada",
		))

	AssertEq(4, len(refresherContents[0]))
	AssertEq(7, len(refresherContents[1]))
	AssertEq(9, len(refresherContents[2]))

	// Test cases.
	eofMatcher := Equals(io.EOF)
	testCases := []readAtTestCase{
		// First read lease only.
		readAtTestCase{0, 0, nil, ""},
		readAtTestCase{0, 1, nil, "t"},
		readAtTestCase{0, 4, nil, "taco"},
		readAtTestCase{1, 4, nil, "aco"},
		readAtTestCase{4, 4, nil, ""},

		// First and second read leases.
		readAtTestCase{0, 5, nil, "tacob"},
		readAtTestCase{1, 11, nil, "acoburrito"},

		// All read leases.
		readAtTestCase{0, 20, nil, "tacoburritoenchilada"},
		readAtTestCase{1, 19, nil, "acoburritoenchilad"},
		readAtTestCase{3, 17, nil, "oburritoenchil"},

		// Second read lease only.
		readAtTestCase{4, 4, nil, ""},
		readAtTestCase{4, 5, nil, "b"},
		readAtTestCase{4, 11, nil, "burrito"},

		// Second and third read leases.
		readAtTestCase{4, 12, nil, "burritoe"},
		readAtTestCase{4, 20, nil, "burritoenchilada"},
		readAtTestCase{5, 100, eofMatcher, "urritoenchilada"},

		// Third read lease only.
		readAtTestCase{11, 20, nil, "enchilada"},
		readAtTestCase{11, 100, eofMatcher, "enchilada"},
		readAtTestCase{12, 20, nil, "nchilada"},
		readAtTestCase{19, 20, nil, "a"},
		readAtTestCase{20, 20, eofMatcher, ""},

		// Past end.
		readAtTestCase{21, 21, eofMatcher, ""},
		readAtTestCase{21, 22, eofMatcher, ""},
		readAtTestCase{21, 100, eofMatcher, ""},
		readAtTestCase{100, 1000, eofMatcher, ""},
	}

	runReadAtTestCases(t.proxy, testCases)
}

func (t *MultiReadProxyTest) ReadAt_ContentAlreadyCached() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_OneRefresherReturnsError() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_AllSuccessful() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) Upgrade_ContentAlreadyCached() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) InitialReadLeaseValid() {
	AssertTrue(false, "TODO")
}

func (t *MultiReadProxyTest) InitialReadLeaseRevoked() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>/*
Copyright 2013 CoreOS Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package test

import (
	"fmt"
	"github.com/coreos/etcd/third_party/github.com/coreos/go-etcd/etcd"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strconv"
	"time"
)

var client = http.Client{
	Transport: &http.Transport{
		Dial: dialTimeoutFast,
	},
}

// Sending set commands
func Set(stop chan bool) {

	stopSet := false
	i := 0
	c := etcd.NewClient(nil)
	for {
		key := fmt.Sprintf("%s_%v", "foo", i)

		result, err := c.Set(key, "bar", 0)

		if err != nil || result.Node.Key != "/"+key || result.Node.Value != "bar" {
			select {
			case <-stop:
				stopSet = true

			default:
			}
		}

		select {
		case <-stop:
			stopSet = true

		default:
		}

		if stopSet {
			break
		}

		i++
	}
	stop <- true
}

// Create a cluster of etcd nodes
func CreateCluster(size int, procAttr *os.ProcAttr, ssl bool) ([][]string, []*os.Process, error) {
	argGroup := make([][]string, size)

	sslServer1 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt",
		"-peer-cert-file=../../fixtures/ca/server.crt",
		"-peer-key-file=../../fixtures/ca/server.key.insecure",
	}

	sslServer2 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt",
		"-peer-cert-file=../../fixtures/ca/server2.crt",
		"-peer-key-file=../../fixtures/ca/server2.key.insecure",
	}

	for i := 0; i < size; i++ {
		if i == 0 {
			argGroup[i] = []string{"etcd", "-data-dir=/tmp/node1", "-name=node1"}
			if ssl {
				argGroup[i] = append(argGroup[i], sslServer1...)
			}
		} else {
			strI := strconv.Itoa(i + 1)
			argGroup[i] = []string{"etcd", "-name=node" + strI, "-addr=127.0.0.1:400" + strI, "-peer-addr=127.0.0.1:700" + strI, "-data-dir=/tmp/node" + strI, "-peers=127.0.0.1:7001"}
			if ssl {
				argGroup[i] = append(argGroup[i], sslServer2...)
			}
		}
	}

	etcds := make([]*os.Process, size)

	for i := range etcds {
		var err error
		etcds[i], err = os.StartProcess(EtcdBinPath, append(argGroup[i], "-f"), procAttr)
		if err != nil {
			return nil, nil, err
		}

		// TODOBP: Change this sleep to wait until the master is up.
		// The problem is that if the master isn't up then the children
		// have to retry. This retry can take upwards of 15 seconds
		// which slows tests way down and some of them fail.
		if i == 0 {
			time.Sleep(time.Second * 2)
		}
	}

	return argGroup, etcds, nil
}

// Destroy all the nodes in the cluster
func DestroyCluster(etcds []*os.Process) error {
	for _, etcd := range etcds {
		err := etcd.Kill()
		if err != nil {
			panic(err.Error())
		}
		etcd.Release()
	}
	return nil
}

//
func Monitor(size int, allowDeadNum int, leaderChan chan string, all chan bool, stop chan bool) {
	leaderMap := make(map[int]string)
	baseAddrFormat := "http://0.0.0.0:400%d"

	for {
		knownLeader := "unknown"
		dead := 0
		var i int

		for i = 0; i < size; i++ {
			leader, err := getLeader(fmt.Sprintf(baseAddrFormat, i+1))

			if err == nil {
				leaderMap[i] = leader

				if knownLeader == "unknown" {
					knownLeader = leader
				} else {
					if leader != knownLeader {
						break
					}

				}

			} else {
				dead++
				if dead > allowDeadNum {
					break
				}
			}

		}

		if i == size {
			select {
			case <-stop:
				return
			case <-leaderChan:
				leaderChan <- knownLeader
			default:
				leaderChan <- knownLeader
			}

		}
		if dead == 0 {
			select {
			case <-all:
				all <- true
			default:
				all <- true
			}
		}

		time.Sleep(time.Millisecond * 10)
	}

}

func getLeader(addr string) (string, error) {

	resp, err := client.Get(addr + "/v1/leader")

	if err != nil {
		return "", err
	}

	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return "", fmt.Errorf("no leader")
	}

	b, err := ioutil.ReadAll(resp.Body)

	resp.Body.Close()

	if err != nil {
		return "", err
	}

	return string(b), nil

}

// Dial with timeout
func dialTimeoutFast(network, addr string) (net.Conn, error) {
	return net.DialTimeout(network, addr, time.Millisecond*10)
}
<commit_msg>feat(tests/util): wait until server is up when creating cluster<commit_after>/*
Copyright 2013 CoreOS Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package test

import (
	"errors"
	"fmt"
	"github.com/coreos/etcd/third_party/github.com/coreos/go-etcd/etcd"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strconv"
	"time"
)

var client = http.Client{
	Transport: &http.Transport{
		Dial: dialTimeoutFast,
	},
}

// Sending set commands
func Set(stop chan bool) {

	stopSet := false
	i := 0
	c := etcd.NewClient(nil)
	for {
		key := fmt.Sprintf("%s_%v", "foo", i)

		result, err := c.Set(key, "bar", 0)

		if err != nil || result.Node.Key != "/"+key || result.Node.Value != "bar" {
			select {
			case <-stop:
				stopSet = true

			default:
			}
		}

		select {
		case <-stop:
			stopSet = true

		default:
		}

		if stopSet {
			break
		}

		i++
	}
	stop <- true
}

func WaitForServer(host string, client http.Client, scheme string) error {
	path := fmt.Sprintf("%s://%s/v2/keys/", scheme, host)

	var resp *http.Response
	var err error
	for i := 0; i < 10; i++ {
		time.Sleep(1 * time.Second)

		resp, err = client.Get(path)
		if err == nil && resp.StatusCode == 200 {
			return nil
		}
	}

	return errors.New(fmt.Sprintf("etcd server was not reachable in a long time, last-time response and error: %v; %v", resp, err))
}

// Create a cluster of etcd nodes
func CreateCluster(size int, procAttr *os.ProcAttr, ssl bool) ([][]string, []*os.Process, error) {
	argGroup := make([][]string, size)

	sslServer1 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt",
		"-peer-cert-file=../../fixtures/ca/server.crt",
		"-peer-key-file=../../fixtures/ca/server.key.insecure",
	}

	sslServer2 := []string{"-peer-ca-file=../../fixtures/ca/ca.crt",
		"-peer-cert-file=../../fixtures/ca/server2.crt",
		"-peer-key-file=../../fixtures/ca/server2.key.insecure",
	}

	for i := 0; i < size; i++ {
		if i == 0 {
			argGroup[i] = []string{"etcd", "-data-dir=/tmp/node1", "-name=node1"}
			if ssl {
				argGroup[i] = append(argGroup[i], sslServer1...)
			}
		} else {
			strI := strconv.Itoa(i + 1)
			argGroup[i] = []string{"etcd", "-name=node" + strI, "-addr=127.0.0.1:400" + strI, "-peer-addr=127.0.0.1:700" + strI, "-data-dir=/tmp/node" + strI, "-peers=127.0.0.1:7001"}
			if ssl {
				argGroup[i] = append(argGroup[i], sslServer2...)
			}
		}
	}

	etcds := make([]*os.Process, size)

	for i := range etcds {
		var err error
		etcds[i], err = os.StartProcess(EtcdBinPath, append(argGroup[i], "-f"), procAttr)
		if err != nil {
			return nil, nil, err
		}

		// The problem is that if the master isn't up then the children
		// have to retry. This retry can take upwards of 15 seconds
		// which slows tests way down and some of them fail.
		if i == 0 {
			client := buildClient()
			err = WaitServerUp("127.0.0.1:4001", client, "http")
			if err != nil {
				return nil, nil, err
			}
		}
	}

	return argGroup, etcds, nil
}

// Destroy all the nodes in the cluster
func DestroyCluster(etcds []*os.Process) error {
	for _, etcd := range etcds {
		err := etcd.Kill()
		if err != nil {
			panic(err.Error())
		}
		etcd.Release()
	}
	return nil
}

//
func Monitor(size int, allowDeadNum int, leaderChan chan string, all chan bool, stop chan bool) {
	leaderMap := make(map[int]string)
	baseAddrFormat := "http://0.0.0.0:400%d"

	for {
		knownLeader := "unknown"
		dead := 0
		var i int

		for i = 0; i < size; i++ {
			leader, err := getLeader(fmt.Sprintf(baseAddrFormat, i+1))

			if err == nil {
				leaderMap[i] = leader

				if knownLeader == "unknown" {
					knownLeader = leader
				} else {
					if leader != knownLeader {
						break
					}

				}

			} else {
				dead++
				if dead > allowDeadNum {
					break
				}
			}

		}

		if i == size {
			select {
			case <-stop:
				return
			case <-leaderChan:
				leaderChan <- knownLeader
			default:
				leaderChan <- knownLeader
			}

		}
		if dead == 0 {
			select {
			case <-all:
				all <- true
			default:
				all <- true
			}
		}

		time.Sleep(time.Millisecond * 10)
	}

}

func getLeader(addr string) (string, error) {

	resp, err := client.Get(addr + "/v1/leader")

	if err != nil {
		return "", err
	}

	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return "", fmt.Errorf("no leader")
	}

	b, err := ioutil.ReadAll(resp.Body)

	resp.Body.Close()

	if err != nil {
		return "", err
	}

	return string(b), nil

}

// Dial with timeout
func dialTimeoutFast(network, addr string) (net.Conn, error) {
	return net.DialTimeout(network, addr, time.Millisecond*10)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path"
	"path/filepath"

	"github.com/markelog/eclectica/cmd/print"
	"github.com/markelog/eclectica/console"
	"github.com/markelog/eclectica/plugins"
	"github.com/markelog/eclectica/variables"
)

func setCmd(cmd *exec.Cmd, name, version string) {
	plugin := plugins.New(name)

	environment, err := plugin.Pkg.Environment(version)
	print.Error(err)

	env := os.Environ()
	env = append(env, environment)

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	cmd.Env = env
}

func getVersion(path string) string {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return "current"
	}

	file, err := os.Open(path)
	print.Error(err)

	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		return scanner.Text()
	}

	if err := scanner.Err(); err != nil {
		print.Error(err)
	}

	return "current"
}

func main() {
	_, name := path.Split(os.Args[0])

	language := plugins.SearchBin(name)
	base := variables.Home()

	pwd, err := os.Getwd()
	print.Error(err)

	versionPath := filepath.Join(pwd, fmt.Sprintf(".%s-version", language))
	version := getVersion(versionPath)

	pathPart := filepath.Join(base, language, version)
	path := filepath.Join(pathPart, "bin", name)

	if _, err := os.Stat(path); os.IsNotExist(err) {
		err = errors.New("Version " + version + " has not been established")
		print.Error(err)
	}

	args := []string{path}
	args = append(args, os.Args[1:]...)

	cmd := console.Get(args)

	setCmd(cmd, language, version)

	cmd.Run()
}
<commit_msg>Implement up walker searcher for `.<language>-version`<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path"
	"path/filepath"

	"github.com/markelog/eclectica/cmd/print"
	"github.com/markelog/eclectica/console"
	"github.com/markelog/eclectica/plugins"
	"github.com/markelog/eclectica/variables"
)

type Walker func(path string) bool

func walkUp(path string, fn Walker) {
	current := path

	for {
		if current == "" || current == "/" {
			return
		}

		current = filepath.Dir(current)

		stop := fn(current)
		if stop == true {
			return
		}
	}

	return
}

func setCmd(cmd *exec.Cmd, name, version string) {
	plugin := plugins.New(name)

	environment, err := plugin.Pkg.Environment(version)
	print.Error(err)

	env := os.Environ()
	env = append(env, environment)

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	cmd.Env = env
}

func getVersion(path string) string {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return "current"
	}

	file, err := os.Open(path)
	print.Error(err)

	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		return scanner.Text()
	}

	if err := scanner.Err(); err != nil {
		print.Error(err)
	}

	return "current"
}

func main() {
	var (
		versionPath string
	)

	_, name := path.Split(os.Args[0])

	language := plugins.SearchBin(name)
	base := variables.Home()
	file := fmt.Sprintf(".%s-version", language)

	pwd, err := os.Getwd()
	print.Error(err)

	walkUp(pwd, func(path string) bool {
		p := filepath.Join(path, file)

		if _, err := os.Stat(p); err == nil {
			versionPath = p
			return true
		}

		return false
	})

	version := getVersion(versionPath)

	pathPart := filepath.Join(base, language, version)
	path := filepath.Join(pathPart, "bin", name)

	if _, err := os.Stat(path); os.IsNotExist(err) {
		err = errors.New("Version " + version + " has not been established")
		print.Error(err)
	}

	args := []string{path}
	args = append(args, os.Args[1:]...)

	cmd := console.Get(args)

	setCmd(cmd, language, version)

	cmd.Run()
}
<|endoftext|>