<commit_before>package pddl

import (
	"fmt"
	"io"
)

// PrintDomain prints the domain in valid PDDL to the given writer.
func PrintDomain(w io.Writer, d *Domain) {
	fmt.Fprintf(w, "(define (domain %s)\n", d.Identifier)
	printReqsDef(w, d.Requirements)
	printTypesDef(w, d.Types)
	printConstsDef(w, ":constants", d.Constants)
	printPredsDef(w, d.Predicates)
	printFuncsDef(w, d.Functions)
	for _, act := range d.Actions {
		printAction(w, act)
	}
	fmt.Fprintln(w, ")")
}

func printReqsDef(w io.Writer, reqs []Identifier) {
	if len(reqs) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(:requirements\n", indent(1))
	for i, r := range reqs {
		s := r.Str
		if i == len(reqs)-1 {
			s += ")"
		}
		fmt.Fprintln(w, indent(2), s)
	}
}

func printTypesDef(w io.Writer, ts []Type) {
	if len(ts) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(:types", indent(1))
	var ids []TypedIdentifier
	for _, t := range ts {
		if t.Location.Line == 0 {
			// skip undeclared implicit types like object
			continue
		}
		ids = append(ids, t.TypedIdentifier)
	}
	printTypedNames(w, "\n"+indent(2), ids)
	fmt.Fprintln(w, ")")
}

func printConstsDef(w io.Writer, def string, cs []TypedIdentifier) {
	if len(cs) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(%s", indent(1), def)
	printTypedNames(w, "\n"+indent(2), cs)
	fmt.Fprintln(w, ")")
}

func printPredsDef(w io.Writer, ps []Predicate) {
	if len(ps) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(:predicates\n", indent(1))
	for i, p := range ps {
		if p.Location.Line == 0 {
			// skip undefined implicit predicates like =
			continue
		}
		fmt.Fprintf(w, "%s(%s", indent(2), p.Str)
		printTypedNames(w, " ", p.Parameters)
		fmt.Fprint(w, ")")
		if i < len(ps)-1 {
			fmt.Fprint(w, "\n")
		}
	}
	fmt.Fprintln(w, ")")
}

func printFuncsDef(w io.Writer, fs []Function) {
	if len(fs) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(:functions\n", indent(1))
	for i, f := range fs {
		fmt.Fprintf(w, "%s(%s", indent(2), f.Str)
		printTypedNames(w, " ", f.Parameters)
		fmt.Fprint(w, ")")
		if len(f.Types) > 0 {
			fmt.Fprint(w, " - ", typeString(f.Types))
		}
		if i < len(fs)-1 {
			fmt.Fprint(w, "\n")
		}
	}
	fmt.Fprintln(w, ")")
}

func printAction(w io.Writer, act Action) {
	fmt.Fprintf(w, "%s(:action %s\n", indent(1), act.Identifier)
	fmt.Fprintf(w, "%s:parameters (", indent(3))
	printTypedNames(w, "", act.Parameters)
	fmt.Fprint(w, ")")
	if act.Precondition != nil {
		fmt.Fprint(w, "\n")
		fmt.Fprintf(w, "%s:precondition\n", indent(3))
		act.Precondition.print(w, indent(4))
	}
	if act.Effect != nil {
		fmt.Fprint(w, "\n")
		fmt.Fprintf(w, "%s:effect\n", indent(3))
		act.Effect.print(w, indent(4))
	}
	fmt.Fprintln(w, ")")
}

// PrintProblem prints the problem in valid PDDL to the given writer.
func PrintProblem(w io.Writer, p *Problem) {
	fmt.Fprintf(w, "(define (problem %s)\n%s(:domain %s)\n",
		p.Identifier, indent(1), p.Domain)
	printReqsDef(w, p.Requirements)
	printConstsDef(w, ":objects", p.Objects)

	fmt.Fprintf(w, "%s(:init", indent(1))
	for _, f := range p.Init {
		fmt.Fprint(w, "\n")
		f.print(w, indent(2))
	}
	fmt.Fprint(w, ")\n")

	fmt.Fprintf(w, "%s(:goal\n", indent(1))
	p.Goal.print(w, indent(2))

	fmt.Fprintln(w, ")\n)")
}

// declGroup is a group of declarators along
// with their type.
type declGroup struct {
	typ  string
	ents []string
}

// declGroups implements sort.Interface, sorting
// the list of typed declarations by their type name.
type declGroups []declGroup

func (t declGroups) Len() int {
	return len(t)
}

func (t declGroups) Less(i, j int) bool {
	return t[i].typ < t[j].typ
}

func (t declGroups) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}

// printTypedNames prints a slice of TypedNames
// in their given order. The prefix is printed before
// each group.
func printTypedNames(w io.Writer, prefix string, ns []TypedIdentifier) {
	if len(ns) == 0 {
		return
	}
	tprev := typeString(ns[0].Types)
	sep := prefix
	for _, n := range ns {
		tcur := typeString(n.Types)
		if tcur != tprev {
			if tprev == "" {
				// Should be impossible.
				panic(n.Location.String() + ": untyped declarations in the middle of a typed list")
			}
			fmt.Fprintf(w, " - %s", tprev)
			tprev = tcur
			sep = prefix
			if sep == "" {
				sep = " "
			}
		}
		fmt.Fprintf(w, "%s%s", sep, n.Str)
		sep = " "
	}
	if tprev != "" {
		fmt.Fprintf(w, " - %s", tprev)
	}
}

// typeString returns the string representation of a type.
func typeString(t []TypeName) (str string) {
	switch len(t) {
	case 0:
		break
	case 1:
		if t[0].Location.Line == 0 {
			break
		}
		str = t[0].Str
	default:
		str = "(either"
		for _, n := range t {
			str += " " + n.Str
		}
		str += ")"
	}
	return
}

func (p *PropositionNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(", prefix)
	fmt.Fprint(w, p.Predicate)
	for _, t := range p.Arguments {
		fmt.Fprintf(w, " %s", t.Identifier)
	}
	fmt.Fprint(w, ")")
}

func (n *AndNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(and", prefix)
	for _, f := range n.Formula {
		fmt.Fprint(w, "\n")
		f.print(w, prefix+indent(1))
	}
	fmt.Fprint(w, ")")
}

func (n *OrNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(or", prefix)
	for _, f := range n.Formula {
		fmt.Fprint(w, "\n")
		f.print(w, prefix+indent(1))
	}
	fmt.Fprint(w, ")")
}

func (n *NotNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(not\n", prefix)
	n.Formula.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *ImplyNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(imply\n", prefix)
	n.Left.print(w, prefix+indent(1))
	fmt.Fprint(w, "\n")
	n.Right.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *ForallNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(forall (", prefix)
	printTypedNames(w, "", n.Variables)
	fmt.Fprint(w, ")\n")
	n.Formula.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *ExistsNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(exists (", prefix)
	printTypedNames(w, "", n.Variables)
	fmt.Fprint(w, ")\n")
	n.Formula.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *WhenNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(when\n", prefix)
	n.Condition.print(w, prefix+indent(1))
	fmt.Fprint(w, "\n")
	n.Formula.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *AssignNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(%s %s %s)", prefix, n.Op, n.Lval.Str, n.Rval)
}

func indent(n int) (s string) {
	for i := 0; i < n; i++ {
		s += "\t"
	}
	return
}<commit_msg>Improve a comment.<commit_after>package pddl

import (
	"fmt"
	"io"
)

// PrintDomain prints the domain in valid PDDL to the given writer.
func PrintDomain(w io.Writer, d *Domain) {
	fmt.Fprintf(w, "(define (domain %s)\n", d.Identifier)
	printReqsDef(w, d.Requirements)
	printTypesDef(w, d.Types)
	printConstsDef(w, ":constants", d.Constants)
	printPredsDef(w, d.Predicates)
	printFuncsDef(w, d.Functions)
	for _, act := range d.Actions {
		printAction(w, act)
	}
	fmt.Fprintln(w, ")")
}

func printReqsDef(w io.Writer, reqs []Identifier) {
	if len(reqs) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(:requirements\n", indent(1))
	for i, r := range reqs {
		s := r.Str
		if i == len(reqs)-1 {
			s += ")"
		}
		fmt.Fprintln(w, indent(2), s)
	}
}

func printTypesDef(w io.Writer, ts []Type) {
	if len(ts) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(:types", indent(1))
	var ids []TypedIdentifier
	for _, t := range ts {
		if t.Location.Line == 0 {
			// skip undeclared implicit types like object
			continue
		}
		ids = append(ids, t.TypedIdentifier)
	}
	printTypedNames(w, "\n"+indent(2), ids)
	fmt.Fprintln(w, ")")
}

func printConstsDef(w io.Writer, def string, cs []TypedIdentifier) {
	if len(cs) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(%s", indent(1), def)
	printTypedNames(w, "\n"+indent(2), cs)
	fmt.Fprintln(w, ")")
}

func printPredsDef(w io.Writer, ps []Predicate) {
	if len(ps) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(:predicates\n", indent(1))
	for i, p := range ps {
		if p.Location.Line == 0 {
			// skip undefined implicit predicates like =
			continue
		}
		fmt.Fprintf(w, "%s(%s", indent(2), p.Str)
		printTypedNames(w, " ", p.Parameters)
		fmt.Fprint(w, ")")
		if i < len(ps)-1 {
			fmt.Fprint(w, "\n")
		}
	}
	fmt.Fprintln(w, ")")
}

func printFuncsDef(w io.Writer, fs []Function) {
	if len(fs) == 0 {
		return
	}
	fmt.Fprintf(w, "%s(:functions\n", indent(1))
	for i, f := range fs {
		fmt.Fprintf(w, "%s(%s", indent(2), f.Str)
		printTypedNames(w, " ", f.Parameters)
		fmt.Fprint(w, ")")
		if len(f.Types) > 0 {
			fmt.Fprint(w, " - ", typeString(f.Types))
		}
		if i < len(fs)-1 {
			fmt.Fprint(w, "\n")
		}
	}
	fmt.Fprintln(w, ")")
}

func printAction(w io.Writer, act Action) {
	fmt.Fprintf(w, "%s(:action %s\n", indent(1), act.Identifier)
	fmt.Fprintf(w, "%s:parameters (", indent(3))
	printTypedNames(w, "", act.Parameters)
	fmt.Fprint(w, ")")
	if act.Precondition != nil {
		fmt.Fprint(w, "\n")
		fmt.Fprintf(w, "%s:precondition\n", indent(3))
		act.Precondition.print(w, indent(4))
	}
	if act.Effect != nil {
		fmt.Fprint(w, "\n")
		fmt.Fprintf(w, "%s:effect\n", indent(3))
		act.Effect.print(w, indent(4))
	}
	fmt.Fprintln(w, ")")
}

// PrintProblem prints the problem in valid PDDL to the given writer.
func PrintProblem(w io.Writer, p *Problem) {
	fmt.Fprintf(w, "(define (problem %s)\n%s(:domain %s)\n",
		p.Identifier, indent(1), p.Domain)
	printReqsDef(w, p.Requirements)
	printConstsDef(w, ":objects", p.Objects)

	fmt.Fprintf(w, "%s(:init", indent(1))
	for _, f := range p.Init {
		fmt.Fprint(w, "\n")
		f.print(w, indent(2))
	}
	fmt.Fprint(w, ")\n")

	fmt.Fprintf(w, "%s(:goal\n", indent(1))
	p.Goal.print(w, indent(2))

	fmt.Fprintln(w, ")\n)")
}

// declGroup is a group of declarators along
// with their type.
type declGroup struct {
	typ  string
	ents []string
}

// declGroups implements sort.Interface, sorting
// the list of typed declarations by their type name.
type declGroups []declGroup

func (t declGroups) Len() int {
	return len(t)
}

func (t declGroups) Less(i, j int) bool {
	return t[i].typ < t[j].typ
}

func (t declGroups) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}

// printTypedNames prints a slice of TypedNames.
// Adjacent items with the same type are all printed
// in a group.  Each group is preceeded by the prefix.
func printTypedNames(w io.Writer, prefix string, ns []TypedIdentifier) {
	if len(ns) == 0 {
		return
	}
	tprev := typeString(ns[0].Types)
	sep := prefix
	for _, n := range ns {
		tcur := typeString(n.Types)
		if tcur != tprev {
			if tprev == "" {
				// Should be impossible.
				panic(n.Location.String() + ": untyped declarations in the middle of a typed list")
			}
			fmt.Fprintf(w, " - %s", tprev)
			tprev = tcur
			sep = prefix
			if sep == "" {
				sep = " "
			}
		}
		fmt.Fprintf(w, "%s%s", sep, n.Str)
		sep = " "
	}
	if tprev != "" {
		fmt.Fprintf(w, " - %s", tprev)
	}
}

// typeString returns the string representation of a type.
func typeString(t []TypeName) (str string) {
	switch len(t) {
	case 0:
		break
	case 1:
		if t[0].Location.Line == 0 {
			break
		}
		str = t[0].Str
	default:
		str = "(either"
		for _, n := range t {
			str += " " + n.Str
		}
		str += ")"
	}
	return
}

func (p *PropositionNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(", prefix)
	fmt.Fprint(w, p.Predicate)
	for _, t := range p.Arguments {
		fmt.Fprintf(w, " %s", t.Identifier)
	}
	fmt.Fprint(w, ")")
}

func (n *AndNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(and", prefix)
	for _, f := range n.Formula {
		fmt.Fprint(w, "\n")
		f.print(w, prefix+indent(1))
	}
	fmt.Fprint(w, ")")
}

func (n *OrNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(or", prefix)
	for _, f := range n.Formula {
		fmt.Fprint(w, "\n")
		f.print(w, prefix+indent(1))
	}
	fmt.Fprint(w, ")")
}

func (n *NotNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(not\n", prefix)
	n.Formula.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *ImplyNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(imply\n", prefix)
	n.Left.print(w, prefix+indent(1))
	fmt.Fprint(w, "\n")
	n.Right.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *ForallNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(forall (", prefix)
	printTypedNames(w, "", n.Variables)
	fmt.Fprint(w, ")\n")
	n.Formula.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *ExistsNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(exists (", prefix)
	printTypedNames(w, "", n.Variables)
	fmt.Fprint(w, ")\n")
	n.Formula.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *WhenNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(when\n", prefix)
	n.Condition.print(w, prefix+indent(1))
	fmt.Fprint(w, "\n")
	n.Formula.print(w, prefix+indent(1))
	fmt.Fprint(w, ")")
}

func (n *AssignNode) print(w io.Writer, prefix string) {
	fmt.Fprintf(w, "%s(%s %s %s)", prefix, n.Op, n.Lval.Str, n.Rval)
}

func indent(n int) (s string) {
	for i := 0; i < n; i++ {
		s += "\t"
	}
	return
}<|endoftext|>


<commit_before>package net

type CmdFunc func([][]byte) (interface{}, error)
type REPLY_TYPE uint8

const (
	STATUS_REPLY REPLY_TYPE = 1 + iota
	ERR_REPLY
	INT_REPLY
	BULK_REPLY
	LIST_REPLY
)

var (
	cmdFuncs  = map[string]CmdFunc{}
	replyType = map[string]REPLY_TYPE{}
)

// Register cmd function to server.
func REG(cmd string, types REPLY_TYPE, f CmdFunc) {
	cmdFuncs[cmd] = f
	replyType[cmd] = types
}

// Execute cmd function and generate reply.
func RUN(cmd string, args [][]byte) (reply Reply) {
	f, ok := cmdFuncs[cmd]
	if !ok {
		reply = &ErrReply{
			Msg: "unknwon command '" + cmd + "'",
		}
		return
	}

	t, ok2 := replyType[cmd]
	if !ok2 {
		reply = &ErrReply{
			Msg: "unknwon reply type of command '" + cmd + "'",
		}
		return
	}

	data, ferr := f(args)
	if ferr != nil {
		reply = &ErrReply{
			Msg: ferr.Error(),
		}
	}

	switch t {
	case STATUS_REPLY:
		if data == nil {
			reply = &StatusReply{
				Code: "OK",
			}
		} else {
			reply = &StatusReply{
				Code: (data.(string)),
			}
		}
		break
	case INT_REPLY:
		reply = &IntReply{
			Nos: data.(int64),
		}
		break

	case BULK_REPLY:
		reply = &BulkReply{
			Bulk: data.(string),
		}
		break

	case LIST_REPLY:
		break
	}

	return
}
<commit_msg>Fix bug:can not reply error info to client.<commit_after>package net

type CmdFunc func([][]byte) (interface{}, error)
type REPLY_TYPE uint8

const (
	STATUS_REPLY REPLY_TYPE = 1 + iota
	ERR_REPLY
	INT_REPLY
	BULK_REPLY
	LIST_REPLY
)

var (
	cmdFuncs  = map[string]CmdFunc{}
	replyType = map[string]REPLY_TYPE{}
)

// Register cmd function to server.
func REG(cmd string, types REPLY_TYPE, f CmdFunc) {
	cmdFuncs[cmd] = f
	replyType[cmd] = types
}

// Execute cmd function and generate reply.
func RUN(cmd string, args [][]byte) (reply Reply) {
	f, ok := cmdFuncs[cmd]
	if !ok {
		reply = &ErrReply{
			Msg: "unknwon command '" + cmd + "'",
		}
		return
	}

	t, ok2 := replyType[cmd]
	if !ok2 {
		reply = &ErrReply{
			Msg: "unknwon reply type of command '" + cmd + "'",
		}
		return
	}

	data, ferr := f(args)
	if ferr != nil {
		return &ErrReply{
			Msg: ferr.Error(),
		}
	}

	switch t {
	case STATUS_REPLY:
		if data == nil {
			reply = &StatusReply{
				Code: "OK",
			}
		} else {
			reply = &StatusReply{
				Code: (data.(string)),
			}
		}
		break
	case INT_REPLY:
		reply = &IntReply{
			Nos: data.(int64),
		}
		break

	case BULK_REPLY:
		reply = &BulkReply{
			Bulk: data.(string),
		}
		break

	case LIST_REPLY:
		break
	}

	return
}
<|endoftext|>


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

// Package os provides a platform-independent interface to operating system
// functionality. The design is Unix-like, although the error handling is
// Go-like; failing calls return values of type error rather than error numbers.
// Often, more information is available within the error. For example,
// if a call that takes a file name fails, such as Open or Stat, the error
// will include the failing file name when printed and will be of type
// *PathError, which may be unpacked for more information.
//
// The os interface is intended to be uniform across all operating systems.
// Features not generally available appear in the system-specific package syscall.
//
// Here is a simple example, opening a file and reading some of it.
//
//	file, err := os.Open("file.go") // For read access.
//	if err != nil {
//		log.Fatal(err)
//	}
//
// If the open fails, the error string will be self-explanatory, like
//
//	open file.go: no such file or directory
//
// The file's data can then be read into a slice of bytes. Read and
// Write take their byte counts from the length of the argument slice.
//
//	data := make([]byte, 100)
//	count, err := file.Read(data)
//	if err != nil {
//		log.Fatal(err)
//	}
//	fmt.Printf("read %d bytes: %q\n", count, data[:count])
//
package os

import (
	"errors"
	"internal/poll"
	"io"
	"syscall"
)

// Name returns the name of the file as presented to Open.
func (f *File) Name() string { return f.name }

// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
//
// Note that the Go runtime writes to standard error for panics and crashes;
// closing Stderr may cause those messages to go elsewhere, perhaps
// to a file opened later.
var (
	Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
	Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
	Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)

// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
	O_RDONLY int = syscall.O_RDONLY // open the file read-only.
	O_WRONLY int = syscall.O_WRONLY // open the file write-only.
	O_RDWR   int = syscall.O_RDWR   // open the file read-write.
	O_APPEND int = syscall.O_APPEND // append data to the file when writing.
	O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
	O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist
	O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
	O_TRUNC  int = syscall.O_TRUNC  // if possible, truncate file when opened.
)

// Seek whence values.
//
// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
const (
	SEEK_SET int = 0 // seek relative to the origin of the file
	SEEK_CUR int = 1 // seek relative to the current offset
	SEEK_END int = 2 // seek relative to the end
)

// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op  string
	Old string
	New string
	Err error
}

func (e *LinkError) Error() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
}

// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	n, e := f.read(b)
	return n, f.wrapErr("read", e)
}

// ReadAt reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// ReadAt always returns a non-nil error when n < len(b).
// At end of file, that error is io.EOF.
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}

	if off < 0 {
		return 0, &PathError{"readat", f.name, errors.New("negative offset")}
	}

	for len(b) > 0 {
		m, e := f.pread(b, off)
		if e != nil {
			err = f.wrapErr("read", e)
			break
		}
		n += m
		b = b[m:]
		off += int64(m)
	}
	return
}

// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
	if err := f.checkValid("write"); err != nil {
		return 0, err
	}
	n, e := f.write(b)
	if n < 0 {
		n = 0
	}
	if n != len(b) {
		err = io.ErrShortWrite
	}

	epipecheck(f, e)

	if e != nil {
		err = f.wrapErr("write", e)
	}

	return n, err
}

// WriteAt writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
// WriteAt returns a non-nil error when n != len(b).
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
	if err := f.checkValid("write"); err != nil {
		return 0, err
	}

	if off < 0 {
		return 0, &PathError{"writeat", f.name, errors.New("negative offset")}
	}

	for len(b) > 0 {
		m, e := f.pwrite(b, off)
		if e != nil {
			err = f.wrapErr("write", e)
			break
		}
		n += m
		b = b[m:]
		off += int64(m)
	}
	return
}

// Seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
// The behavior of Seek on a file opened with O_APPEND is not specified.
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
	if err := f.checkValid("seek"); err != nil {
		return 0, err
	}
	r, e := f.seek(offset, whence)
	if e == nil && f.dirinfo != nil && r != 0 {
		e = syscall.EISDIR
	}
	if e != nil {
		return 0, f.wrapErr("seek", e)
	}
	return r, nil
}

// WriteString is like Write, but writes the contents of string s rather than
// a slice of bytes.
func (f *File) WriteString(s string) (n int, err error) {
	return f.Write([]byte(s))
}

// Mkdir creates a new directory with the specified name and permission bits.
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm FileMode) error {
	e := syscall.Mkdir(fixLongPath(name), syscallMode(perm))

	if e != nil {
		return &PathError{"mkdir", name, e}
	}

	// mkdir(2) itself won't handle the sticky bit on *BSD and Solaris
	if !supportsCreateWithStickyBit && perm&ModeSticky != 0 {
		Chmod(name, perm)
	}

	return nil
}

// Chdir changes the current working directory to the named directory.
// If there is an error, it will be of type *PathError.
func Chdir(dir string) error {
	if e := syscall.Chdir(dir); e != nil {
		return &PathError{"chdir", dir, e}
	}
	return nil
}

// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0)
}

// Create creates the named file with mode 0666 (before umask), truncating
// it if it already exists. If successful, methods on the returned
// File can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
	return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

// lstat is overridden in tests.
var lstat = Lstat

// Rename renames (moves) oldpath to newpath.
// If newpath already exists and is not a directory, Rename replaces it.
// OS-specific restrictions may apply when oldpath and newpath are in different directories.
// If there is an error, it will be of type *LinkError.
func Rename(oldpath, newpath string) error {
	return rename(oldpath, newpath)
}

// Many functions in package syscall return a count of -1 instead of 0.
// Using fixCount(call()) instead of call() corrects the count.
func fixCount(n int, err error) (int, error) {
	if n < 0 {
		n = 0
	}
	return n, err
}

// wrapErr wraps an error that occurred during an operation on an open file.
// It passes io.EOF through unchanged, otherwise converts
// poll.ErrFileClosing to ErrClosed and wraps the error in a PathError.
func (f *File) wrapErr(op string, err error) error {
	if err == nil || err == io.EOF {
		return err
	}
	if err == poll.ErrFileClosing {
		err = ErrClosed
	}
	return &PathError{op, f.name, err}
}

// TempDir returns the default directory to use for temporary files.
//
// On Unix systems, it returns $TMPDIR if non-empty, else /tmp.
// On Windows, it uses GetTempPath, returning the first non-empty
// value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory.
// On Plan 9, it returns /tmp.
//
// The directory is neither guaranteed to exist nor have accessible
// permissions.
func TempDir() string {
	return tempDir()
}

// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
// If there is an error, it will be of type *PathError.
//
// A different subset of the mode bits are used, depending on the
// operating system.
//
// On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and
// ModeSticky are used.
//
// On Windows, the mode must be non-zero but otherwise only the 0200
// bit (owner writable) of mode is used; it controls whether the
// file's read-only attribute is set or cleared. attribute. The other
// bits are currently unused. Use mode 0400 for a read-only file and
// 0600 for a readable+writable file.
//
// On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive,
// and ModeTemporary are used.
func Chmod(name string, mode FileMode) error { return chmod(name, mode) }

// Chmod changes the mode of the file to mode.
// If there is an error, it will be of type *PathError.
func (f *File) Chmod(mode FileMode) error { return f.chmod(mode) }
<commit_msg>os: add period at end of O_EXCL comment<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package os provides a platform-independent interface to operating system
// functionality. The design is Unix-like, although the error handling is
// Go-like; failing calls return values of type error rather than error numbers.
// Often, more information is available within the error. For example,
// if a call that takes a file name fails, such as Open or Stat, the error
// will include the failing file name when printed and will be of type
// *PathError, which may be unpacked for more information.
//
// The os interface is intended to be uniform across all operating systems.
// Features not generally available appear in the system-specific package syscall.
//
// Here is a simple example, opening a file and reading some of it.
//
//	file, err := os.Open("file.go") // For read access.
//	if err != nil {
//		log.Fatal(err)
//	}
//
// If the open fails, the error string will be self-explanatory, like
//
//	open file.go: no such file or directory
//
// The file's data can then be read into a slice of bytes. Read and
// Write take their byte counts from the length of the argument slice.
//
//	data := make([]byte, 100)
//	count, err := file.Read(data)
//	if err != nil {
//		log.Fatal(err)
//	}
//	fmt.Printf("read %d bytes: %q\n", count, data[:count])
//
package os

import (
	"errors"
	"internal/poll"
	"io"
	"syscall"
)

// Name returns the name of the file as presented to Open.
func (f *File) Name() string { return f.name }

// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
//
// Note that the Go runtime writes to standard error for panics and crashes;
// closing Stderr may cause those messages to go elsewhere, perhaps
// to a file opened later.
var (
	Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
	Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
	Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)

// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
	O_RDONLY int = syscall.O_RDONLY // open the file read-only.
	O_WRONLY int = syscall.O_WRONLY // open the file write-only.
	O_RDWR   int = syscall.O_RDWR   // open the file read-write.
	O_APPEND int = syscall.O_APPEND // append data to the file when writing.
	O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
	O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
	O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
	O_TRUNC  int = syscall.O_TRUNC  // if possible, truncate file when opened.
)

// Seek whence values.
//
// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
const (
	SEEK_SET int = 0 // seek relative to the origin of the file
	SEEK_CUR int = 1 // seek relative to the current offset
	SEEK_END int = 2 // seek relative to the end
)

// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op  string
	Old string
	New string
	Err error
}

func (e *LinkError) Error() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
}

// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	n, e := f.read(b)
	return n, f.wrapErr("read", e)
}

// ReadAt reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// ReadAt always returns a non-nil error when n < len(b).
// At end of file, that error is io.EOF.
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}

	if off < 0 {
		return 0, &PathError{"readat", f.name, errors.New("negative offset")}
	}

	for len(b) > 0 {
		m, e := f.pread(b, off)
		if e != nil {
			err = f.wrapErr("read", e)
			break
		}
		n += m
		b = b[m:]
		off += int64(m)
	}
	return
}

// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
	if err := f.checkValid("write"); err != nil {
		return 0, err
	}
	n, e := f.write(b)
	if n < 0 {
		n = 0
	}
	if n != len(b) {
		err = io.ErrShortWrite
	}

	epipecheck(f, e)

	if e != nil {
		err = f.wrapErr("write", e)
	}

	return n, err
}

// WriteAt writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
// WriteAt returns a non-nil error when n != len(b).
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
	if err := f.checkValid("write"); err != nil {
		return 0, err
	}

	if off < 0 {
		return 0, &PathError{"writeat", f.name, errors.New("negative offset")}
	}

	for len(b) > 0 {
		m, e := f.pwrite(b, off)
		if e != nil {
			err = f.wrapErr("write", e)
			break
		}
		n += m
		b = b[m:]
		off += int64(m)
	}
	return
}

// Seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
// The behavior of Seek on a file opened with O_APPEND is not specified.
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
	if err := f.checkValid("seek"); err != nil {
		return 0, err
	}
	r, e := f.seek(offset, whence)
	if e == nil && f.dirinfo != nil && r != 0 {
		e = syscall.EISDIR
	}
	if e != nil {
		return 0, f.wrapErr("seek", e)
	}
	return r, nil
}

// WriteString is like Write, but writes the contents of string s rather than
// a slice of bytes.
func (f *File) WriteString(s string) (n int, err error) {
	return f.Write([]byte(s))
}

// Mkdir creates a new directory with the specified name and permission bits.
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm FileMode) error {
	e := syscall.Mkdir(fixLongPath(name), syscallMode(perm))

	if e != nil {
		return &PathError{"mkdir", name, e}
	}

	// mkdir(2) itself won't handle the sticky bit on *BSD and Solaris
	if !supportsCreateWithStickyBit && perm&ModeSticky != 0 {
		Chmod(name, perm)
	}

	return nil
}

// Chdir changes the current working directory to the named directory.
// If there is an error, it will be of type *PathError.
func Chdir(dir string) error {
	if e := syscall.Chdir(dir); e != nil {
		return &PathError{"chdir", dir, e}
	}
	return nil
}

// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0)
}

// Create creates the named file with mode 0666 (before umask), truncating
// it if it already exists. If successful, methods on the returned
// File can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
	return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

// lstat is overridden in tests.
var lstat = Lstat

// Rename renames (moves) oldpath to newpath.
// If newpath already exists and is not a directory, Rename replaces it.
// OS-specific restrictions may apply when oldpath and newpath are in different directories.
// If there is an error, it will be of type *LinkError.
func Rename(oldpath, newpath string) error {
	return rename(oldpath, newpath)
}

// Many functions in package syscall return a count of -1 instead of 0.
// Using fixCount(call()) instead of call() corrects the count.
func fixCount(n int, err error) (int, error) {
	if n < 0 {
		n = 0
	}
	return n, err
}

// wrapErr wraps an error that occurred during an operation on an open file.
// It passes io.EOF through unchanged, otherwise converts
// poll.ErrFileClosing to ErrClosed and wraps the error in a PathError.
func (f *File) wrapErr(op string, err error) error {
	if err == nil || err == io.EOF {
		return err
	}
	if err == poll.ErrFileClosing {
		err = ErrClosed
	}
	return &PathError{op, f.name, err}
}

// TempDir returns the default directory to use for temporary files.
//
// On Unix systems, it returns $TMPDIR if non-empty, else /tmp.
// On Windows, it uses GetTempPath, returning the first non-empty
// value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory.
// On Plan 9, it returns /tmp.
//
// The directory is neither guaranteed to exist nor have accessible
// permissions.
func TempDir() string {
	return tempDir()
}

// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
// If there is an error, it will be of type *PathError.
//
// A different subset of the mode bits are used, depending on the
// operating system.
//
// On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and
// ModeSticky are used.
//
// On Windows, the mode must be non-zero but otherwise only the 0200
// bit (owner writable) of mode is used; it controls whether the
// file's read-only attribute is set or cleared. attribute. The other
// bits are currently unused. Use mode 0400 for a read-only file and
// 0600 for a readable+writable file.
//
// On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive,
// and ModeTemporary are used.
func Chmod(name string, mode FileMode) error { return chmod(name, mode) }

// Chmod changes the mode of the file to mode.
// If there is an error, it will be of type *PathError.
func (f *File) Chmod(mode FileMode) error { return f.chmod(mode) }
<|endoftext|>


<commit_before>package server

import (
    "github.com/coreos/go-etcd/etcd"
    "log"
    "regexp"
    "testing"
)

func loadBackend (t *testing.T, value string) *ServiceBackend {
    node := etcd.Node{Key: "/clusterf/services/test/backends/test", Value: value, Dir: false}

    if backend, err := loadEtcdServiceBackend(&node); err != nil {
        t.Error("ServiceBackend.loadEtcd(%v): %s", value, err)
        return nil
    } else {
        return backend
    }
}

func TestBackendLoad (t *testing.T) {
    simple := loadBackend(t, "{\"ipv4\": \"127.0.0.1\"}")

    if simple == nil {

    } else if simple.IPv4 != "127.0.0.1" {
        t.Error("%v.IPv4 != 127.0.0.1", simple)
    }
}

var testSyncErrors = []struct {
    action  string
    key     string
    dir     bool
    value   string

    events  []Event
    error   string
}{
    {action:"set", key:"/clusterf", dir:false, value:"haha", error: "Ignore unknown node"},
    {action:"set", key:"/clusterf/services", dir:false, value:"haha", error: "Ignore unknown node"},
    {action:"set", key:"/clusterf/wtf", dir:false, value:"haha", error: "Ignore unknown node"},
    {action:"set", key:"/clusterf/wtf", dir:true, error: "Ignore unknown node"},
    {action:"create", key:"/clusterf/services/wtf/frontend", dir:true, error: "Ignore unknown service wtf node"},
    {action:"create", key:"/clusterf/services/wtf/backends/test", dir:true, error: "Ignore unknown service wtf backends node"},
    {action:"set", key:"/clusterf/services/wtf/backends/test/three", value: "3", error: "Ignore unknown service wtf backends node"},
    {action:"set", key:"/clusterf/services/wtf/asdf", value: "quux", error: "Ignore unknown service wtf node"},

    {action:"set",      key:"/clusterf/services/test/frontend", value:"not json", error: "service test frontend: invalid character 'o' in literal null"},

    {action:"create",   key:"/clusterf", dir:true},
    {action:"create",   key:"/clusterf/services", dir:true},
    {action:"create",   key:"/clusterf/services/test", dir:true},
    {action:"set",      key:"/clusterf/services/test/frontend",
        value: "{\"ipv4\": \"127.0.0.1\", \"tcp\": 8080}",
        events: []Event{{Type: NewService, Service: &Service{Name: "test"}}},
    },
    {action:"create",   key:"/clusterf/services/test/backends", dir:true},
    {action:"set",      key:"/clusterf/services/test/backends/test1",
        value: "{\"ipv4\": \"127.0.0.1\", \"tcp\": 8081}",
        events: []Event{{Type: NewBackend, Service: &Service{Name: "test"}, BackendName: "test1"}},
    },
    {action:"set",      key:"/clusterf/services/test/backends/test2",
        value: "{\"ipv4\": \"127.0.0.1\", \"tcp\": 8082}",
        events: []Event{{Type: NewBackend, Service: &Service{Name: "test"}, BackendName: "test2"}},
    },
    {action:"set",      key:"/clusterf/services/test6/frontend",
        value: "{\"ipv6\": \"2001:db8::1\", \"tcp\": 8080}",
        events: []Event{{Type: NewService, Service: &Service{Name: "test6"}}},
    },

    {action:"delete",   key:"/clusterf/services/test3/backends/test1"},
    {action:"delete",   key:"/clusterf/services/test3/backends", dir:true},
    {action:"delete",   key:"/clusterf/services/test3", dir:true},
    {action:"delete",   key:"/clusterf/services/test", dir:true,
        events: []Event{{Type: DelService, Service: &Service{Name: "test"}}},
    },
    {action:"delete",   key:"/clusterf/services", dir:true,
        events: []Event{{Type: DelService, Service: &Service{Name: "test6"}}},
    },
}

func TestSync(t *testing.T) {
    self := &Etcd{
        config:     EtcdConfig{Prefix: "/clusterf"},
        services:   newServices(),
    }

    for _, testCase := range testSyncErrors {
        node := &etcd.Node{
            Key:    testCase.key,
            Dir:    testCase.dir,
            Value:  testCase.value,
        }
        var events []*Event

        log.Printf("--- %+v\n", testCase)
        err := self.sync(testCase.action, node, func (event *Event) {
            if event != nil {
                events = append(events, event)
            }
        })

        if err != nil {
            if testCase.error == "" {
                t.Errorf("error %+v: error %s", testCase, err)
            } else if !regexp.MustCompile(testCase.error).MatchString(err.Error()) {
                t.Errorf("fail %+v: error: %s", testCase, err)
            }
        } else if testCase.error != "" {
            t.Errorf("fail %+v: error nil", testCase)
        }

        for i := 0; i < len(events) && i < len(testCase.events); i++ {
            if i >= len(events) {
                t.Errorf("fail %+v: missing event %+v", testCase, testCase.events[i])
            } else if i >= len(testCase.events) {
                t.Errorf("fail %+v: extra event %+v", testCase, events[i])
            } else {
                if events[i].Type != testCase.events[i].Type {
                    t.Errorf("fail %+v: event %+v type", testCase, events[i])
                }

                if events[i].Service == nil {
                    // XXX: srs?
                    if testCase.events[i].Service != nil {
                        t.Errorf("fail %+v: event %+v service", testCase, events[i])
                    }
                } else if events[i].Service.Name != testCase.events[i].Service.Name {
                    t.Errorf("fail %+v: event %+v service name", testCase, events[i])
                }
            }
        }

        // t.Logf("ok %+v", testCase)
    }
}
<commit_msg>server/etcd_test: fixup events cmp; cleanup<commit_after>package server

import (
    "github.com/coreos/go-etcd/etcd"
    "log"
    "regexp"
    "testing"
)

func loadBackend (t *testing.T, value string) *ServiceBackend {
    node := etcd.Node{Key: "/clusterf/services/test/backends/test", Value: value, Dir: false}

    if backend, err := loadEtcdServiceBackend(&node); err != nil {
        t.Error("ServiceBackend.loadEtcd(%v): %s", value, err)
        return nil
    } else {
        return backend
    }
}

func TestBackendLoad (t *testing.T) {
    simple := loadBackend(t, "{\"ipv4\": \"127.0.0.1\"}")

    if simple == nil {

    } else if simple.IPv4 != "127.0.0.1" {
        t.Error("%v.IPv4 != 127.0.0.1", simple)
    }
}

var testSyncErrors = []struct {
    action  string
    key     string
    dir     bool
    value   string

    events  []Event
    error   string
}{
    {action:"set", key:"/clusterf", dir:false, value:"haha", error: "Ignore unknown node"},
    {action:"set", key:"/clusterf/services", dir:false, value:"haha", error: "Ignore unknown node"},
    {action:"set", key:"/clusterf/wtf", dir:false, value:"haha", error: "Ignore unknown node"},
    {action:"set", key:"/clusterf/wtf", dir:true, error: "Ignore unknown node"},
    {action:"create", key:"/clusterf/services/wtf/frontend", dir:true, error: "Ignore unknown service wtf node"},
    {action:"create", key:"/clusterf/services/wtf/backends/test", dir:true, error: "Ignore unknown service wtf backends node"},
    {action:"set", key:"/clusterf/services/wtf/backends/test/three", value: "3", error: "Ignore unknown service wtf backends node"},
    {action:"set", key:"/clusterf/services/wtf/asdf", value: "quux", error: "Ignore unknown service wtf node"},

    {action:"set",      key:"/clusterf/services/test/frontend", value:"not json", error: "service test frontend: invalid character 'o' in literal null"},

    {action:"create",   key:"/clusterf", dir:true},
    {action:"create",   key:"/clusterf/services", dir:true},
    {action:"create",   key:"/clusterf/services/test", dir:true},
    {action:"set",      key:"/clusterf/services/test/frontend",
        value: "{\"ipv4\": \"127.0.0.1\", \"tcp\": 8080}",
        events: []Event{{Type: NewService, Service: &Service{Name: "test"}}},
    },
    {action:"create",   key:"/clusterf/services/test/backends", dir:true},
    {action:"set",      key:"/clusterf/services/test/backends/test1",
        value: "{\"ipv4\": \"127.0.0.1\", \"tcp\": 8081}",
        events: []Event{{Type: NewBackend, Service: &Service{Name: "test"}, BackendName: "test1"}},
    },
    {action:"set",      key:"/clusterf/services/test/backends/test2",
        value: "{\"ipv4\": \"127.0.0.1\", \"tcp\": 8082}",
        events: []Event{{Type: NewBackend, Service: &Service{Name: "test"}, BackendName: "test2"}},
    },
    {action:"set",      key:"/clusterf/services/test6/frontend",
        value: "{\"ipv6\": \"2001:db8::1\", \"tcp\": 8080}",
        events: []Event{{Type: NewService, Service: &Service{Name: "test6"}}},
    },

    {action:"delete",   key:"/clusterf/services/test3/backends/test1"},
    {action:"delete",   key:"/clusterf/services/test3/backends", dir:true},
    {action:"delete",   key:"/clusterf/services/test3", dir:true},
    {action:"delete",   key:"/clusterf/services/test", dir:true,
        events: []Event{{Type: DelService, Service: &Service{Name: "test"}}},
    },
    {action:"delete",   key:"/clusterf/services", dir:true,
        events: []Event{{Type: DelService, Service: &Service{Name: "test6"}}},
    },
}

func TestSync(t *testing.T) {
    self := &Etcd{
        config:     EtcdConfig{Prefix: "/clusterf"},
        services:   newServices(),
    }

    for _, testCase := range testSyncErrors {
        node := &etcd.Node{
            Key:    testCase.key,
            Dir:    testCase.dir,
            Value:  testCase.value,
        }
        var events []*Event

        log.Printf("--- %+v\n", testCase)
        err := self.sync(testCase.action, node, func (event *Event) {
            if event != nil {
                events = append(events, event)
            }
        })

        if err != nil {
            if testCase.error == "" {
                t.Errorf("error %+v: error %s", testCase, err)
            } else if !regexp.MustCompile(testCase.error).MatchString(err.Error()) {
                t.Errorf("fail %+v: error: %s", testCase, err)
            }
        } else if testCase.error != "" {
            t.Errorf("fail %+v: error nil", testCase)
        }

        for i := 0; i < len(events) || i < len(testCase.events); i++ {
            if i >= len(events) {
                t.Errorf("fail %+v: missing event %+v", testCase, testCase.events[i])
            } else if i >= len(testCase.events) {
                t.Errorf("fail %+v: extra event %+v", testCase, events[i])
            } else {
                if events[i].Type != testCase.events[i].Type {
                    t.Errorf("fail %+v: event %+v type", testCase, events[i])
                }

                if events[i].Service == nil {
                    t.Errorf("fail %+v: event %+v service", testCase, events[i])
                } else if events[i].Service.Name != testCase.events[i].Service.Name {
                    t.Errorf("fail %+v: event %+v service name", testCase, events[i])
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung)
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package example_test

import (
	"errors"

	"github.com/jung-kurt/gofpdf/internal/example"
)

// Test the Filename() and Summary() functions.
func ExampleExample_Filename() {
	fileStr := example.Filename("example")
	example.Summary(errors.New("printer on fire"), fileStr)
	// Output:
	// printer on fire
}
<commit_msg>Change example test function name to comply with go vet<commit_after>/*
 * Copyright (c) 2015 Kurt Jung (Gmail: kurt.w.jung)
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package example_test

import (
	"errors"

	"github.com/jung-kurt/gofpdf/internal/example"
)

// Test the Filename() and Summary() functions.
func ExampleFilename() {
	fileStr := example.Filename("example")
	example.Summary(errors.New("printer on fire"), fileStr)
	// Output:
	// printer on fire
}
<|endoftext|>


<commit_before>package handler

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"regexp"
	"strconv"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/brocaar/lorawan"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"github.com/garyburd/redigo/redis"
)

const txTopic = "application/+/node/+/tx"
const downlinkLockTTL = time.Millisecond * 100

var txTopicRegex = regexp.MustCompile(`application/(\w+)/node/(\w+)/tx`)

// DataRate contains the data-rate related fields.
type DataRate struct {
	Modulation   string `json:"modulation"`
	Bandwidth    int    `json:"bandwidth"`
	SpreadFactor int    `json:"spreadFactor,omitempty"`
	Bitrate      int    `json:"bitrate,omitempty"`
}

// RXInfo contains the RX information.
type RXInfo struct {
	MAC     lorawan.EUI64 `json:"mac"`
	Time    *time.Time    `json:"time,omitempty"`
	RSSI    int           `json:"rssi"`
	LoRaSNR float64       `json:"loRaSNR"`
}

// TXInfo contains the TX information.
type TXInfo struct {
	Frequency int      `json:"frequency"`
	DataRate  DataRate `json:"dataRate"`
	ADR       bool     `json:"adr"`
	CodeRate  string   `json:"codeRate"`
}

// DataUpPayload represents a data-up payload.
type DataUpPayload struct {
	ApplicationID   int64         `json:"applicationID"`
	ApplicationName string        `json:"applicationName"`
	NodeName        string        `json:"nodeName"`
	DevEUI          lorawan.EUI64 `json:"devEUI"`
	RXInfo          []RXInfo      `json:"rxInfo"`
	TXInfo          TXInfo        `json:"txInfo"`
	FCnt            uint32        `json:"fCnt"`
	FPort           uint8         `json:"fPort"`
	Data            []byte        `json:"data"`
}

// DataDownPayload represents a data-down payload.
type DataDownPayload struct {
	ApplicationID int64         `json:"applicationID"`
	DevEUI        lorawan.EUI64 `json:"devEUI"`
	Reference     string        `json:"reference"`
	Confirmed     bool          `json:"confirmed"`
	FPort         uint8         `json:"fPort"`
	Data          []byte        `json:"data"`
}

// JoinNotification defines the payload sent to the application on
// a JoinNotificationType event.
type JoinNotification struct {
	ApplicationID   int64           `json:"applicationID"`
	ApplicationName string          `json:"applicationName"`
	NodeName        string          `json:"nodeName"`
	DevEUI          lorawan.EUI64   `json:"devEUI"`
	DevAddr         lorawan.DevAddr `json:"devAddr"`
}

// MQTTHandler implements a MQTT handler for sending and receiving data by
// an application.
type MQTTHandler struct {
	conn         mqtt.Client
	dataDownChan chan DataDownPayload
	wg           sync.WaitGroup
	redisPool    *redis.Pool
}

// ACKNotification defines the payload sent to the application
// on an ACK event.
type ACKNotification struct {
	ApplicationID   int64         `json:"applicationID"`
	ApplicationName string        `json:"applicationName"`
	NodeName        string        `json:"nodeName"`
	DevEUI          lorawan.EUI64 `json:"devEUI"`
	Reference       string        `json:"reference"`
}

// ErrorNotification defines the payload sent to the application
// on an error event.
type ErrorNotification struct {
	ApplicationID   int64         `json:"applicationID"`
	ApplicationName string        `json:"applicationName"`
	NodeName        string        `json:"nodeName"`
	DevEUI          lorawan.EUI64 `json:"devEUI"`
	Type            string        `json:"type"`
	Error           string        `json:"error"`
}

// NewMQTTHandler creates a new MQTTHandler.
func NewMQTTHandler(p *redis.Pool, server, username, password string) (Handler, error) {
	h := MQTTHandler{
		dataDownChan: make(chan DataDownPayload),
		redisPool:    p,
	}

	opts := mqtt.NewClientOptions()
	opts.AddBroker(server)
	opts.SetUsername(username)
	opts.SetPassword(password)
	opts.SetOnConnectHandler(h.onConnected)
	opts.SetConnectionLostHandler(h.onConnectionLost)

	log.WithField("server", server).Info("handler/mqtt: connecting to mqtt broker")
	h.conn = mqtt.NewClient(opts)
	if token := h.conn.Connect(); token.Wait() && token.Error() != nil {
		return nil, fmt.Errorf("handler/mqtt: connecting to broker error: %s", token.Error())
	}
	return &h, nil
}

// Close stops the handler.
func (h *MQTTHandler) Close() error {
	log.Info("handler/mqtt: closing handler")
	log.WithField("topic", txTopic).Info("handler/mqtt: unsubscribing from tx topic")
	if token := h.conn.Unsubscribe(txTopic); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: unsubscribe from %s error: %s", txTopic, token.Error())
	}
	log.Info("handler/mqtt: handling last items in queue")
	h.wg.Wait()
	close(h.dataDownChan)
	return nil
}

// SendDataUp sends a DataUpPayload.
func (h *MQTTHandler) SendDataUp(payload DataUpPayload) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("handler/mqtt: data-up payload marshal error: %s", err)
	}

	topic := fmt.Sprintf("application/%d/node/%s/rx", payload.ApplicationID, payload.DevEUI)
	log.WithField("topic", topic).Info("handler/mqtt: publishing data-up payload")
	if token := h.conn.Publish(topic, 0, false, b); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: publish data-up payload error: %s", err)
	}
	return nil
}

// SendJoinNotification sends a JoinNotification.
func (h *MQTTHandler) SendJoinNotification(payload JoinNotification) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("handler/mqtt: join notification marshal error: %s", err)
	}
	topic := fmt.Sprintf("application/%d/node/%s/join", payload.ApplicationID, payload.DevEUI)
	log.WithField("topic", topic).Info("handler/mqtt: publishing join notification")
	if token := h.conn.Publish(topic, 0, false, b); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: publish join notification error: %s", err)
	}
	return nil
}

// SendACKNotification sends an ACKNotification.
func (h *MQTTHandler) SendACKNotification(payload ACKNotification) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("handler/mqtt: ack notification marshal error: %s", err)
	}
	topic := fmt.Sprintf("application/%d/node/%s/ack", payload.ApplicationID, payload.DevEUI)
	log.WithField("topic", topic).Info("handler/mqtt: publishing ack notification")
	if token := h.conn.Publish(topic, 0, false, b); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: publish ack notification error: %s", err)
	}
	return nil
}

// SendErrorNotification sends an ErrorNotification.
func (h *MQTTHandler) SendErrorNotification(payload ErrorNotification) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("handler/mqtt: error notification marshal error: %s", err)
	}
	topic := fmt.Sprintf("application/%d/node/%s/error", payload.ApplicationID, payload.DevEUI)
	log.WithField("topic", topic).Info("handler/mqtt: publishing error notification")
	if token := h.conn.Publish(topic, 0, false, b); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: publish error notification error: %s", err)
	}
	return nil
}

// DataDownChan returns the channel containing the received DataDownPayload.
func (h *MQTTHandler) DataDownChan() chan DataDownPayload {
	return h.dataDownChan
}

func (h *MQTTHandler) txPayloadHandler(c mqtt.Client, msg mqtt.Message) {
	h.wg.Add(1)
	defer h.wg.Done()

	log.WithField("topic", msg.Topic()).Info("handler/mqtt: data-down payload received")

	// get the name of the application and node from the topic
	match := txTopicRegex.FindStringSubmatch(msg.Topic())
	if len(match) != 3 {
		log.WithField("topic", msg.Topic()).Error("handler/mqtt: topic regex match error")
		return
	}

	var pl DataDownPayload
	dec := json.NewDecoder(bytes.NewReader(msg.Payload()))
	if err := dec.Decode(&pl); err != nil {
		log.WithFields(log.Fields{
			"data_base64": base64.StdEncoding.EncodeToString(msg.Payload()),
		}).Errorf("handler/mqtt: tx payload unmarshal error: %s", err)
		return
	}

	// set ApplicationID and DevEUI from topic
	var err error
	pl.ApplicationID, err = strconv.ParseInt(match[1], 10, 64)
	if err != nil {
		log.WithFields(log.Fields{
			"topic": msg.Topic(),
		}).Errorf("handler/mqtt: parse application id error: %s", err)
		return
	}

	if err = pl.DevEUI.UnmarshalText([]byte(match[2])); err != nil {
		log.WithFields(log.Fields{
			"topic": msg.Topic(),
		}).Errorf("handler/mqtt: parse dev_eui error: %s", err)
		return
	}

	// Since with MQTT all subscribers will receive the downlink messages sent
	// by the application, the first instance receiving the message must lock it,
	// so that other instances can ignore the message.
	// As an unique id, the Reference field is used.
	key := fmt.Sprintf("lora:as:downlink:lock:%d:%s:%s", pl.ApplicationID, pl.DevEUI, pl.Reference)
	redisConn := h.redisPool.Get()
	defer redisConn.Close()

	_, err = redis.String(redisConn.Do("SET", key, "lock", "PX", int64(downlinkLockTTL/time.Millisecond), "NX"))
	if err != nil {
		if err == redis.ErrNil {
			// the payload is already being processed by an other instance
			return
		}
		log.Errorf("handler/mqtt: acquire downlink payload lock error: %s", err)
		return
	}

	h.dataDownChan <- pl
}

func (h *MQTTHandler) onConnected(c mqtt.Client) {
	log.Info("handler/mqtt: connected to mqtt broker")
	for {
		log.WithField("topic", txTopic).Info("handler/mqtt: subscribling to tx topic")
		if token := h.conn.Subscribe(txTopic, 2, h.txPayloadHandler); token.Wait() && token.Error() != nil {
			log.WithField("topic", txTopic).Errorf("handler/mqtt: subscribe error: %s", token.Error())
			time.Sleep(time.Second)
			continue
		}
		return
	}
}

func (h *MQTTHandler) onConnectionLost(c mqtt.Client, reason error) {
	log.Errorf("handler/mqtt: mqtt connection error: %s", reason)
}
<commit_msg>Output int64 as string.<commit_after>package handler

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"regexp"
	"strconv"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/brocaar/lorawan"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"github.com/garyburd/redigo/redis"
)

const txTopic = "application/+/node/+/tx"
const downlinkLockTTL = time.Millisecond * 100

var txTopicRegex = regexp.MustCompile(`application/(\w+)/node/(\w+)/tx`)

// DataRate contains the data-rate related fields.
type DataRate struct {
	Modulation   string `json:"modulation"`
	Bandwidth    int    `json:"bandwidth"`
	SpreadFactor int    `json:"spreadFactor,omitempty"`
	Bitrate      int    `json:"bitrate,omitempty"`
}

// RXInfo contains the RX information.
type RXInfo struct {
	MAC     lorawan.EUI64 `json:"mac"`
	Time    *time.Time    `json:"time,omitempty"`
	RSSI    int           `json:"rssi"`
	LoRaSNR float64       `json:"loRaSNR"`
}

// TXInfo contains the TX information.
type TXInfo struct {
	Frequency int      `json:"frequency"`
	DataRate  DataRate `json:"dataRate"`
	ADR       bool     `json:"adr"`
	CodeRate  string   `json:"codeRate"`
}

// DataUpPayload represents a data-up payload.
type DataUpPayload struct {
	ApplicationID   int64         `json:"applicationID,string"`
	ApplicationName string        `json:"applicationName"`
	NodeName        string        `json:"nodeName"`
	DevEUI          lorawan.EUI64 `json:"devEUI"`
	RXInfo          []RXInfo      `json:"rxInfo"`
	TXInfo          TXInfo        `json:"txInfo"`
	FCnt            uint32        `json:"fCnt"`
	FPort           uint8         `json:"fPort"`
	Data            []byte        `json:"data"`
}

// DataDownPayload represents a data-down payload.
type DataDownPayload struct {
	ApplicationID int64         `json:"applicationID,string"`
	DevEUI        lorawan.EUI64 `json:"devEUI"`
	Reference     string        `json:"reference"`
	Confirmed     bool          `json:"confirmed"`
	FPort         uint8         `json:"fPort"`
	Data          []byte        `json:"data"`
}

// JoinNotification defines the payload sent to the application on
// a JoinNotificationType event.
type JoinNotification struct {
	ApplicationID   int64           `json:"applicationID,string"`
	ApplicationName string          `json:"applicationName"`
	NodeName        string          `json:"nodeName"`
	DevEUI          lorawan.EUI64   `json:"devEUI"`
	DevAddr         lorawan.DevAddr `json:"devAddr"`
}

// MQTTHandler implements a MQTT handler for sending and receiving data by
// an application.
type MQTTHandler struct {
	conn         mqtt.Client
	dataDownChan chan DataDownPayload
	wg           sync.WaitGroup
	redisPool    *redis.Pool
}

// ACKNotification defines the payload sent to the application
// on an ACK event.
type ACKNotification struct {
	ApplicationID   int64         `json:"applicationID,string"`
	ApplicationName string        `json:"applicationName"`
	NodeName        string        `json:"nodeName"`
	DevEUI          lorawan.EUI64 `json:"devEUI"`
	Reference       string        `json:"reference"`
}

// ErrorNotification defines the payload sent to the application
// on an error event.
type ErrorNotification struct {
	ApplicationID   int64         `json:"applicationID,string"`
	ApplicationName string        `json:"applicationName"`
	NodeName        string        `json:"nodeName"`
	DevEUI          lorawan.EUI64 `json:"devEUI"`
	Type            string        `json:"type"`
	Error           string        `json:"error"`
}

// NewMQTTHandler creates a new MQTTHandler.
func NewMQTTHandler(p *redis.Pool, server, username, password string) (Handler, error) {
	h := MQTTHandler{
		dataDownChan: make(chan DataDownPayload),
		redisPool:    p,
	}

	opts := mqtt.NewClientOptions()
	opts.AddBroker(server)
	opts.SetUsername(username)
	opts.SetPassword(password)
	opts.SetOnConnectHandler(h.onConnected)
	opts.SetConnectionLostHandler(h.onConnectionLost)

	log.WithField("server", server).Info("handler/mqtt: connecting to mqtt broker")
	h.conn = mqtt.NewClient(opts)
	if token := h.conn.Connect(); token.Wait() && token.Error() != nil {
		return nil, fmt.Errorf("handler/mqtt: connecting to broker error: %s", token.Error())
	}
	return &h, nil
}

// Close stops the handler.
func (h *MQTTHandler) Close() error {
	log.Info("handler/mqtt: closing handler")
	log.WithField("topic", txTopic).Info("handler/mqtt: unsubscribing from tx topic")
	if token := h.conn.Unsubscribe(txTopic); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: unsubscribe from %s error: %s", txTopic, token.Error())
	}
	log.Info("handler/mqtt: handling last items in queue")
	h.wg.Wait()
	close(h.dataDownChan)
	return nil
}

// SendDataUp sends a DataUpPayload.
func (h *MQTTHandler) SendDataUp(payload DataUpPayload) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("handler/mqtt: data-up payload marshal error: %s", err)
	}

	topic := fmt.Sprintf("application/%d/node/%s/rx", payload.ApplicationID, payload.DevEUI)
	log.WithField("topic", topic).Info("handler/mqtt: publishing data-up payload")
	if token := h.conn.Publish(topic, 0, false, b); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: publish data-up payload error: %s", err)
	}
	return nil
}

// SendJoinNotification sends a JoinNotification.
func (h *MQTTHandler) SendJoinNotification(payload JoinNotification) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("handler/mqtt: join notification marshal error: %s", err)
	}
	topic := fmt.Sprintf("application/%d/node/%s/join", payload.ApplicationID, payload.DevEUI)
	log.WithField("topic", topic).Info("handler/mqtt: publishing join notification")
	if token := h.conn.Publish(topic, 0, false, b); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: publish join notification error: %s", err)
	}
	return nil
}

// SendACKNotification sends an ACKNotification.
func (h *MQTTHandler) SendACKNotification(payload ACKNotification) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("handler/mqtt: ack notification marshal error: %s", err)
	}
	topic := fmt.Sprintf("application/%d/node/%s/ack", payload.ApplicationID, payload.DevEUI)
	log.WithField("topic", topic).Info("handler/mqtt: publishing ack notification")
	if token := h.conn.Publish(topic, 0, false, b); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: publish ack notification error: %s", err)
	}
	return nil
}

// SendErrorNotification sends an ErrorNotification.
func (h *MQTTHandler) SendErrorNotification(payload ErrorNotification) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("handler/mqtt: error notification marshal error: %s", err)
	}
	topic := fmt.Sprintf("application/%d/node/%s/error", payload.ApplicationID, payload.DevEUI)
	log.WithField("topic", topic).Info("handler/mqtt: publishing error notification")
	if token := h.conn.Publish(topic, 0, false, b); token.Wait() && token.Error() != nil {
		return fmt.Errorf("handler/mqtt: publish error notification error: %s", err)
	}
	return nil
}

// DataDownChan returns the channel containing the received DataDownPayload.
func (h *MQTTHandler) DataDownChan() chan DataDownPayload {
	return h.dataDownChan
}

func (h *MQTTHandler) txPayloadHandler(c mqtt.Client, msg mqtt.Message) {
	h.wg.Add(1)
	defer h.wg.Done()

	log.WithField("topic", msg.Topic()).Info("handler/mqtt: data-down payload received")

	// get the name of the application and node from the topic
	match := txTopicRegex.FindStringSubmatch(msg.Topic())
	if len(match) != 3 {
		log.WithField("topic", msg.Topic()).Error("handler/mqtt: topic regex match error")
		return
	}

	var pl DataDownPayload
	dec := json.NewDecoder(bytes.NewReader(msg.Payload()))
	if err := dec.Decode(&pl); err != nil {
		log.WithFields(log.Fields{
			"data_base64": base64.StdEncoding.EncodeToString(msg.Payload()),
		}).Errorf("handler/mqtt: tx payload unmarshal error: %s", err)
		return
	}

	// set ApplicationID and DevEUI from topic
	var err error
	pl.ApplicationID, err = strconv.ParseInt(match[1], 10, 64)
	if err != nil {
		log.WithFields(log.Fields{
			"topic": msg.Topic(),
		}).Errorf("handler/mqtt: parse application id error: %s", err)
		return
	}

	if err = pl.DevEUI.UnmarshalText([]byte(match[2])); err != nil {
		log.WithFields(log.Fields{
			"topic": msg.Topic(),
		}).Errorf("handler/mqtt: parse dev_eui error: %s", err)
		return
	}

	// Since with MQTT all subscribers will receive the downlink messages sent
	// by the application, the first instance receiving the message must lock it,
	// so that other instances can ignore the message.
	// As an unique id, the Reference field is used.
	key := fmt.Sprintf("lora:as:downlink:lock:%d:%s:%s", pl.ApplicationID, pl.DevEUI, pl.Reference)
	redisConn := h.redisPool.Get()
	defer redisConn.Close()

	_, err = redis.String(redisConn.Do("SET", key, "lock", "PX", int64(downlinkLockTTL/time.Millisecond), "NX"))
	if err != nil {
		if err == redis.ErrNil {
			// the payload is already being processed by an other instance
			return
		}
		log.Errorf("handler/mqtt: acquire downlink payload lock error: %s", err)
		return
	}

	h.dataDownChan <- pl
}

func (h *MQTTHandler) onConnected(c mqtt.Client) {
	log.Info("handler/mqtt: connected to mqtt broker")
	for {
		log.WithField("topic", txTopic).Info("handler/mqtt: subscribling to tx topic")
		if token := h.conn.Subscribe(txTopic, 2, h.txPayloadHandler); token.Wait() && token.Error() != nil {
			log.WithField("topic", txTopic).Errorf("handler/mqtt: subscribe error: %s", token.Error())
			time.Sleep(time.Second)
			continue
		}
		return
	}
}

func (h *MQTTHandler) onConnectionLost(c mqtt.Client, reason error) {
	log.Errorf("handler/mqtt: mqtt connection error: %s", reason)
}
<|endoftext|>


<commit_before>package main

func initRString() *RObject {
	obj := &RObject{}
	obj.name = "RString"
	obj.ivars = make(map[string]Object)
	obj.class = nil
	obj.methods = make(map[string]*RMethod)

	// RString method initialization
	obj.methods["new"] = &RMethod{gofunc: RString_new}
	obj.methods["to_s"] = &RMethod{gofunc: RString_to_s}

	return obj
}

// String.new(str='')
// v = [string]
func RString_new(vm *GobiesVM, receiver Object, v []Object) Object {
	str := ""
	if len(v) == 1 {
		str = v[0].(string)
	}

	obj := &RObject{}
	obj.class = vm.consts["RString"]
	obj.val.str = str

	return obj
}

func RString_to_s(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(RObject)
	return obj.val.str
}
<commit_msg>[RString] length functions implemented<commit_after>package main

func initRString() *RObject {
	obj := &RObject{}
	obj.name = "RString"
	obj.ivars = make(map[string]Object)
	obj.class = nil
	obj.methods = make(map[string]*RMethod)

	// RString method initialization
	obj.methods["new"] = &RMethod{gofunc: RString_new}
	obj.methods["to_s"] = &RMethod{gofunc: RString_to_s}
	obj.methods["size"] = &RMethod{gofunc: RString_length}
	obj.methods["len"] = &RMethod{gofunc: RString_length}

	return obj
}

// String.new(str='')
// v = [string]
func RString_new(vm *GobiesVM, receiver Object, v []Object) Object {
	str := ""
	if len(v) == 1 {
		str = v[0].(string)
	}

	obj := &RObject{}
	obj.class = vm.consts["RString"]
	obj.val.str = str

	return obj
}

func RString_to_s(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(RObject)
	return obj.val.str
}

func RString_length(vm *GobiesVM, receiver Object, v []Object) Object {
	obj := receiver.(RObject)
	arg := make([]Object, 1, 1)
	arg[0] = int64(len(obj.val.str))
	return RFixnum_new(vm, receiver, arg)
}
<|endoftext|>


<commit_before>package tcp

import (
	"net"
	"sync"
	"time"

	log "github.com/cihub/seelog"
	"github.com/pkg/errors"
	"github.com/schollz/croc/src/comm"
	"github.com/schollz/croc/src/logger"
	"github.com/schollz/croc/src/models"
)

type roomInfo struct {
	receiver comm.Comm
	opened   time.Time
}

type roomMap struct {
	rooms map[string]roomInfo
	sync.Mutex
}

var rooms roomMap

// Run starts a tcp listener, run async
func Run(debugLevel, port string) {
	logger.SetLogLevel(debugLevel)
	rooms.Lock()
	rooms.rooms = make(map[string]roomInfo)
	rooms.Unlock()
	err := run(port)
	if err != nil {
		log.Error(err)
	}

	// TODO:
	// delete old rooms
}

func run(port string) (err error) {
	log.Debugf("starting TCP server on " + port)
	server, err := net.Listen("tcp", "0.0.0.0:"+port)
	if err != nil {
		return errors.Wrap(err, "Error listening on :"+port)
	}
	defer server.Close()
	// spawn a new goroutine whenever a client connects
	for {
		connection, err := server.Accept()
		if err != nil {
			return errors.Wrap(err, "problem accepting connection")
		}
		log.Debugf("client %s connected", connection.RemoteAddr().String())
		go func(port string, connection net.Conn) {
			errCommunication := clientCommuncation(port, comm.New(connection))
			if errCommunication != nil {
				log.Warnf("relay-%s: %s", connection.RemoteAddr().String(), errCommunication.Error())
			}
		}(port, connection)
	}
}

func clientCommuncation(port string, c comm.Comm) (err error) {
	// send ok to tell client they are connected
	err = c.Send("ok")
	if err != nil {
		return
	}

	// wait for client to tell me which room they want
	room, err := c.Receive()
	if err != nil {
		return
	}

	rooms.Lock()
	// first connection is always the receiver
	if _, ok := rooms.rooms[room]; !ok {
		rooms.rooms[room] = roomInfo{
			receiver: c,
			opened:   time.Now(),
		}
		rooms.Unlock()
		// tell the client that they got the room
		err = c.Send("recipient")
		if err != nil {
			return
		}
		return nil
	}
	receiver := rooms.rooms[room].receiver
	rooms.Unlock()

	// second connection is the sender, time to staple connections
	var wg sync.WaitGroup
	wg.Add(1)

	// start piping
	go func(com1, com2 comm.Comm, wg *sync.WaitGroup) {
		log.Debug("starting pipes")
		pipe(com1.Connection(), com2.Connection())
		wg.Done()
		log.Debug("done piping")
	}(c, receiver, &wg)

	// tell the sender everything is ready
	err = c.Send("sender")
	if err != nil {
		return
	}
	wg.Wait()

	// delete room
	rooms.Lock()
	log.Debugf("deleting room: %s", room)
	delete(rooms.rooms, room)
	rooms.Unlock()
	return nil
}

// chanFromConn creates a channel from a Conn object, and sends everything it
//  Read()s from the socket to the channel.
func chanFromConn(conn net.Conn) chan []byte {
	c := make(chan []byte)

	go func() {
		b := make([]byte, models.TCP_BUFFER_SIZE)

		for {
			n, err := conn.Read(b)
			if n > 0 {
				res := make([]byte, n)
				// Copy the buffer so it doesn't get changed while read by the recipient.
				copy(res, b[:n])
				c <- res
			}
			if err != nil {
				c <- nil
				break
			}
		}
	}()

	return c
}

// pipe creates a full-duplex pipe between the two sockets and
// transfers data from one to the other.
func pipe(conn1 net.Conn, conn2 net.Conn) {
	chan1 := chanFromConn(conn1)
	chan2 := chanFromConn(conn2)

	for {
		select {
		case b1 := <-chan1:
			if b1 == nil {
				return
			}
			conn2.Write(b1)

		case b2 := <-chan2:
			if b2 == nil {
				return
			}
			conn1.Write(b2)
		}
	}
}
<commit_msg>optimize relay<commit_after>package tcp

import (
	"net"
	"sync"
	"time"

	log "github.com/cihub/seelog"
	"github.com/pkg/errors"
	"github.com/schollz/croc/src/comm"
	"github.com/schollz/croc/src/logger"
	"github.com/schollz/croc/src/models"
)

type roomInfo struct {
	receiver comm.Comm
	opened   time.Time
}

type roomMap struct {
	rooms map[string]roomInfo
	sync.Mutex
}

var rooms roomMap

// Run starts a tcp listener, run async
func Run(debugLevel, port string) {
	logger.SetLogLevel(debugLevel)
	rooms.Lock()
	rooms.rooms = make(map[string]roomInfo)
	rooms.Unlock()
	err := run(port)
	if err != nil {
		log.Error(err)
	}

	// TODO:
	// delete old rooms
}

func run(port string) (err error) {
	log.Debugf("starting TCP server on " + port)
	server, err := net.Listen("tcp", "0.0.0.0:"+port)
	if err != nil {
		return errors.Wrap(err, "Error listening on :"+port)
	}
	defer server.Close()
	// spawn a new goroutine whenever a client connects
	for {
		connection, err := server.Accept()
		if err != nil {
			return errors.Wrap(err, "problem accepting connection")
		}
		log.Debugf("client %s connected", connection.RemoteAddr().String())
		go func(port string, connection net.Conn) {
			errCommunication := clientCommuncation(port, comm.New(connection))
			if errCommunication != nil {
				log.Warnf("relay-%s: %s", connection.RemoteAddr().String(), errCommunication.Error())
			}
		}(port, connection)
	}
}

func clientCommuncation(port string, c comm.Comm) (err error) {
	// send ok to tell client they are connected
	err = c.Send("ok")
	if err != nil {
		return
	}

	// wait for client to tell me which room they want
	room, err := c.Receive()
	if err != nil {
		return
	}

	rooms.Lock()
	// first connection is always the receiver
	if _, ok := rooms.rooms[room]; !ok {
		rooms.rooms[room] = roomInfo{
			receiver: c,
			opened:   time.Now(),
		}
		rooms.Unlock()
		// tell the client that they got the room
		err = c.Send("recipient")
		if err != nil {
			return
		}
		return nil
	}
	receiver := rooms.rooms[room].receiver
	rooms.Unlock()

	// second connection is the sender, time to staple connections
	var wg sync.WaitGroup
	wg.Add(1)

	// start piping
	go func(com1, com2 comm.Comm, wg *sync.WaitGroup) {
		log.Debug("starting pipes")
		pipe(com1.Connection(), com2.Connection())
		wg.Done()
		log.Debug("done piping")
	}(c, receiver, &wg)

	// tell the sender everything is ready
	err = c.Send("sender")
	if err != nil {
		return
	}
	wg.Wait()

	// delete room
	rooms.Lock()
	log.Debugf("deleting room: %s", room)
	delete(rooms.rooms, room)
	rooms.Unlock()
	return nil
}

// chanFromConn creates a channel from a Conn object, and sends everything it
//  Read()s from the socket to the channel.
func chanFromConn(conn net.Conn) chan []byte {
	c := make(chan []byte)

	go func() {

		for {
			b := make([]byte, models.TCP_BUFFER_SIZE)
			_, err := conn.Read(b)
			if err != nil {
				c <- nil
				break
			} else {
				c <- b
			}
		}
	}()

	return c
}

// pipe creates a full-duplex pipe between the two sockets and
// transfers data from one to the other.
func pipe(conn1 net.Conn, conn2 net.Conn) {
	chan1 := chanFromConn(conn1)
	chan2 := chanFromConn(conn2)

	for {
		select {
		case b1 := <-chan1:
			if b1 == nil {
				return
			}
			conn2.Write(b1)

		case b2 := <-chan2:
			if b2 == nil {
				return
			}
			conn1.Write(b2)
		}
	}
}
<|endoftext|>


<commit_before>package services

import (
	"github.com/jinzhu/gorm"
	"github.com/pufferpanel/pufferpanel/models"
	uuid2 "github.com/satori/go.uuid"
	"strings"
)

type Server struct {
	DB *gorm.DB
}

type ServerSearch struct {
	Username string
	NodeId   uint
	NodeName string
	Name     string
	PageSize uint
	Page     uint
}

func (ss *Server) Search(searchCriteria ServerSearch) (*models.Servers, uint, error) {
	servers := &models.Servers{}

	query := ss.DB

	if searchCriteria.NodeId != 0 {
		query = query.Where(&models.Server{NodeID: searchCriteria.NodeId})
	} else if searchCriteria.NodeName != "" {
		query = query.Joins("JOIN nodes n ON servers.node_id = n.id AND n.name = ?", searchCriteria.NodeName)
	}

	if searchCriteria.Username != "" {
		query = query.Joins("JOIN client_server_scopes css ON css.server_id = servers.id AND css.scope = 'servers.view'")
		query = query.Joins("JOIN client_infos ci ON ci.id = css.client_info_id")
		query = query.Joins("JOIN users ON users.id = ci.user_id")
		query = query.Where("users.username = ?", searchCriteria.Username)
	}

	nameFilter := strings.Replace(searchCriteria.Name, "*", "%", -1)

	if nameFilter != "" && nameFilter != "%" {
		query = query.Where("name LIKE ?", nameFilter)
	}

	var count uint
	err := query.Model(&servers).Count(&count).Error

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

	res := query.Preload("Node").Offset((searchCriteria.Page - 1) * searchCriteria.PageSize).Limit(searchCriteria.PageSize).Find(servers)

	return servers, count, res.Error
}

func (ss *Server) Get(id string) (*models.Server, bool, error) {
	if id == "" {
		return nil, false, nil
	}
	model := &models.Server{
		Identifier: id,
	}

	res := ss.DB.Where(model).Preload("Node").First(model)

	return model, model.ID != 0, res.Error
}

func (ss *Server) Update(model *models.Server) error {
	res := ss.DB.Save(model)
	return res.Error
}

func (ss *Server) Delete(id uint) error {
	model := &models.Server{
		ID: id,
	}

	res := ss.DB.Delete(model)
	return res.Error
}

func (ss *Server) Create(model *models.Server) (err error) {
	uuid := uuid2.NewV4()
	generatedId := strings.ToUpper(uuid.String())[0:8]
	model.Identifier = generatedId

	res := ss.DB.Create(model)
	if res.Error != nil {
		err = res.Error
		return
	}
	return
}
<commit_msg>Enforce name sort, fix create to only generate an id if not provided<commit_after>package services

import (
	"github.com/jinzhu/gorm"
	"github.com/pufferpanel/pufferpanel/models"
	uuid2 "github.com/satori/go.uuid"
	"strings"
)

type Server struct {
	DB *gorm.DB
}

type ServerSearch struct {
	Username string
	NodeId   uint
	NodeName string
	Name     string
	PageSize uint
	Page     uint
}

func (ss *Server) Search(searchCriteria ServerSearch) (*models.Servers, uint, error) {
	servers := &models.Servers{}

	query := ss.DB

	if searchCriteria.NodeId != 0 {
		query = query.Where(&models.Server{NodeID: searchCriteria.NodeId})
	} else if searchCriteria.NodeName != "" {
		query = query.Joins("JOIN nodes n ON servers.node_id = n.id AND n.name = ?", searchCriteria.NodeName)
	}

	if searchCriteria.Username != "" {
		query = query.Joins("JOIN client_server_scopes css ON css.server_id = servers.id AND css.scope = 'servers.view'")
		query = query.Joins("JOIN client_infos ci ON ci.id = css.client_info_id")
		query = query.Joins("JOIN users ON users.id = ci.user_id")
		query = query.Where("users.username = ?", searchCriteria.Username)
	}

	nameFilter := strings.Replace(searchCriteria.Name, "*", "%", -1)

	if nameFilter != "" && nameFilter != "%" {
		query = query.Where("name LIKE ?", nameFilter)
	}

	var count uint
	err := query.Model(&servers).Count(&count).Error

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

	res := query.Preload("Node").Offset((searchCriteria.Page - 1) * searchCriteria.PageSize).Limit(searchCriteria.PageSize).Order("servers.name").Find(servers)

	return servers, count, res.Error
}

func (ss *Server) Get(id string) (*models.Server, bool, error) {
	if id == "" {
		return nil, false, nil
	}
	model := &models.Server{
		Identifier: id,
	}

	res := ss.DB.Where(model).Preload("Node").First(model)

	return model, model.ID != 0, res.Error
}

func (ss *Server) Update(model *models.Server) error {
	res := ss.DB.Save(model)
	return res.Error
}

func (ss *Server) Delete(id uint) error {
	model := &models.Server{
		ID: id,
	}

	res := ss.DB.Delete(model)
	return res.Error
}

func (ss *Server) Create(model *models.Server) (err error) {
	if model.Identifier == "" {
		uuid := uuid2.NewV4()
		generatedId := strings.ToUpper(uuid.String())[0:8]
		model.Identifier = generatedId
	}

	res := ss.DB.Create(model)
	if res.Error != nil {
		err = res.Error
		return
	}
	return
}
<|endoftext|>


<commit_before>// store provices an implementation of sessions.Store that also carries its name around with it.
package store

import (
	"time"

	"encoding/hex"

	"fmt"

	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
)

var (
	sessionAge = 24 * time.Hour
)

// Name is a sessions.Store that also has its own name
type Namer interface {
	sessions.Store
	Name() string
}

type namedStore struct {
	*sessions.CookieStore
	domain string
	name   string
	secret string
}

// Name implements the Namer interface, returning this store's name
func (s *namedStore) Name() string {
	return s.name
}

var (
	// ErrMissingName is returned when attempting to create a Namer with an empty name
	ErrMissingName = fmt.Errorf("name cannot be empty")

	// ErrMissingDomain returned when attempting to create a Namer with an empty domain
	ErrMissingDomain = fmt.Errorf("domain cannot be empty")
)

// A KeyError is returned when attmpting to create a Namer with an invalid key.  Keys must be
// hex-encoded strings with length 128.
type KeyError struct {
	sample []byte
}

func (k KeyError) Error() string {
	return fmt.Sprintf(
		"Session store secret should be a 128-character hex-encoded string.  "+
			"Here's a freshly generated one: %q", hex.EncodeToString(k.sample))
}

func (s *namedStore) init() error {
	if s.name == "" {
		return ErrMissingName
	}
	if s.domain == "" {
		return ErrMissingDomain
	}
	key, err := hex.DecodeString(s.secret)
	if err != nil || len(key) != 64 {
		return KeyError{sample: securecookie.GenerateRandomKey(64)}
	}
	cookieStore := sessions.NewCookieStore(key)
	cookieStore.Options.HttpOnly = true
	cookieStore.Options.Secure = true
	cookieStore.Options.Domain = s.domain
	cookieStore.Options.MaxAge = int(sessionAge / time.Second)
	s.CookieStore = cookieStore
	return nil
}

// New returns a new Namer for the given name, secret key and domain.  secret should be a hex-encoded
// string with length 128.
func New(name, secret, domain string) (Namer, error) {
	sc := &namedStore{name: name, domain: domain, secret: secret}
	err := sc.init()
	return sc, err
}
<commit_msg>Simplify<commit_after>// store provices an implementation of sessions.Store that also carries its name around with it.
package store

import (
	"encoding/hex"
	"fmt"
	"time"

	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
)

var (
	sessionAge = 24 * time.Hour
)

// Name is a sessions.Store that also has its own name
type Namer interface {
	sessions.Store
	Name() string
}

type namedStore struct {
	*sessions.CookieStore
	domain string
	name   string
	secret string
}

// Name implements the Namer interface, returning this store's name
func (s *namedStore) Name() string {
	return s.name
}

var (
	// ErrMissingName is returned when attempting to create a Namer with an empty name
	ErrMissingName = fmt.Errorf("name cannot be empty")

	// ErrMissingDomain returned when attempting to create a Namer with an empty domain
	ErrMissingDomain = fmt.Errorf("domain cannot be empty")
)

// A KeyError is returned when attmpting to create a Namer with an invalid key.  Keys must be
// hex-encoded strings with length 128.
type KeyError struct{}

func (k KeyError) Error() string {
	return fmt.Sprintf(
		"Session store secret should be a 128-character hex-encoded string.  "+
			"Here's a freshly generated one: %q",
		hex.EncodeToString(securecookie.GenerateRandomKey(64)))
}

func (s *namedStore) init() error {
	if s.name == "" {
		return ErrMissingName
	}
	if s.domain == "" {
		return ErrMissingDomain
	}
	key, err := hex.DecodeString(s.secret)
	if err != nil || len(key) != 64 {
		return KeyError{}
	}
	cookieStore := sessions.NewCookieStore(key)
	cookieStore.Options.HttpOnly = true
	cookieStore.Options.Secure = true
	cookieStore.Options.Domain = s.domain
	cookieStore.Options.MaxAge = int(sessionAge / time.Second)
	s.CookieStore = cookieStore
	return nil
}

// New returns a new Namer for the given name, secret key and domain.  secret should be a hex-encoded
// string with length 128.
func New(name, secret, domain string) (Namer, error) {
	sc := &namedStore{name: name, domain: domain, secret: secret}
	err := sc.init()
	return sc, err
}
<|endoftext|>


<commit_before>package slices

import (
	"fmt"
	"reflect"
	"testing"
)

// https://golang.org/cmd/go/#hdr-Description_of_testing_flags
// go test -v -bench string_test.go -benchtime 2s

var ()

func TestIsEmpty(t *testing.T) {

	if err := expect(IsEmpty([]string{}), true); err != nil {
		t.Error(err)
	}

	if err := expect(IsEmpty([]string{""}), true); err != nil {
		t.Error(err)
	}

	if err := expect(IsEmpty([]string{"a", "b", "c"}), false); err != nil {
		t.Error(err)
	}
}

func TestConcat(t *testing.T) {

	if err := expect(Concat([]string{}), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Concat([]string{""}), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Concat([]string{"a", "b", "c"}), "abc"); err != nil {
		t.Error(err)
	}
}

func TestJoin(t *testing.T) {

	if err := expect(Join([]string{}, "-"), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Join([]string{""}, "-"), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Join([]string{"a", "b", "c"}, "-"), "a-b-c"); err != nil {
		t.Error(err)
	}
}

func TestCut(t *testing.T) {

	if err := expect(Cut([]string{}, 0, -1), []string{}); err != nil {
		t.Error(err)
	}

	if err := expectNot(Cut([]string{}, 0, -1), []string{""}); err != nil {
		t.Error(err)
	}

	if err := expectNot(Cut([]string{""}, 0, -1), []string{""}); err != nil {
		t.Error(err)
	}
}

// func BenchmarkStringsReplaceAll(b *testing.B) {
// 	for i := 0; i < b.N; i++ {
// 		strings.Replace(TestFileContent, ToFind, ToReplace, -1)
// 	}
// }

func expect(args ...interface{}) error {
	switch len(args) {
	case 0, 1:
		return fmt.Errorf("Not enough arguments to expect. Args passed: %v", args)
	default:
		return _expect(true, args...)
	}
}

func expectNot(args ...interface{}) error {
	switch len(args) {
	case 0, 1:
		return fmt.Errorf("Not enough arguments to expect. Args passed: %v", args)
	default:
		return _expect(false, args...)
	}
}

func _expect(boolWanted bool, args ...interface{}) error {

	result := args[0]
	expected := args[1]

	if boolReceived := _areEqual(result, expected); boolReceived != boolWanted {
		return _errExpected(boolWanted, args...)
	}
	return nil
}

func _areEqual(result interface{}, expected interface{}) bool {
	return reflect.DeepEqual(result, expected)
}

func _errExpected(boolWanted bool, args ...interface{}) error {

	condition := "to equal"
	if boolWanted == false {
		condition = "not to equal"
	}

	result := args[0]
	expected := args[1]

	switch len(args) {
	case 2:
		return fmt.Errorf("Expected '%v' %v '%v'", result, condition, expected)
	default:
		return fmt.Errorf("%v: Expected '%v' %v '%v'", args[2], result, condition, expected)
	}
}
<commit_msg>private func public<commit_after>package slices

import (
	"fmt"
	"reflect"
	"testing"
)

// https://golang.org/cmd/go/#hdr-Description_of_testing_flags
// go test -v -bench string_test.go -benchtime 2s

var ()

func TestIsEmpty(t *testing.T) {

	if err := expect(IsEmpty([]string{}), true); err != nil {
		t.Error(err)
	}

	if err := expect(IsEmpty([]string{""}), true); err != nil {
		t.Error(err)
	}

	if err := expect(IsEmpty([]string{"a", "b", "c"}), false); err != nil {
		t.Error(err)
	}
}

func TestConcat(t *testing.T) {

	if err := expect(Concat([]string{}), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Concat([]string{""}), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Concat([]string{"a", "b", "c"}), "abc"); err != nil {
		t.Error(err)
	}
}

func TestJoin(t *testing.T) {

	if err := expect(Join([]string{}, "-"), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Join([]string{""}, "-"), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Join([]string{"a", "b", "c"}, "-"), "a-b-c"); err != nil {
		t.Error(err)
	}
}

func TestCut(t *testing.T) {

	if err := expect(Cut([]string{}, 0, -1), []string{}); err != nil {
		t.Error(err)
	}

	if err := expectNot(Cut([]string{}, 0, -1), []string{""}); err != nil {
		t.Error(err)
	}
}

// func BenchmarkStringsReplaceAll(b *testing.B) {
// 	for i := 0; i < b.N; i++ {
// 		strings.Replace(TestFileContent, ToFind, ToReplace, -1)
// 	}
// }

func expect(args ...interface{}) error {
	switch len(args) {
	case 0, 1:
		return fmt.Errorf("Not enough arguments to expect. Args passed: %v", args)
	default:
		return _expect(true, args...)
	}
}

func expectNot(args ...interface{}) error {
	switch len(args) {
	case 0, 1:
		return fmt.Errorf("Not enough arguments to expect. Args passed: %v", args)
	default:
		return _expect(false, args...)
	}
}

func _expect(boolWanted bool, args ...interface{}) error {

	result := args[0]
	expected := args[1]

	if boolReceived := _areEqual(result, expected); boolReceived != boolWanted {
		return errExpected(boolWanted, args...)
	}
	return nil
}

func _areEqual(result interface{}, expected interface{}) bool {
	return reflect.DeepEqual(result, expected)
}

func errExpected(boolWanted bool, args ...interface{}) error {

	condition := "to equal"
	if boolWanted == false {
		condition = "not to equal"
	}

	result := args[0]
	expected := args[1]

	switch len(args) {
	case 2:
		return fmt.Errorf("Expected '%v' %v '%v'", result, condition, expected)
	default:
		return fmt.Errorf("%v: Expected '%v' %v '%v'", args[2], result, condition, expected)
	}
}
<|endoftext|>