<commit_before>package stream

import (
	"errors"
	"fmt"

	"github.com/skriptble/nine/element"
)

// ElementMux is a stream element multiplexer. It matches elements based on the
// namespace and tag and calls the handler that matches.
//
// TODO: Should there be fuzzy matching? e.g. be able to match a namespace
// handler if there is not handler for both the namespace and tag?
type ElementMux struct {
	m   []elementEntry
	err error
}

type elementEntry struct {
	space, tag string
	h          ElementHandler
}

// NewElementMux returns an initialized ElementMux.
func NewElementMux() ElementMux {
	return ElementMux{}
}

// Handle registers the ElementHandler for the given namespace and tag.
//
// This method is meant to be chained. If an error occurs all following calls
// to Handle are skipped. The error can be retrieved by calling Err().
//
// 		em := NewElementMux().
//				Handle(...).
//				Handle(...).
//				Handle(...)
//		if em.Err() != nil {
//			// handle error
//			panic(em.Err())
//		}
//
// TODO: Determine if a single handler should be able to handle an entire
// namespace.
func (em ElementMux) Handle(space, tag string, h ElementHandler) ElementMux {
	if em.err != nil {
		return em
	}
	if space == "" || tag == "" {
		em.err = errors.New("space and tag cannot be empty")
		return em
	}
	if h == nil {
		em.err = errors.New("ElementHandler cannot be nil")
		return em
	}
	for _, entry := range em.m {
		if entry.space == space && entry.tag == tag {
			em.err = fmt.Errorf("stream: multiple registrations for %s:%s", space, tag)
			return em
		}
	}
	entry := elementEntry{space: space, tag: tag, h: h}
	em.m = append(em.m, entry)
	return em
}

// Err returns an error set on the ElementMux. This method is usually called
// after a call to a chain of Handle().
func (em ElementMux) Err() error {
	return em.err
}

// HandleElement dispatches the element to the handler who can handle the space
// and tag combination.
func (em ElementMux) HandleElement(el element.Element, p Properties) ([]element.Element, Properties) {
	h := em.Handler(el.Space, el.Tag)
	return h.HandleElement(el, p)
}

// Handler returns the ElementHandler for the given space and tag pair. Handler
// will always return a non-nil ElementHandler.
func (em ElementMux) Handler(space, tag string) ElementHandler {
	for _, entry := range em.m {
		if space == entry.space && tag == entry.tag {
			return entry.h
		}
	}
	Trace.Printf("No handlers for %s:%s", space, tag)
	return UnsupportedStanza{}
}

// UnsupportedStanza is an ElementHandler implementation with returns an
// unsupported-stanza-type error for all Elements it handles. This is mainly
// used in the Element multiplexer implementation where it is returned if there
// is no matching handler for a given Element.
type UnsupportedStanza struct{}

// HandleElement returns a stream error of unsupported-stanza-type and sets the
// status bit on the stream to closed.
func (us UnsupportedStanza) HandleElement(el element.Element, p Properties) ([]element.Element, Properties) {
	p.Status = Closed
	return []element.Element{element.StreamError.UnsupportedStanzaType}, p
}

// Blackhole is an ElementHandler implementation which does nothing with the
// handled element and returns no elements. This is mainly used as a
// placeholder for message and presence stanzas in nine since the handling of
// those types of stanzas is beyond the scope of RFC6120.
type Blackhole struct{}

// HandleElement does nothing and returns the Properties unchanged.
func (bh Blackhole) HandleElement(_ element.Element, p Properties) ([]element.Element, Properties) {
	return []element.Element{}, p
}

// ElementHandler is implemented by types that can process elements. If the
// handler modifies the properties it should return those properties. It should
// return any elements that should be written to the stream the element came
// from.
type ElementHandler interface {
	HandleElement(element.Element, Properties) ([]element.Element, Properties)
}
<commit_msg>Updating error messages for ElementMux.<commit_after>package stream

import (
	"errors"
	"fmt"

	"github.com/skriptble/nine/element"
)

// ErrEmptySpaceTag is the error set on an ElementMux when Handle is called
// with an empty namespace or empty tag.
var ErrEmptySpaceTag = errors.New("space and tag cannot be empty")

// ErrNilElementHandler is the error set on an ElementMux when Handle is called
// with nil as the parameter for ElementHandler.
var ErrNilElementHandler = errors.New("ElementHandler cannot be nil")

// ElementMux is a stream element multiplexer. It matches elements based on the
// namespace and tag and calls the handler that matches.
//
// TODO: Should there be fuzzy matching? e.g. be able to match a namespace
// handler if there is not handler for both the namespace and tag?
type ElementMux struct {
	m   []elementEntry
	err error
}

type elementEntry struct {
	space, tag string
	h          ElementHandler
}

// NewElementMux returns an initialized ElementMux.
func NewElementMux() ElementMux {
	return ElementMux{}
}

// Handle registers the ElementHandler for the given namespace and tag.
//
// This method is meant to be chained. If an error occurs all following calls
// to Handle are skipped. The error can be retrieved by calling Err().
//
// 		em := NewElementMux().
//				Handle(...).
//				Handle(...).
//				Handle(...)
//		if em.Err() != nil {
//			// handle error
//			panic(em.Err())
//		}
//
// TODO: Determine if a single handler should be able to handle an entire
// namespace.
func (em ElementMux) Handle(space, tag string, h ElementHandler) ElementMux {
	if em.err != nil {
		return em
	}
	if space == "" || tag == "" {
		em.err = ErrEmptySpaceTag
		return em
	}
	if h == nil {
		em.err = ErrNilElementHandler
		return em
	}
	for _, entry := range em.m {
		if entry.space == space && entry.tag == tag {
			em.err = fmt.Errorf("stream: multiple registrations for <%s:%s>", space, tag)
			return em
		}
	}
	entry := elementEntry{space: space, tag: tag, h: h}
	em.m = append(em.m, entry)
	return em
}

// Err returns an error set on the ElementMux. This method is usually called
// after a call to a chain of Handle().
func (em ElementMux) Err() error {
	return em.err
}

// HandleElement dispatches the element to the handler who can handle the space
// and tag combination.
func (em ElementMux) HandleElement(el element.Element, p Properties) ([]element.Element, Properties) {
	h := em.Handler(el.Space, el.Tag)
	return h.HandleElement(el, p)
}

// Handler returns the ElementHandler for the given space and tag pair. Handler
// will always return a non-nil ElementHandler.
func (em ElementMux) Handler(space, tag string) ElementHandler {
	for _, entry := range em.m {
		if space == entry.space && tag == entry.tag {
			return entry.h
		}
	}
	Trace.Printf("No handlers for %s:%s", space, tag)
	return UnsupportedStanza{}
}

// UnsupportedStanza is an ElementHandler implementation with returns an
// unsupported-stanza-type error for all Elements it handles. This is mainly
// used in the Element multiplexer implementation where it is returned if there
// is no matching handler for a given Element.
type UnsupportedStanza struct{}

// HandleElement returns a stream error of unsupported-stanza-type and sets the
// status bit on the stream to closed.
func (us UnsupportedStanza) HandleElement(el element.Element, p Properties) ([]element.Element, Properties) {
	p.Status = Closed
	return []element.Element{element.StreamError.UnsupportedStanzaType}, p
}

// Blackhole is an ElementHandler implementation which does nothing with the
// handled element and returns no elements. This is mainly used as a
// placeholder for message and presence stanzas in nine since the handling of
// those types of stanzas is beyond the scope of RFC6120.
type Blackhole struct{}

// HandleElement does nothing and returns the Properties unchanged.
func (bh Blackhole) HandleElement(_ element.Element, p Properties) ([]element.Element, Properties) {
	return []element.Element{}, p
}

// ElementHandler is implemented by types that can process elements. If the
// handler modifies the properties it should return those properties. It should
// return any elements that should be written to the stream the element came
// from.
type ElementHandler interface {
	HandleElement(element.Element, Properties) ([]element.Element, Properties)
}
<|endoftext|>


<commit_before>package collision

import (
	"log"
	"strconv"

	"bitbucket.org/oakmoundstudio/oak/event"
	"github.com/Sythe2o0/rtreego"
)

// ID Types constant
const (
	NONE = iota
	CID
	PID
)

// Spaces are a rectangle
// with a couple of ways of identifying
// the underlying object.
type Space struct {
	Location *rtreego.Rect
	// A label can store type information.
	// Recommended to use with an enum.
	Label int
	// A CID can be used to get the exact
	// entity which this rectangle belongs to.
	CID event.CID
	// Type represents which ID space the above ID
	// corresponds to.
	Type int
}

// Bounds satisfies the rtreego.Spatial interface.
func (s *Space) Bounds() *rtreego.Rect {
	return s.Location
}

func (s *Space) GetX() float64 {
	return s.Location.PointCoord(0)
}

func (s *Space) GetY() float64 {
	return s.Location.PointCoord(1)
}

func (s *Space) GetW() float64 {
	return s.Location.LengthsCoord(0)
}

func (s *Space) GetH() float64 {
	return s.Location.LengthsCoord(1)
}

func (s *Space) GetCenter() (float64, float64) {
	return s.GetX() + s.GetW()/2, s.GetY() + s.GetH()/2
}

func (s *Space) GetPos() (float64, float64) {
	return s.Location.PointCoord(1), s.Location.PointCoord(0)
}

func (s *Space) Above(other *Space) float64 {
	return other.GetY() - s.GetY()
}

func (s *Space) Below(other *Space) float64 {
	return s.GetY() - other.GetY()
}

func (s *Space) LeftOf(other *Space) float64 {
	return other.GetX() - s.GetX()
}

func (s *Space) RightOf(other *Space) float64 {
	return s.GetX() - other.GetX()
}

func (s *Space) Overlap(other *Space) (xOver, yOver float64) {
	if s.GetX() > other.GetX() {
		x2 := other.GetX() + other.GetW()
		if s.GetX() < x2 {
			xOver = s.GetX() - x2
		}
	} else {
		x2 := s.GetX() + s.GetW()
		if other.GetX() < x2 {
			xOver = x2 - other.GetX()
		}
	}
	if s.GetY() > other.GetY() {
		y2 := other.GetY() + other.GetH()
		if s.GetY() < y2 {
			yOver = s.GetY() - y2
		}
	} else {
		y2 := s.GetY() + s.GetW()
		if other.GetY() < y2 {
			yOver = y2 - other.GetY()
		}
	}
	return
}

func (s *Space) SetDim(w, h float64) {
	s.Update(s.GetX(), s.GetY(), w, h)
}

func (s *Space) Update(x, y, w, h float64) {
	loc := NewRect(x, y, w, h)
	rt.Delete(s)
	s.Location = loc
	rt.Insert(s)
}

func (s *Space) SubtractRect(x2, y2, w2, h2 float64) []*Space {
	x1 := s.GetX()
	y1 := s.GetY()
	w1 := s.GetW()
	h1 := s.GetH()

	// Left, Top, Right, Bottom
	// X, Y, W, H
	rects := [4][4]float64{}

	rects[0][0] = x1
	rects[0][1] = y1
	rects[0][2] = x2
	rects[0][3] = h1

	rects[1][0] = x1
	rects[1][1] = y1
	rects[1][2] = w1
	rects[1][3] = y2

	rects[2][0] = x1 + x2 + w2
	rects[2][1] = y1
	rects[2][2] = w1 - (x2 + w2)
	rects[2][3] = h1

	rects[3][0] = x1
	rects[3][1] = y1 + y2 + h2
	rects[3][2] = w1
	rects[3][3] = h1 - (y2 + h2)

	spaces := make([]*Space, 0)

	for _, r := range rects {
		if r[2] > 0 && r[3] > 0 {
			spaces = append(spaces, NewFullSpace(r[0], r[1], r[2], r[3], s.Label, s.CID))
		}
	}

	return spaces
}

func (s *Space) String() string {
	return strconv.FormatFloat(s.GetX(), 'f', 2, 32) + "," +
		strconv.FormatFloat(s.GetY(), 'f', 2, 32) + "," +
		strconv.FormatFloat(s.GetW(), 'f', 2, 32) + "," +
		strconv.FormatFloat(s.GetH(), 'f', 2, 32)
}

func NewUnassignedSpace(x, y, w, h float64) *Space {
	rect := NewRect(x, y, w, h)
	return &Space{
		Location: rect,
		Type:     NONE,
	}
}

func NewSpace(x, y, w, h float64, cID event.CID) *Space {
	rect := NewRect(x, y, w, h)
	return &Space{
		rect,
		-1,
		cID,
		CID,
	}
}

func NewLabeledSpace(x, y, w, h float64, l int) *Space {
	rect := NewRect(x, y, w, h)
	return &Space{
		Location: rect,
		Label:    l,
		Type:     NONE,
	}
}

func NewFullSpace(x, y, w, h float64, l int, cID event.CID) *Space {
	rect := NewRect(x, y, w, h)
	return &Space{
		rect,
		l,
		cID,
		CID,
	}
}

// NewRect is a wrapper around rtreego.NewRect,
// casting the given x,y to an rtreego.Point.
// Used to not expose rtreego.Point to the user.
func NewRect(x, y, w, h float64) *rtreego.Rect {
	rect, err := rtreego.NewRect(rtreego.Point{x, y}, [3]float64{w, h, 1})
	if err != nil {
		log.Fatal(err)
	}
	return &rect
}
<commit_msg> Added a contains method to spaces<commit_after>package collision

import (
	"log"
	"strconv"

	"bitbucket.org/oakmoundstudio/oak/event"
	"github.com/Sythe2o0/rtreego"
)

// ID Types constant
const (
	NONE = iota
	CID
	PID
)

// Spaces are a rectangle
// with a couple of ways of identifying
// the underlying object.
type Space struct {
	Location *rtreego.Rect
	// A label can store type information.
	// Recommended to use with an enum.
	Label int
	// A CID can be used to get the exact
	// entity which this rectangle belongs to.
	CID event.CID
	// Type represents which ID space the above ID
	// corresponds to.
	Type int
}

// Bounds satisfies the rtreego.Spatial interface.
func (s *Space) Bounds() *rtreego.Rect {
	return s.Location
}

func (s *Space) GetX() float64 {
	return s.Location.PointCoord(0)
}

func (s *Space) GetY() float64 {
	return s.Location.PointCoord(1)
}

func (s *Space) GetW() float64 {
	return s.Location.LengthsCoord(0)
}

func (s *Space) GetH() float64 {
	return s.Location.LengthsCoord(1)
}

func (s *Space) GetCenter() (float64, float64) {
	return s.GetX() + s.GetW()/2, s.GetY() + s.GetH()/2
}

func (s *Space) GetPos() (float64, float64) {
	return s.Location.PointCoord(1), s.Location.PointCoord(0)
}

func (s *Space) Above(other *Space) float64 {
	return other.GetY() - s.GetY()
}

func (s *Space) Below(other *Space) float64 {
	return s.GetY() - other.GetY()
}

func (s *Space) Contains(other *Space) bool {
	//You contain another space if it is fully inside your space
	//If you are the same size and location as the space you are checking then you both contain eachother
	if s.GetX() > other.GetX() || s.GetX()+s.GetW() < other.GetX()+other.GetW() ||
		s.GetY() > other.GetY() || s.GetY()+s.GetH() < other.GetY()+other.GetH() {
		return false
	}
	return true
}

func (s *Space) LeftOf(other *Space) float64 {
	return other.GetX() - s.GetX()
}

func (s *Space) RightOf(other *Space) float64 {
	return s.GetX() - other.GetX()
}

func (s *Space) Overlap(other *Space) (xOver, yOver float64) {
	if s.GetX() > other.GetX() {
		x2 := other.GetX() + other.GetW()
		if s.GetX() < x2 {
			xOver = s.GetX() - x2
		}
	} else {
		x2 := s.GetX() + s.GetW()
		if other.GetX() < x2 {
			xOver = x2 - other.GetX()
		}
	}
	if s.GetY() > other.GetY() {
		y2 := other.GetY() + other.GetH()
		if s.GetY() < y2 {
			yOver = s.GetY() - y2
		}
	} else {
		y2 := s.GetY() + s.GetW()
		if other.GetY() < y2 {
			yOver = y2 - other.GetY()
		}
	}
	return
}

func (s *Space) SetDim(w, h float64) {
	s.Update(s.GetX(), s.GetY(), w, h)
}

func (s *Space) Update(x, y, w, h float64) {
	loc := NewRect(x, y, w, h)
	rt.Delete(s)
	s.Location = loc
	rt.Insert(s)
}

func (s *Space) SubtractRect(x2, y2, w2, h2 float64) []*Space {
	x1 := s.GetX()
	y1 := s.GetY()
	w1 := s.GetW()
	h1 := s.GetH()

	// Left, Top, Right, Bottom
	// X, Y, W, H
	rects := [4][4]float64{}

	rects[0][0] = x1
	rects[0][1] = y1
	rects[0][2] = x2
	rects[0][3] = h1

	rects[1][0] = x1
	rects[1][1] = y1
	rects[1][2] = w1
	rects[1][3] = y2

	rects[2][0] = x1 + x2 + w2
	rects[2][1] = y1
	rects[2][2] = w1 - (x2 + w2)
	rects[2][3] = h1

	rects[3][0] = x1
	rects[3][1] = y1 + y2 + h2
	rects[3][2] = w1
	rects[3][3] = h1 - (y2 + h2)

	spaces := make([]*Space, 0)

	for _, r := range rects {
		if r[2] > 0 && r[3] > 0 {
			spaces = append(spaces, NewFullSpace(r[0], r[1], r[2], r[3], s.Label, s.CID))
		}
	}

	return spaces
}

func (s *Space) String() string {
	return strconv.FormatFloat(s.GetX(), 'f', 2, 32) + "," +
		strconv.FormatFloat(s.GetY(), 'f', 2, 32) + "," +
		strconv.FormatFloat(s.GetW(), 'f', 2, 32) + "," +
		strconv.FormatFloat(s.GetH(), 'f', 2, 32)
}

func NewUnassignedSpace(x, y, w, h float64) *Space {
	rect := NewRect(x, y, w, h)
	return &Space{
		Location: rect,
		Type:     NONE,
	}
}

func NewSpace(x, y, w, h float64, cID event.CID) *Space {
	rect := NewRect(x, y, w, h)
	return &Space{
		rect,
		-1,
		cID,
		CID,
	}
}

func NewLabeledSpace(x, y, w, h float64, l int) *Space {
	rect := NewRect(x, y, w, h)
	return &Space{
		Location: rect,
		Label:    l,
		Type:     NONE,
	}
}

func NewFullSpace(x, y, w, h float64, l int, cID event.CID) *Space {
	rect := NewRect(x, y, w, h)
	return &Space{
		rect,
		l,
		cID,
		CID,
	}
}

// NewRect is a wrapper around rtreego.NewRect,
// casting the given x,y to an rtreego.Point.
// Used to not expose rtreego.Point to the user.
func NewRect(x, y, w, h float64) *rtreego.Rect {
	rect, err := rtreego.NewRect(rtreego.Point{x, y}, [3]float64{w, h, 1})
	if err != nil {
		log.Fatal(err)
	}
	return &rect
}
<|endoftext|>


<commit_before>package immortal

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"sync"
	"syscall"
	"testing"
	"time"
)

func TestHelperProcessSupervise(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	select {
	case <-c:
		os.Exit(1)
	case <-time.After(10 * time.Second):
		os.Exit(0)
	}
}

func TestHelperProcessSupervise2(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	os.Exit(0)
}

func TestSupervise(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	log.SetFlags(0)
	base := filepath.Base(os.Args[0]) // "exec.test"
	dir := filepath.Dir(os.Args[0])   // "/tmp/go-buildNNNN/os/exec/_test"
	if dir == "." {
		t.Skip("skipping; running test at root somehow")
	}
	parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec"
	dirBase := filepath.Base(dir)  // "_test"
	if dirBase == "." {
		t.Skipf("skipping; unexpected shallow dir of %q", dir)
	}
	tmpfile, err := ioutil.TempFile("", "TestPidFile")
	if err != nil {
		t.Error(err)
	}
	defer os.Remove(tmpfile.Name()) // clean up
	cfg := &Config{
		Env:     map[string]string{"GO_WANT_HELPER_PROCESS": "1"},
		command: []string{filepath.Join(dirBase, base), "-test.run=TestHelperProcessSupervise", "--"},
		Cwd:     parentDir,
		ctrl:    true,
		Pid: Pid{
			Parent: filepath.Join(parentDir, "parent.pid"),
			Child:  filepath.Join(parentDir, "child.pid"),
			Follow: tmpfile.Name(),
		},
	}
	// to remove lock
	os.RemoveAll(filepath.Join(parentDir, "supervise"))
	d, err := New(cfg)
	if err != nil {
		t.Fatal(err)
	}
	time.Sleep(time.Second)
	fctrl, err := OpenFifo(filepath.Join(parentDir, "supervise/control"))
	if err != nil {
		t.Fatal(err)
	}
	go Supervise(d)
	defer func() {
		fmt.Fprintln(fctrl, "kill")
		fmt.Fprintln(fctrl, "exit")
	}()

	sup := &Sup{}

	time.Sleep(time.Second)

	// check pids
	parent_pid, err := sup.ReadPidFile(filepath.Join(parentDir, "parent.pid"))
	if err != nil {
		t.Error(err)
	}
	expect(t, os.Getpid(), parent_pid)
	child_pid, err := sup.ReadPidFile(filepath.Join(parentDir, "child.pid"))
	if err != nil {
		t.Error(err)
	}
	expect(t, true, child_pid > 0)

	fmt.Fprintln(fctrl, "t")
	time.Sleep(time.Second)
	newchild_pid, err := sup.ReadPidFile(filepath.Join(parentDir, "child.pid"))
	if err != nil {
		t.Error(err)
	}
	if child_pid == newchild_pid {
		t.Error("Expecting new child pid")
	}

	// test info
	syscall.Kill(parent_pid, syscall.SIGQUIT)
	time.Sleep(time.Second)

	// fake watch pid with other process
	cmd := exec.Command("sleep", "1")
	cmd.Start()
	go func() {
		cmd.Wait()
	}()
	watchPid := cmd.Process.Pid
	err = ioutil.WriteFile(tmpfile.Name(), []byte(strconv.Itoa(watchPid)), 0644)
	if err != nil {
		t.Error(err)
	}

	// reset
	fmt.Fprintln(fctrl, "t")

	select {
	case <-time.After(5 * time.Second):
		t.Error("time out")
	default:
		for sup.IsRunning(watchPid) {
			// wait mock watchpid to finish
			time.Sleep(2100 * time.Millisecond)
		}
		newchild_pid_after, err := sup.ReadPidFile(filepath.Join(parentDir, "child.pid"))
		if err != nil {
			t.Error(err)
		}
		if newchild_pid == newchild_pid_after {
			t.Error("Expecting different pids")
		}
	}
}

func TestSuperviseWait(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	log.SetFlags(0)
	base := filepath.Base(os.Args[0]) // "exec.test"
	dir := filepath.Dir(os.Args[0])   // "/tmp/go-buildNNNN/os/exec/_test"
	if dir == "." {
		t.Skip("skipping; running test at root somehow")
	}
	parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec"
	dirBase := filepath.Base(dir)  // "_test"
	if dirBase == "." {
		t.Skipf("skipping; unexpected shallow dir of %q", dir)
	}
	cfg := &Config{
		Env:     map[string]string{"GO_WANT_HELPER_PROCESS": "1"},
		command: []string{filepath.Join(dirBase, base), "-test.run=TestHelperProcessSupervise2", "--"},
		Cwd:     parentDir,
		ctrl:    true,
		Pid: Pid{
			Parent: filepath.Join(parentDir, "parent.pid"),
			Child:  filepath.Join(parentDir, "child.pid"),
		},
	}
	// to remove lock
	os.RemoveAll(filepath.Join(parentDir, "supervise"))
	d, err := New(cfg)
	if err != nil {
		t.Fatal(err)
	}
	time.Sleep(time.Second)
	fctrl, err := OpenFifo(filepath.Join(parentDir, "supervise/control"))
	if err != nil {
		t.Fatal(err)
	}
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		Supervise(d)
	}()
	time.Sleep(2 * time.Second)
	fmt.Fprintln(fctrl, "exit")
	wg.Wait()
	expect(t, true, d.count >= 2)
}
<commit_msg>	modified:   supervise_test.go<commit_after>package immortal

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"sync"
	"syscall"
	"testing"
	"time"
)

func TestHelperProcessSupervise(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	select {
	case <-c:
		os.Exit(1)
	case <-time.After(10 * time.Second):
		os.Exit(0)
	}
}

func TestHelperProcessSupervise2(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	os.Exit(0)
}

func TestSupervise(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	log.SetFlags(0)
	base := filepath.Base(os.Args[0]) // "exec.test"
	dir := filepath.Dir(os.Args[0])   // "/tmp/go-buildNNNN/os/exec/_test"
	if dir == "." {
		t.Skip("skipping; running test at root somehow")
	}
	parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec"
	dirBase := filepath.Base(dir)  // "_test"
	if dirBase == "." {
		t.Skipf("skipping; unexpected shallow dir of %q", dir)
	}
	tmpfile, err := ioutil.TempFile("", "TestPidFile")
	if err != nil {
		t.Error(err)
	}
	defer os.Remove(tmpfile.Name()) // clean up
	cfg := &Config{
		Env:     map[string]string{"GO_WANT_HELPER_PROCESS": "1"},
		command: []string{filepath.Join(dirBase, base), "-test.run=TestHelperProcessSupervise", "--"},
		Cwd:     parentDir,
		ctrl:    true,
		Pid: Pid{
			Parent: filepath.Join(parentDir, "parent.pid"),
			Child:  filepath.Join(parentDir, "child.pid"),
			Follow: tmpfile.Name(),
		},
	}
	// to remove lock
	os.RemoveAll(filepath.Join(parentDir, "supervise"))
	d, err := New(cfg)
	if err != nil {
		t.Fatal(err)
	}
	time.Sleep(time.Second)
	fctrl, err := OpenFifo(filepath.Join(parentDir, "supervise/control"))
	if err != nil {
		t.Fatal(err)
	}
	go Supervise(d)
	defer func() {
		fmt.Fprintln(fctrl, "kill")
		fmt.Fprintln(fctrl, "exit")
	}()

	sup := &Sup{}

	time.Sleep(time.Second)

	// check pids
	parent_pid, err := sup.ReadPidFile(filepath.Join(parentDir, "parent.pid"))
	if err != nil {
		t.Error(err)
	}
	expect(t, os.Getpid(), parent_pid)
	child_pid, err := sup.ReadPidFile(filepath.Join(parentDir, "child.pid"))
	if err != nil {
		t.Error(err)
	}
	expect(t, true, child_pid > 0)

	fmt.Fprintln(fctrl, "t")
	time.Sleep(time.Second)
	newchild_pid, err := sup.ReadPidFile(filepath.Join(parentDir, "child.pid"))
	if err != nil {
		t.Error(err)
	}
	if child_pid == newchild_pid {
		t.Error("Expecting new child pid")
	}

	// test info
	syscall.Kill(parent_pid, syscall.SIGQUIT)
	time.Sleep(time.Second)

	// fake watch pid with other process
	cmd := exec.Command("sleep", "1")
	cmd.Start()
	go func() {
		cmd.Wait()
	}()
	watchPid := cmd.Process.Pid
	err = ioutil.WriteFile(tmpfile.Name(), []byte(strconv.Itoa(watchPid)), 0644)
	if err != nil {
		t.Error(err)
	}

	// reset
	fmt.Fprintln(fctrl, "t")
	time.Sleep(time.Second)

	select {
	case <-time.After(5 * time.Second):
		t.Error("time out")
	default:
		for sup.IsRunning(watchPid) {
			// wait mock watchpid to finish
			time.Sleep(500 * time.Millisecond)
		}
		newchild_pid_after, err := sup.ReadPidFile(filepath.Join(parentDir, "child.pid"))
		if err != nil {
			t.Error(err)
		}
		if newchild_pid == newchild_pid_after {
			t.Error("Expecting different pids")
		}
	}
}

func TestSuperviseWait(t *testing.T) {
	log.SetOutput(ioutil.Discard)
	log.SetFlags(0)
	base := filepath.Base(os.Args[0]) // "exec.test"
	dir := filepath.Dir(os.Args[0])   // "/tmp/go-buildNNNN/os/exec/_test"
	if dir == "." {
		t.Skip("skipping; running test at root somehow")
	}
	parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec"
	dirBase := filepath.Base(dir)  // "_test"
	if dirBase == "." {
		t.Skipf("skipping; unexpected shallow dir of %q", dir)
	}
	cfg := &Config{
		Env:     map[string]string{"GO_WANT_HELPER_PROCESS": "1"},
		command: []string{filepath.Join(dirBase, base), "-test.run=TestHelperProcessSupervise2", "--"},
		Cwd:     parentDir,
		ctrl:    true,
		Pid: Pid{
			Parent: filepath.Join(parentDir, "parent.pid"),
			Child:  filepath.Join(parentDir, "child.pid"),
		},
	}
	// to remove lock
	os.RemoveAll(filepath.Join(parentDir, "supervise"))
	d, err := New(cfg)
	if err != nil {
		t.Fatal(err)
	}
	time.Sleep(time.Second)
	fctrl, err := OpenFifo(filepath.Join(parentDir, "supervise/control"))
	if err != nil {
		t.Fatal(err)
	}
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		Supervise(d)
	}()
	time.Sleep(2 * time.Second)
	fmt.Fprintln(fctrl, "exit")
	wg.Wait()
	expect(t, true, d.count >= 2)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package widget

import (
	"fmt"
	"github.com/richardwilkes/ui/color"
	"github.com/richardwilkes/ui/draw"
	"github.com/richardwilkes/ui/event"
	"github.com/richardwilkes/ui/geom"
	"github.com/richardwilkes/ui/layout"
	"github.com/richardwilkes/ui/theme"
	"math"
	"strings"
	"time"
)

// TextField provides a single-line text input control.
type TextField struct {
	Block
	text            string
	watermark       string
	Theme           *theme.TextField // The theme the text field will use to draw itself.
	selectionStart  int
	selectionEnd    int
	selectionAnchor int
	forceShowUntil  time.Time
	scrollOffset    float32
	align           draw.Alignment
	showCursor      bool
	pending         bool
	extendByWord    bool
}

// NewTextField creates a new, empty, text field.
func NewTextField() *TextField {
	field := &TextField{}
	field.Theme = theme.StdTextField
	field.SetBackground(color.TextBackground)
	field.SetBorder(field.Theme.Border)
	field.SetFocusable(true)
	field.SetSizer(field)
	handlers := field.EventHandlers()
	handlers.Add(event.PaintType, field.paint)
	handlers.Add(event.FocusGainedType, field.focusGained)
	handlers.Add(event.FocusLostType, field.focusLost)
	handlers.Add(event.MouseDownType, field.mouseDown)
	return field
}

// Sizes implements Sizer
func (field *TextField) Sizes(hint geom.Size) (min, pref, max geom.Size) {
	if hint.Width != layout.NoHint {
		if hint.Width < field.Theme.MinimumTextWidth {
			hint.Width = field.Theme.MinimumTextWidth
		}
	}
	if hint.Height != layout.NoHint {
		if hint.Height < 1 {
			hint.Height = 1
		}
	}
	var text string
	if field.text == "" {
		text = "M"
	} else {
		text = field.text
	}
	size := field.Theme.Font.Size(text)
	// Add the descent height to allow for a more balanced vertical look
	size.Height += field.Theme.Font.Descent()
	size.GrowToInteger()
	size.ConstrainForHint(hint)
	if border := field.Border(); border != nil {
		size.AddInsets(border.Insets())
	}
	return size, size, layout.DefaultMaxSize(size)
}

func (field *TextField) paint(evt event.Event) {
	bounds := field.LocalInsetBounds()
	e := evt.(*event.Paint)
	gc := e.GC()
	gc.Save()
	defer gc.Restore()
	if !field.Enabled() && field.Theme.DisabledBackgroundColor.Alpha() > 0 {
		gc.SetFillColor(field.Theme.DisabledBackgroundColor)
		gc.FillRect(e.DirtyRect())
	}
	gc.ClipRect(bounds)
	gc.SetFont(field.Theme.Font)
	descent := field.Theme.Font.Descent()
	textTop := bounds.Y + (bounds.Height-(field.Theme.Font.Height()-descent))/2
	if field.HasSelectionRange() {
		left := bounds.X + field.scrollOffset
		if field.selectionStart > 0 {
			gc.SetFillColor(color.Text)
			pre := field.text[:field.selectionStart]
			gc.DrawString(left, textTop, pre)
			left += field.Theme.Font.Width(pre)
		}
		mid := field.text[field.selectionStart:field.selectionEnd]
		right := bounds.X + field.Theme.Font.Width(field.text[:field.selectionEnd]) + field.scrollOffset
		selRect := geom.Rect{Point: geom.Point{X: left, Y: textTop - descent}, Size: geom.Size{Width: right - left, Height: field.Theme.Font.Height() + descent}}
		if field.Focused() {
			gc.SetFillColor(color.SelectedTextBackground)
			gc.FillRect(selRect)
		} else {
			gc.SetStrokeColor(color.SelectedTextBackground)
			gc.SetStrokeWidth(2)
			selRect.InsetUniform(0.5)
			gc.StrokeRect(selRect)
		}
		gc.SetFillColor(color.SelectedText)
		gc.DrawString(left, textTop, mid)
		if field.selectionStart < len(field.text) {
			gc.SetFillColor(color.Text)
			gc.DrawString(right, textTop, field.text[field.selectionEnd:])
		}
	} else if field.text == "" {
		if field.watermark != "" {
			gc.SetFillColor(color.Gray)
			gc.DrawString(bounds.X, textTop, field.watermark)
		}
	} else {
		gc.SetFillColor(color.Text)
		gc.DrawString(bounds.X, textTop, field.text)
	}
	if !field.HasSelectionRange() && field.Focused() {
		if field.showCursor {
			var cursorColor color.Color
			if field.Background().Luminance() > 0.6 {
				cursorColor = color.Black
			} else {
				cursorColor = color.White
			}
			x := bounds.X + field.Theme.Font.Width(field.text[:field.selectionEnd]) + field.scrollOffset
			gc.SetStrokeColor(cursorColor)
			gc.StrokeLine(x, textTop-descent, x, textTop+field.Theme.Font.Height()+descent-1)
		}
		field.scheduleBlink()
	}
}

func (field *TextField) scheduleBlink() {
	if !field.pending && field.Focused() {
		field.pending = true
		event.InvokeAfter(field.blink, field.Theme.BlinkRate)
	}
}

func (field *TextField) blink() {
	field.pending = false
	if time.Now().After(field.forceShowUntil) {
		field.showCursor = !field.showCursor
		field.Repaint()
	}
	field.scheduleBlink()
}

func (field *TextField) focusGained(evt event.Event) {
	field.SetBorder(field.Theme.FocusBorder)
	field.showCursor = true
	field.Repaint()
}

func (field *TextField) focusLost(evt event.Event) {
	field.SetBorder(field.Theme.Border)
	field.Repaint()
}

func (field *TextField) mouseDown(evt event.Event) {
	field.Window().SetFocus(field)
	e := evt.(*event.MouseDown)
	if e.Button() == event.LeftButton {
		field.extendByWord = false
		switch e.Clicks() {
		case 2:
		case 3:
			field.SelectAll()
		default:
		}
	} else if e.Button() == event.RightButton {
		fmt.Println("right click")
	}
}

// Text returns the content of the field.
func (field *TextField) Text() string {
	return field.text
}

// SetText sets the content of the field. Returns true if a modification was made.
func (field *TextField) SetText(text string) bool {
	text = sanitize(text)
	if field.text != text {
		field.text = text
		field.SetSelectionToEnd()
		field.Repaint()
		event.Dispatch(event.NewModified(field))
		return true
	}
	return false
}

func sanitize(text string) string {
	return strings.NewReplacer("\n", "", "\r", "").Replace(text)
}

// Watermark returns the current watermark, if any.
func (field *TextField) Watermark() string {
	return field.watermark
}

// SetWatermark sets the watermark. The watermark is used to give the user a hint about what the
// field is for when it is empty.
func (field *TextField) SetWatermark(text string) {
	field.watermark = text
	field.Repaint()
}

// SelectedText returns the currently selected text.
func (field *TextField) SelectedText() string {
	return field.text[field.selectionStart:field.selectionEnd]
}

// HasSelectionRange returns true is a selection range is currently present.
func (field *TextField) HasSelectionRange() bool {
	return field.selectionStart < field.selectionEnd
}

// SelectionCount returns the number of characters currently selected.
func (field *TextField) SelectionCount() int {
	return field.selectionEnd - field.selectionStart
}

// Selection returns the current start and end selection indexes.
func (field *TextField) Selection() (start, end int) {
	return field.selectionStart, field.selectionEnd
}

// SelectAll selects all of the text in the field.
func (field *TextField) SelectAll() {
	field.SetSelection(0, len(field.text))
}

// SetSelectionToStart moves the cursor to the beginning of the text and removes any range that may
// have been present.
func (field *TextField) SetSelectionToStart() {
	field.SetSelection(0, 0)
}

// SetSelectionToEnd moves the cursor to the end of the text and removes any range that may have
// been present.
func (field *TextField) SetSelectionToEnd() {
	field.SetSelection(math.MaxInt64, math.MaxInt64)
}

// SetSelectionTo moves the cursor to the specified index and removes any range that may have been
// present.
func (field *TextField) SetSelectionTo(pos int) {
	field.SetSelection(pos, pos)
}

// SetSelection sets the start and end range of the selection. Values beyond either end will be
// constrained to the appropriate end. Likewise, an end value less than the start value will be
// treated as if the start and end values were the same.
func (field *TextField) SetSelection(start, end int) {
	field.setSelection(start, end, start)
}

func (field *TextField) setSelection(start, end, anchor int) {
	length := len(field.text)
	if start < 0 {
		start = 0
	} else if start > length {
		start = length
	}
	if end < start {
		end = start
	} else if end > length {
		end = length
	}
	if anchor < start {
		anchor = start
	} else if anchor > end {
		anchor = end
	}
	if field.selectionStart != start || field.selectionEnd != end || field.selectionAnchor != anchor {
		field.selectionStart = start
		field.selectionEnd = end
		field.selectionAnchor = anchor
		field.forceShowUntil = time.Now().Add(field.Theme.BlinkRate)
		field.showCursor = true
		field.Repaint()
		field.ScrollIntoView()
		field.autoScroll()
	}
}

// ScrollIntoView scrolls the insertion cursor into view.
func (field *TextField) ScrollIntoView() {
	// RAW: Implement
}

func (field *TextField) autoScroll() {
	// RAW: Implement
}
<commit_msg>Add some mouse interaction<commit_after>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package widget

import (
	"fmt"
	"github.com/richardwilkes/ui/color"
	"github.com/richardwilkes/ui/draw"
	"github.com/richardwilkes/ui/event"
	"github.com/richardwilkes/ui/geom"
	"github.com/richardwilkes/ui/layout"
	"github.com/richardwilkes/ui/theme"
	"math"
	"strings"
	"time"
	"unicode"
)

// TextField provides a single-line text input control.
type TextField struct {
	Block
	runes           []rune
	watermark       string
	Theme           *theme.TextField // The theme the text field will use to draw itself.
	selectionStart  int
	selectionEnd    int
	selectionAnchor int
	forceShowUntil  time.Time
	scrollOffset    float32
	align           draw.Alignment
	showCursor      bool
	pending         bool
	extendByWord    bool
}

// NewTextField creates a new, empty, text field.
func NewTextField() *TextField {
	field := &TextField{}
	field.Theme = theme.StdTextField
	field.SetBackground(color.TextBackground)
	field.SetBorder(field.Theme.Border)
	field.SetFocusable(true)
	field.SetSizer(field)
	handlers := field.EventHandlers()
	handlers.Add(event.PaintType, field.paint)
	handlers.Add(event.FocusGainedType, field.focusGained)
	handlers.Add(event.FocusLostType, field.focusLost)
	handlers.Add(event.MouseDownType, field.mouseDown)
	handlers.Add(event.MouseDraggedType, field.mouseDragged)
	return field
}

// Sizes implements Sizer
func (field *TextField) Sizes(hint geom.Size) (min, pref, max geom.Size) {
	if hint.Width != layout.NoHint {
		if hint.Width < field.Theme.MinimumTextWidth {
			hint.Width = field.Theme.MinimumTextWidth
		}
	}
	if hint.Height != layout.NoHint {
		if hint.Height < 1 {
			hint.Height = 1
		}
	}
	var text string
	if len(field.runes) != 0 {
		text = string(field.runes)
	} else {
		text = "M"
	}
	size := field.Theme.Font.Size(text)
	// Add the descent height to allow for a more balanced vertical look
	size.Height += field.Theme.Font.Descent()
	size.GrowToInteger()
	size.ConstrainForHint(hint)
	if border := field.Border(); border != nil {
		size.AddInsets(border.Insets())
	}
	return size, size, layout.DefaultMaxSize(size)
}

func (field *TextField) paint(evt event.Event) {
	bounds := field.LocalInsetBounds()
	e := evt.(*event.Paint)
	gc := e.GC()
	gc.Save()
	defer gc.Restore()
	if !field.Enabled() && field.Theme.DisabledBackgroundColor.Alpha() > 0 {
		gc.SetFillColor(field.Theme.DisabledBackgroundColor)
		gc.FillRect(e.DirtyRect())
	}
	gc.ClipRect(bounds)
	gc.SetFont(field.Theme.Font)
	descent := field.Theme.Font.Descent()
	textTop := bounds.Y + (bounds.Height-(field.Theme.Font.Height()-descent))/2
	if field.HasSelectionRange() {
		left := bounds.X + field.scrollOffset
		if field.selectionStart > 0 {
			gc.SetFillColor(color.Text)
			pre := string(field.runes[:field.selectionStart])
			gc.DrawString(left, textTop, pre)
			left += field.Theme.Font.Width(pre)
		}
		mid := string(field.runes[field.selectionStart:field.selectionEnd])
		right := bounds.X + field.Theme.Font.Width(string(field.runes[:field.selectionEnd])) + field.scrollOffset
		selRect := geom.Rect{Point: geom.Point{X: left, Y: textTop - descent}, Size: geom.Size{Width: right - left, Height: field.Theme.Font.Height() + descent}}
		if field.Focused() {
			gc.SetFillColor(color.SelectedTextBackground)
			gc.FillRect(selRect)
		} else {
			gc.SetStrokeColor(color.SelectedTextBackground)
			gc.SetStrokeWidth(2)
			selRect.InsetUniform(0.5)
			gc.StrokeRect(selRect)
		}
		gc.SetFillColor(color.SelectedText)
		gc.DrawString(left, textTop, mid)
		if field.selectionStart < len(field.runes) {
			gc.SetFillColor(color.Text)
			gc.DrawString(right, textTop, string(field.runes[field.selectionEnd:]))
		}
	} else if len(field.runes) == 0 {
		if field.watermark != "" {
			gc.SetFillColor(color.Gray)
			gc.DrawString(bounds.X, textTop, field.watermark)
		}
	} else {
		gc.SetFillColor(color.Text)
		gc.DrawString(bounds.X, textTop, string(field.runes))
	}
	if !field.HasSelectionRange() && field.Focused() {
		if field.showCursor {
			var cursorColor color.Color
			if field.Background().Luminance() > 0.6 {
				cursorColor = color.Black
			} else {
				cursorColor = color.White
			}
			x := bounds.X + field.Theme.Font.Width(string(field.runes[:field.selectionEnd])) + field.scrollOffset
			gc.SetStrokeColor(cursorColor)
			gc.StrokeLine(x, textTop-descent, x, textTop+field.Theme.Font.Height()+descent-1)
		}
		field.scheduleBlink()
	}
}

func (field *TextField) scheduleBlink() {
	if !field.pending && field.Focused() {
		field.pending = true
		event.InvokeAfter(field.blink, field.Theme.BlinkRate)
	}
}

func (field *TextField) blink() {
	field.pending = false
	if time.Now().After(field.forceShowUntil) {
		field.showCursor = !field.showCursor
		field.Repaint()
	}
	field.scheduleBlink()
}

func (field *TextField) focusGained(evt event.Event) {
	field.SetBorder(field.Theme.FocusBorder)
	field.showCursor = true
	field.Repaint()
}

func (field *TextField) focusLost(evt event.Event) {
	field.SetBorder(field.Theme.Border)
	field.Repaint()
}

func (field *TextField) mouseDown(evt event.Event) {
	field.Window().SetFocus(field)
	e := evt.(*event.MouseDown)
	if e.Button() == event.LeftButton {
		field.extendByWord = false
		switch e.Clicks() {
		case 2:
			start, end := field.findWordAt(field.ToSelectionIndex(field.FromWindow(e.Where()).X))
			field.SetSelection(start, end)
			field.extendByWord = true
		case 3:
			field.SelectAll()
		default:
			oldAnchor := field.selectionAnchor
			field.selectionAnchor = field.ToSelectionIndex(field.FromWindow(e.Where()).X)
			var start, end int
			if e.Modifiers().ShiftDown() {
				if oldAnchor > field.selectionAnchor {
					start = field.selectionAnchor
					end = oldAnchor
				} else {
					start = oldAnchor
					end = field.selectionAnchor
				}
			} else {
				start = field.selectionAnchor
				end = field.selectionAnchor
			}
			field.setSelection(start, end, field.selectionAnchor)
		}
	} else if e.Button() == event.RightButton {
		fmt.Println("right click")
	}
}

func (field *TextField) mouseDragged(evt event.Event) {
	oldAnchor := field.selectionAnchor
	pos := field.ToSelectionIndex(field.FromWindow(evt.(*event.MouseDragged).Where()).X)
	var start, end int
	if field.extendByWord {
		s1, e1 := field.findWordAt(oldAnchor)
		var dir int
		if pos > s1 {
			dir = -1
		} else {
			dir = 1
		}
		for {
			start, end = field.findWordAt(pos)
			if start != end {
				if start > s1 {
					start = s1
				}
				if end < e1 {
					end = e1
				}
				break
			}
			pos += dir
			if dir > 0 && pos >= s1 || dir < 0 && pos <= e1 {
				start = s1
				end = e1
				break
			}
		}
	} else {
		if pos > oldAnchor {
			start = oldAnchor
			end = pos
		} else {
			start = pos
			end = oldAnchor
		}
	}
	field.setSelection(start, end, oldAnchor)
}

// Text returns the content of the field.
func (field *TextField) Text() string {
	return string(field.runes)
}

// SetText sets the content of the field. Returns true if a modification was made.
func (field *TextField) SetText(text string) bool {
	text = sanitize(text)
	if string(field.runes) != text {
		field.runes = ([]rune)(text)
		field.SetSelectionToEnd()
		field.Repaint()
		event.Dispatch(event.NewModified(field))
		return true
	}
	return false
}

func sanitize(text string) string {
	return strings.NewReplacer("\n", "", "\r", "").Replace(text)
}

// Watermark returns the current watermark, if any.
func (field *TextField) Watermark() string {
	return field.watermark
}

// SetWatermark sets the watermark. The watermark is used to give the user a hint about what the
// field is for when it is empty.
func (field *TextField) SetWatermark(text string) {
	field.watermark = text
	field.Repaint()
}

// SelectedText returns the currently selected text.
func (field *TextField) SelectedText() string {
	return string(field.runes[field.selectionStart:field.selectionEnd])
}

// HasSelectionRange returns true is a selection range is currently present.
func (field *TextField) HasSelectionRange() bool {
	return field.selectionStart < field.selectionEnd
}

// SelectionCount returns the number of characters currently selected.
func (field *TextField) SelectionCount() int {
	return field.selectionEnd - field.selectionStart
}

// Selection returns the current start and end selection indexes.
func (field *TextField) Selection() (start, end int) {
	return field.selectionStart, field.selectionEnd
}

// SelectAll selects all of the text in the field.
func (field *TextField) SelectAll() {
	field.SetSelection(0, len(field.runes))
}

// SetSelectionToStart moves the cursor to the beginning of the text and removes any range that may
// have been present.
func (field *TextField) SetSelectionToStart() {
	field.SetSelection(0, 0)
}

// SetSelectionToEnd moves the cursor to the end of the text and removes any range that may have
// been present.
func (field *TextField) SetSelectionToEnd() {
	field.SetSelection(math.MaxInt64, math.MaxInt64)
}

// SetSelectionTo moves the cursor to the specified index and removes any range that may have been
// present.
func (field *TextField) SetSelectionTo(pos int) {
	field.SetSelection(pos, pos)
}

// SetSelection sets the start and end range of the selection. Values beyond either end will be
// constrained to the appropriate end. Likewise, an end value less than the start value will be
// treated as if the start and end values were the same.
func (field *TextField) SetSelection(start, end int) {
	field.setSelection(start, end, start)
}

func (field *TextField) setSelection(start, end, anchor int) {
	length := len(field.runes)
	if start < 0 {
		start = 0
	} else if start > length {
		start = length
	}
	if end < start {
		end = start
	} else if end > length {
		end = length
	}
	if anchor < start {
		anchor = start
	} else if anchor > end {
		anchor = end
	}
	if field.selectionStart != start || field.selectionEnd != end || field.selectionAnchor != anchor {
		field.selectionStart = start
		field.selectionEnd = end
		field.selectionAnchor = anchor
		field.forceShowUntil = time.Now().Add(field.Theme.BlinkRate)
		field.showCursor = true
		field.Repaint()
		field.ScrollIntoView()
		field.autoScroll()
	}
}

// ScrollIntoView scrolls the insertion cursor into view.
func (field *TextField) ScrollIntoView() {
	// RAW: Implement
}

func (field *TextField) autoScroll() {
	// RAW: Implement
}

func (field *TextField) ToSelectionIndex(x float32) int {
	bounds := field.LocalInsetBounds()
	return field.Theme.Font.IndexForPosition(x+field.scrollOffset-bounds.X, string(field.runes))
}

func (field *TextField) findWordAt(pos int) (start, end int) {
	length := len(field.runes)
	if pos < 0 {
		pos = 0
	} else if pos >= length {
		pos = length - 1
	}
	start = pos
	end = pos
	if length > 0 && !unicode.IsSpace(field.runes[start]) {
		for start > 0 && !unicode.IsSpace(field.runes[start-1]) {
			start--
		}
		for end < length && !unicode.IsSpace(field.runes[end]) {
			end++
		}
	}
	return start, end
}
<|endoftext|>


<commit_before>package caddy

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/tarent/loginsrv/login"
	"github.com/tarent/loginsrv/model"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

//Tests a page while being logged in as a user (doesn't test that the {user} replacer changes)
func Test_ServeHTTP_200(t *testing.T) {
	//Set the ServeHTTP *http.Request
	r, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}

	//Set the ServeHTTP http.ResponseWriter
	w := httptest.NewRecorder()

	//Set the CaddyHandler config
	configh := login.DefaultConfig()
	configh.Backends = login.Options{"simple": {"bob": "secret"}}
	loginh, err := login.NewHandler(configh)
	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	//Set the CaddyHandler that will use ServeHTTP
	h := &CaddyHandler{
		next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			return http.StatusOK, nil // not t.Fatalf, or we will not see what other methods yield
		}),
		config:       login.DefaultConfig(),
		loginHandler: loginh,
	}

	//Set user token
	userInfo := model.UserInfo{Sub: "bob", Expiry: time.Now().Add(time.Second).Unix()}
	token := jwt.NewWithClaims(jwt.SigningMethodHS512, userInfo)
	validToken, err := token.SignedString([]byte(h.config.JwtSecret))
	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	//Set cookie for user token on the ServeHTTP http.ResponseWriter
	cookie := http.Cookie{Name: "jwt_token", Value: validToken, HttpOnly: true}
	http.SetCookie(w, &cookie)

	//Add the cookie to the request
	r.AddCookie(&cookie)

	//Test that cookie is a valid token
	_, valid := loginh.GetToken(r)
	if !valid {
		t.Errorf("loginHandler cookie is not valid")
	}

	status, err := h.ServeHTTP(w, r)

	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	if status != 200 {
		t.Errorf("Expected returned status code to be %d, got %d", 0, status)
	}
}

//Tests the login page without being logged as a user (doesn't test that the {user} replacer stays as-is)
func Test_ServeHTTP_login(t *testing.T) {
	//Set the ServeHTTP *http.Request
	r, err := http.NewRequest("GET", "/login", nil)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}

	//Set the ServeHTTP http.ResponseWriter
	w := httptest.NewRecorder()

	//Set the CaddyHandler config
	configh := login.DefaultConfig()
	configh.Backends = login.Options{"simple": {"bob": "secret"}}
	loginh, err := login.NewHandler(configh)
	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	//Set the CaddyHandler that will use ServeHTTP
	h := &CaddyHandler{
		next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			return http.StatusOK, nil // not t.Fatalf, or we will not see what other methods yield
		}),
		config:       login.DefaultConfig(),
		loginHandler: loginh,
	}

	status, err := h.ServeHTTP(w, r)

	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	if status != 0 {
		t.Errorf("Expected returned status code to be %d, got %d", 0, status)
	}
}
<commit_msg>Check for replacement of the user in caddy handler.<commit_after>package caddy

import (
	"context"
	"github.com/dgrijalva/jwt-go"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/tarent/loginsrv/login"
	"github.com/tarent/loginsrv/model"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

//Tests a page while being logged in as a user (doesn't test that the {user} replacer changes)
func Test_ServeHTTP_200(t *testing.T) {
	//Set the ServeHTTP *http.Request
	r, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}

	// Associate a replacer with the request:
	r = r.WithContext(context.WithValue(context.Background(), httpserver.ReplacerCtxKey, httpserver.NewReplacer(r, nil, "-")))

	//Set the ServeHTTP http.ResponseWriter
	w := httptest.NewRecorder()

	//Set the CaddyHandler config
	configh := login.DefaultConfig()
	configh.Backends = login.Options{"simple": {"bob": "secret"}}
	loginh, err := login.NewHandler(configh)
	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	//Set the CaddyHandler that will use ServeHTTP
	h := &CaddyHandler{
		next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			return http.StatusOK, nil // not t.Fatalf, or we will not see what other methods yield
		}),
		config:       login.DefaultConfig(),
		loginHandler: loginh,
	}

	//Set user token
	userInfo := model.UserInfo{Sub: "bob", Expiry: time.Now().Add(time.Second).Unix()}
	token := jwt.NewWithClaims(jwt.SigningMethodHS512, userInfo)
	validToken, err := token.SignedString([]byte(h.config.JwtSecret))
	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	//Set cookie for user token on the ServeHTTP http.ResponseWriter
	cookie := http.Cookie{Name: "jwt_token", Value: validToken, HttpOnly: true}
	http.SetCookie(w, &cookie)

	//Add the cookie to the request
	r.AddCookie(&cookie)

	//Test that cookie is a valid token
	_, valid := loginh.GetToken(r)
	if !valid {
		t.Errorf("loginHandler cookie is not valid")
	}

	status, err := h.ServeHTTP(w, r)

	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	if status != 200 {
		t.Errorf("Expected returned status code to be %d, got %d", 0, status)
	}

	// Check that the replacer now is able to substitute the user variable in log lines
	replacer, replacerOk := r.Context().Value(httpserver.ReplacerCtxKey).(httpserver.Replacer)
	if !replacerOk {
		t.Errorf("no replacer associated with request")

	} else {
		replacement := replacer.Replace("{user}")
		if replacement != "bob" {
			t.Errorf(`wrong replacement: expected "bob", but got %q`, replacement)
		}
	}
}

//Tests the login page without being logged as a user (doesn't test that the {user} replacer stays as-is)
func Test_ServeHTTP_login(t *testing.T) {
	//Set the ServeHTTP *http.Request
	r, err := http.NewRequest("GET", "/login", nil)
	if err != nil {
		t.Fatalf("Unable to create request: %v", err)
	}

	//Set the ServeHTTP http.ResponseWriter
	w := httptest.NewRecorder()

	//Set the CaddyHandler config
	configh := login.DefaultConfig()
	configh.Backends = login.Options{"simple": {"bob": "secret"}}
	loginh, err := login.NewHandler(configh)
	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	//Set the CaddyHandler that will use ServeHTTP
	h := &CaddyHandler{
		next: httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			return http.StatusOK, nil // not t.Fatalf, or we will not see what other methods yield
		}),
		config:       login.DefaultConfig(),
		loginHandler: loginh,
	}

	status, err := h.ServeHTTP(w, r)

	if err != nil {
		t.Errorf("Expected nil error, got: %v", err)
	}

	if status != 0 {
		t.Errorf("Expected returned status code to be %d, got %d", 0, status)
	}
}
<|endoftext|>


<commit_before>// beehive's IRC module.
package ircbee

import (
	_ "fmt"
	irc "github.com/fluffle/goirc/client"
	"github.com/muesli/beehive/app"
	"github.com/muesli/beehive/modules"
	"log"
	"strings"
	"time"
)

type IrcBee struct {
	// channel signaling irc connection status
	ConnectedState chan bool

	// setup IRC client:
	client *irc.Conn

	irchost     string
	ircnick     string
	ircpassword string
	ircssl      bool
	ircchannel  string

	channels    []string
}

// Interface impl

func (sys *IrcBee) Name() string {
	return "ircbee"
}

func (sys *IrcBee) Events() []modules.Event {
	events := []modules.Event{}
	return events
}

func (sys *IrcBee) Actions() []modules.Action {
	actions := []modules.Action{}
	return actions
}

func (sys *IrcBee) Handle(cm modules.Event) bool {
	log.Println("Handling event:", cm.Name)

/*	if len(cm.To) == 0 {
		sys.client.Privmsg(sys.ircchannel, cm.Msg)
		return true
	} else {
		for _, recv := range cm.To {
			if recv == "*" {
				// special: send to all joined channels
				for _, to := range sys.channels {
					sys.client.Privmsg(to, cm.Msg)
				}
			} else {
				// needs stripping hostname when sending to user!host
				if strings.Index(recv, "!") > 0 {
					recv = recv[0:strings.Index(recv, "!")]
				}

				sys.client.Privmsg(recv, cm.Msg)
			}
		}

		return true
	}*/

	return false
}

// ircbee specific impl

func (sys *IrcBee) Rejoin() {
	for _, channel := range sys.channels {
		sys.client.Join(channel)
	}
}

func (sys *IrcBee) Join(channel string) {
	channel = strings.TrimSpace(channel)
	sys.client.Join(channel)

	sys.channels = append(sys.channels, channel)
}

func (sys *IrcBee) Part(channel string) {
	channel = strings.TrimSpace(channel)
	sys.client.Part(channel)

	for k, v := range sys.channels {
		if v == channel {
			sys.channels = append(sys.channels[:k], sys.channels[k+1:]...)
			return
		}
	}
}

func (sys *IrcBee) Run(channelIn chan modules.Event) {
	if len(sys.irchost) == 0 {
		return
	}

	// channel signaling irc connection status
	sys.ConnectedState = make(chan bool)

	// setup IRC client:
	sys.client = irc.SimpleClient(sys.ircnick, "beehive", "beehive")
	sys.client.SSL = sys.ircssl

	sys.client.AddHandler(irc.CONNECTED, func(conn *irc.Conn, line *irc.Line) {
		sys.ConnectedState <- true
	})
	sys.client.AddHandler(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) {
		sys.ConnectedState <- false
	})
	sys.client.AddHandler("PRIVMSG", func(conn *irc.Conn, line *irc.Line) {
		channel := line.Args[0]
		if channel == sys.client.Me.Nick {
//			log.Println("PM from " + line.Src)
			channel = line.Src // replies go via PM too.
		} else {
//			log.Println("Message in channel " + channel + " from " + line.Src)
		}

		ev := modules.Event{
			Name: channel,
			Options: []modules.Placeholder{
				modules.Placeholder{
					Name: "channel",
					Type: "string",
					Value: channel,
				},
				modules.Placeholder{
					Name: "user",
					Type: "string",
					Value: line.Src,
				},
				modules.Placeholder{
					Name: "params",
					Type: "string",
					Value: line.Args[1],
				},
			},
		}
		channelIn <- ev
	})

	// loop on IRC dis/connected events
	go func() {
		for {
			log.Println("Connecting to IRC:", sys.irchost)
			err := sys.client.Connect(sys.irchost, sys.ircpassword)
			if err != nil {
				log.Println("Failed to connect to IRC:", sys.irchost)
				log.Println(err)
				continue
			}
			for {
				status := <-sys.ConnectedState
				if status {
					log.Println("Connected to IRC:", sys.irchost)

					if len(sys.channels) == 0 {
						// join default channel
						sys.Join(sys.ircchannel)
					} else {
						// we must have been disconnected, rejoin channels
						sys.Rejoin()
					}
				} else {
					log.Println("Disconnected from IRC:", sys.irchost)
					break
				}
			}
			time.Sleep(5 * time.Second)
		}
	}()
}

func init() {
	irc := IrcBee{}

	app.AddFlags([]app.CliFlag{
		app.CliFlag{&irc.irchost, "irchost", "", "Hostname of IRC server, eg: irc.example.org:6667"},
		app.CliFlag{&irc.ircnick, "ircnick", "beehive", "Nickname to use for IRC"},
		app.CliFlag{&irc.ircpassword, "ircpassword", "", "Password to use to connect to IRC server"},
		app.CliFlag{&irc.ircchannel, "ircchannel", "#beehivetest", "Which channel to join"},
		app.CliFlag{&irc.ircssl, "ircssl", false, "Use SSL for IRC connection"},
	})

	modules.RegisterModule(&irc)
}
<commit_msg>* Define events/actions for ircbee.<commit_after>// beehive's IRC module.
package ircbee

import (
	_ "fmt"
	irc "github.com/fluffle/goirc/client"
	"github.com/muesli/beehive/app"
	"github.com/muesli/beehive/modules"
	"log"
	"strings"
	"time"
)

type IrcBee struct {
	// channel signaling irc connection status
	ConnectedState chan bool

	// setup IRC client:
	client *irc.Conn

	irchost     string
	ircnick     string
	ircpassword string
	ircssl      bool
	ircchannel  string

	channels    []string
}

// Interface impl

func (sys *IrcBee) Name() string {
	return "ircbee"
}

func (sys *IrcBee) Events() []modules.Event {
	events := []modules.Event{
		modules.Event{
			Name: "ping",
			Options: []modules.Placeholder{
				modules.Placeholder{
					Name: "text",
					Type: "string",
				},
			},
		},
	}
	return events
}

func (sys *IrcBee) Actions() []modules.Action {
	actions := []modules.Action{
		modules.Action{
			Name: "send",
			Options: []modules.Placeholder{
				modules.Placeholder{
					Name: "channel",
					Type: "string",
				},
				modules.Placeholder{
					Name: "text",
					Type: "string",
				},
			},
		},
	}
	return actions
}

func (sys *IrcBee) Action(action modules.Action) bool {
	tos := []string{}
	text := ""

	switch action.Name {
	case "send":
		for _, opt := range action.Options {
			if opt.Name == "channel" {
				tos = append(tos, opt.Value)
			}
			if opt.Name == "text" {
				text = opt.Value
			}
		}
	default:
		// unknown action
		return false
	}

	for _, recv := range tos {
		if recv == "*" {
			// special: send to all joined channels
			for _, to := range sys.channels {
				sys.client.Privmsg(to, text)
			}
		} else {
			// needs stripping hostname when sending to user!host
			if strings.Index(recv, "!") > 0 {
				recv = recv[0:strings.Index(recv, "!")]
			}

			log.Println("recv:", recv)
			log.Println("text:", text)
			sys.client.Privmsg(recv, text)
		}
	}

	return true
}

// ircbee specific impl

func (sys *IrcBee) Rejoin() {
	for _, channel := range sys.channels {
		sys.client.Join(channel)
	}
}

func (sys *IrcBee) Join(channel string) {
	channel = strings.TrimSpace(channel)
	sys.client.Join(channel)

	sys.channels = append(sys.channels, channel)
}

func (sys *IrcBee) Part(channel string) {
	channel = strings.TrimSpace(channel)
	sys.client.Part(channel)

	for k, v := range sys.channels {
		if v == channel {
			sys.channels = append(sys.channels[:k], sys.channels[k+1:]...)
			return
		}
	}
}

func (sys *IrcBee) Run(channelIn chan modules.Event, channelOut chan modules.Action) {
	if len(sys.irchost) == 0 {
		return
	}

	// channel signaling irc connection status
	sys.ConnectedState = make(chan bool)

	// setup IRC client:
	sys.client = irc.SimpleClient(sys.ircnick, "beehive", "beehive")
	sys.client.SSL = sys.ircssl

	sys.client.AddHandler(irc.CONNECTED, func(conn *irc.Conn, line *irc.Line) {
		sys.ConnectedState <- true
	})
	sys.client.AddHandler(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) {
		sys.ConnectedState <- false
	})
	sys.client.AddHandler("PRIVMSG", func(conn *irc.Conn, line *irc.Line) {
		channel := line.Args[0]
		if channel == sys.client.Me.Nick {
//			log.Println("PM from " + line.Src)
			channel = line.Src // replies go via PM too.
		} else {
//			log.Println("Message in channel " + channel + " from " + line.Src)
		}

		action := modules.Action{
			Name: "send",
			Options: []modules.Placeholder{
				modules.Placeholder{
					Name: "channel",
					Type: "string",
					Value: channel,
				},
				modules.Placeholder{
					Name: "text",
					Type: "string",
					Value: line.Src + " said: " + line.Args[1],
				},
			},
		}
		sys.Action(action)

		ev := modules.Event{
			Name: channel,
			Options: []modules.Placeholder{
				modules.Placeholder{
					Name: "channel",
					Type: "string",
					Value: channel,
				},
				modules.Placeholder{
					Name: "user",
					Type: "string",
					Value: line.Src,
				},
				modules.Placeholder{
					Name: "params",
					Type: "string",
					Value: line.Args[1],
				},
			},
		}
		channelIn <- ev
	})

	// loop on IRC dis/connected events
	go func() {
		for {
			log.Println("Connecting to IRC:", sys.irchost)
			err := sys.client.Connect(sys.irchost, sys.ircpassword)
			if err != nil {
				log.Println("Failed to connect to IRC:", sys.irchost)
				log.Println(err)
				continue
			}
			for {
				status := <-sys.ConnectedState
				if status {
					log.Println("Connected to IRC:", sys.irchost)

					if len(sys.channels) == 0 {
						// join default channel
						sys.Join(sys.ircchannel)
					} else {
						// we must have been disconnected, rejoin channels
						sys.Rejoin()
					}
				} else {
					log.Println("Disconnected from IRC:", sys.irchost)
					break
				}
			}
			time.Sleep(5 * time.Second)
		}
	}()
}

func init() {
	irc := IrcBee{}

	app.AddFlags([]app.CliFlag{
		app.CliFlag{&irc.irchost, "irchost", "", "Hostname of IRC server, eg: irc.example.org:6667"},
		app.CliFlag{&irc.ircnick, "ircnick", "beehive", "Nickname to use for IRC"},
		app.CliFlag{&irc.ircpassword, "ircpassword", "", "Password to use to connect to IRC server"},
		app.CliFlag{&irc.ircchannel, "ircchannel", "#beehivetest", "Which channel to join"},
		app.CliFlag{&irc.ircssl, "ircssl", false, "Use SSL for IRC connection"},
	})

	modules.RegisterModule(&irc)
}
<|endoftext|>


<commit_before>package agent

import (
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/consul"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/consul/logger"
	"github.com/hashicorp/consul/testutil/retry"
	"github.com/hashicorp/consul/types"
	"github.com/hashicorp/consul/version"
	uuid "github.com/hashicorp/go-uuid"
)

func init() {
	rand.Seed(time.Now().UnixNano()) // seed random number generator
}

// TempDir defines the base dir for temporary directories.
var TempDir = os.TempDir()

// TestAgent encapsulates an Agent with a default configuration and
// startup procedure suitable for testing. It panics if there are errors
// during creation or startup instead of returning errors. It manages a
// temporary data directory which is removed after shutdown.
//
// todo(fs): do we need the temp data dir if we run in dev mode?
type TestAgent struct {
	// Name is an optional name of the agent.
	Name string

	// Config is the agent configuration. If Config is nil then
	// TestConfig() is used. If Config.DataDir is set then it is
	// the callers responsibility to clean up the data directory.
	// Otherwise, a temporary data directory is created and removed
	// when Shutdown() is called.
	Config *Config

	// LogOutput is the sink for the logs. If nil, logs are written
	// to os.Stderr.
	LogOutput io.Writer

	// LogWriter is used for streaming logs.
	LogWriter *logger.LogWriter

	// DataDir is the data directory which is used when Config.DataDir
	// is not set. It is created automatically and removed when
	// Shutdown() is called.
	DataDir string

	// Key is the optional encryption key for the LAN and WAN keyring.
	Key string

	// dns is a reference to the first started DNS endpoint.
	// It is valid after Start().
	dns *DNSServer

	// srv is a reference to the first started HTTP endpoint.
	// It is valid after Start().
	srv *HTTPServer

	// Agent is the embedded consul agent.
	// It is valid after Start().
	*Agent
}

// NewTestAgent returns a started agent with the given name and
// configuration. It panics if the agent could not be started. The
// caller should call Shutdown() to stop the agent and remove temporary
// directories.
func NewTestAgent(name string, c *Config) *TestAgent {
	a := &TestAgent{Name: name, Config: c}
	a.Start()
	return a
}

type panicFailer struct{}

func (f *panicFailer) Log(args ...interface{}) { fmt.Println(args...) }
func (f *panicFailer) FailNow()                { panic("failed") }

// Start starts a test agent. It panics if the agent could not be started.
func (a *TestAgent) Start() *TestAgent {
	if a.Agent != nil {
		panic("TestAgent already started")
	}
	if a.Config == nil {
		a.Config = TestConfig()
	}
	if a.Config.DataDir == "" {
		name := "agent"
		if a.Name != "" {
			name = a.Name + "-agent"
		}
		name = strings.Replace(name, "/", "_", -1)
		d, err := ioutil.TempDir(TempDir, name)
		if err != nil {
			panic(fmt.Sprintf("Error creating data dir %s: %s", filepath.Join(TempDir, name), err))
		}
		a.DataDir = d
		a.Config.DataDir = d
	}
	if a.Config.DNSRecursor != "" {
		a.Config.DNSRecursors = append(a.Config.DNSRecursors, a.Config.DNSRecursor)
	}
	if a.Key != "" {
		writeKey := func(key, filename string) {
			path := filepath.Join(a.Config.DataDir, filename)
			if err := initKeyring(path, key); err != nil {
				panic(fmt.Sprintf("Error creating keyring %s: %s", path, err))
			}
		}
		writeKey(a.Key, serfLANKeyring)
		writeKey(a.Key, serfWANKeyring)
	}

	agent, err := NewAgent(a.Config)
	if err != nil {
		panic(fmt.Sprintf("Error creating agent: %s", err))
	}
	a.Agent = agent
	a.Agent.LogOutput = a.LogOutput
	a.Agent.LogWriter = a.LogWriter
	tenTimes := &retry.Counter{Count: 10, Wait: 100 * time.Millisecond}
	retry.RunWith(tenTimes, &panicFailer{}, func(r *retry.R) {
		err := a.Agent.Start()
		if err == nil {
			return
		}

		// retry with different ports on port conflict
		if strings.Contains(err.Error(), "bind: address already in use") {
			pickRandomPorts(a.Config)
			r.Fatal("port conflict")
		}

		// do not retry on other failures
		panic(fmt.Sprintf("Error starting agent: %s", err))
	})

	var out structs.IndexedNodes
	retry.Run(&panicFailer{}, func(r *retry.R) {
		if len(a.httpServers) == 0 {
			r.Fatal("waiting for server")
		}
		if a.Config.Bootstrap && a.Config.Server {
			// Ensure we have a leader and a node registration.
			args := &structs.DCSpecificRequest{Datacenter: a.Config.Datacenter}
			if err := a.RPC("Catalog.ListNodes", args, &out); err != nil {
				r.Fatalf("Catalog.ListNodes failed: %v", err)
			}
			if !out.QueryMeta.KnownLeader {
				r.Fatalf("No leader")
			}
			if out.Index == 0 {
				r.Fatalf("Consul index is 0")
			}
		} else {
			req, _ := http.NewRequest("GET", "/v1/agent/self", nil)
			resp := httptest.NewRecorder()
			_, err := a.httpServers[0].AgentSelf(resp, req)
			if err != nil || resp.Code != 200 {
				r.Fatal("failed OK respose", err)
			}
		}
	})
	a.dns = a.dnsServers[0]
	a.srv = a.httpServers[0]
	return a
}

// Shutdown stops the agent and removes the data directory if it is
// managed by the test agent.
func (a *TestAgent) Shutdown() error {
	defer func() {
		if a.DataDir != "" {
			os.RemoveAll(a.DataDir)
		}
	}()
	return a.Agent.Shutdown()
}

func (a *TestAgent) HTTPAddr() string {
	if a.srv == nil {
		return ""
	}
	return a.srv.Addr
}

func (a *TestAgent) Client() *api.Client {
	conf := api.DefaultConfig()
	conf.Address = a.HTTPAddr()
	c, err := api.NewClient(conf)
	if err != nil {
		panic(fmt.Sprintf("Error creating consul API client: %s", err))
	}
	return c
}

func (a *TestAgent) consulConfig() *consul.Config {
	c, err := a.Agent.consulConfig()
	if err != nil {
		panic(err)
	}
	return c
}

// pickRandomPorts selects random ports from fixed size random blocks of
// ports. This does not eliminate the chance for port conflict but
// reduces it significanltly with little overhead. Furthermore, asking
// the kernel for a random port by binding to port 0 prolongs the test
// execution (in our case +20sec) while also not fully eliminating the
// chance of port conflicts for concurrently executed test binaries.
// Instead of relying on one set of ports to be sufficient we retry
// starting the agent with different ports on port conflict.
func pickRandomPorts(c *Config) {
	port := 1030 + int(rand.Int31n(6440))*10
	c.Ports.DNS = port + 1
	c.Ports.HTTP = port + 2
	// when we enable HTTPS then we need to fix finding the
	// "first" HTTP server since that might be HTTPS server
	// c.Ports.HTTPS = port + 3
	c.Ports.SerfLan = port + 4
	c.Ports.SerfWan = port + 5
	c.Ports.Server = port + 6
}

// BoolTrue and BoolFalse exist to create a *bool value.
var BoolTrue = true
var BoolFalse = false

// TestConfig returns a unique default configuration for testing an
// agent.
func TestConfig() *Config {
	nodeID, err := uuid.GenerateUUID()
	if err != nil {
		panic(err)
	}

	cfg := DefaultConfig()
	pickRandomPorts(cfg)

	cfg.Version = version.Version
	cfg.VersionPrerelease = "c.d"

	cfg.NodeID = types.NodeID(nodeID)
	cfg.NodeName = "Node " + nodeID
	cfg.BindAddr = "127.0.0.1"
	cfg.AdvertiseAddr = "127.0.0.1"
	cfg.Datacenter = "dc1"
	cfg.Bootstrap = true
	cfg.Server = true

	ccfg := consul.DefaultConfig()
	cfg.ConsulConfig = ccfg

	ccfg.SerfLANConfig.MemberlistConfig.SuspicionMult = 3
	ccfg.SerfLANConfig.MemberlistConfig.ProbeTimeout = 100 * time.Millisecond
	ccfg.SerfLANConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
	ccfg.SerfLANConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond

	ccfg.SerfWANConfig.MemberlistConfig.SuspicionMult = 3
	ccfg.SerfWANConfig.MemberlistConfig.ProbeTimeout = 100 * time.Millisecond
	ccfg.SerfWANConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
	ccfg.SerfWANConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond

	ccfg.RaftConfig.LeaderLeaseTimeout = 20 * time.Millisecond
	ccfg.RaftConfig.HeartbeatTimeout = 40 * time.Millisecond
	ccfg.RaftConfig.ElectionTimeout = 40 * time.Millisecond

	ccfg.CoordinateUpdatePeriod = 100 * time.Millisecond
	ccfg.ServerHealthInterval = 10 * time.Millisecond
	return cfg
}

// TestACLConfig returns a default configuration for testing an agent
// with ACLs.
func TestACLConfig() *Config {
	cfg := TestConfig()
	cfg.ACLDatacenter = cfg.Datacenter
	cfg.ACLDefaultPolicy = "deny"
	cfg.ACLMasterToken = "root"
	cfg.ACLAgentToken = "root"
	cfg.ACLAgentMasterToken = "towel"
	cfg.ACLEnforceVersion8 = &BoolTrue
	return cfg
}
<commit_msg>test: Shutdown half-started agent before retrying<commit_after>package agent

import (
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/consul"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/consul/logger"
	"github.com/hashicorp/consul/testutil/retry"
	"github.com/hashicorp/consul/types"
	"github.com/hashicorp/consul/version"
	uuid "github.com/hashicorp/go-uuid"
)

func init() {
	rand.Seed(time.Now().UnixNano()) // seed random number generator
}

// TempDir defines the base dir for temporary directories.
var TempDir = os.TempDir()

// TestAgent encapsulates an Agent with a default configuration and
// startup procedure suitable for testing. It panics if there are errors
// during creation or startup instead of returning errors. It manages a
// temporary data directory which is removed after shutdown.
//
// todo(fs): do we need the temp data dir if we run in dev mode?
type TestAgent struct {
	// Name is an optional name of the agent.
	Name string

	// Config is the agent configuration. If Config is nil then
	// TestConfig() is used. If Config.DataDir is set then it is
	// the callers responsibility to clean up the data directory.
	// Otherwise, a temporary data directory is created and removed
	// when Shutdown() is called.
	Config *Config

	// LogOutput is the sink for the logs. If nil, logs are written
	// to os.Stderr.
	LogOutput io.Writer

	// LogWriter is used for streaming logs.
	LogWriter *logger.LogWriter

	// DataDir is the data directory which is used when Config.DataDir
	// is not set. It is created automatically and removed when
	// Shutdown() is called.
	DataDir string

	// Key is the optional encryption key for the LAN and WAN keyring.
	Key string

	// dns is a reference to the first started DNS endpoint.
	// It is valid after Start().
	dns *DNSServer

	// srv is a reference to the first started HTTP endpoint.
	// It is valid after Start().
	srv *HTTPServer

	// Agent is the embedded consul agent.
	// It is valid after Start().
	*Agent
}

// NewTestAgent returns a started agent with the given name and
// configuration. It panics if the agent could not be started. The
// caller should call Shutdown() to stop the agent and remove temporary
// directories.
func NewTestAgent(name string, c *Config) *TestAgent {
	a := &TestAgent{Name: name, Config: c}
	a.Start()
	return a
}

type panicFailer struct{}

func (f *panicFailer) Log(args ...interface{}) { fmt.Println(args...) }
func (f *panicFailer) FailNow()                { panic("failed") }

// Start starts a test agent. It panics if the agent could not be started.
func (a *TestAgent) Start() *TestAgent {
	if a.Agent != nil {
		panic("TestAgent already started")
	}
	if a.Config == nil {
		a.Config = TestConfig()
	}
	if a.Config.DataDir == "" {
		name := "agent"
		if a.Name != "" {
			name = a.Name + "-agent"
		}
		name = strings.Replace(name, "/", "_", -1)
		d, err := ioutil.TempDir(TempDir, name)
		if err != nil {
			panic(fmt.Sprintf("Error creating data dir %s: %s", filepath.Join(TempDir, name), err))
		}
		a.DataDir = d
		a.Config.DataDir = d
	}
	if a.Config.DNSRecursor != "" {
		a.Config.DNSRecursors = append(a.Config.DNSRecursors, a.Config.DNSRecursor)
	}
	if a.Key != "" {
		writeKey := func(key, filename string) {
			path := filepath.Join(a.Config.DataDir, filename)
			if err := initKeyring(path, key); err != nil {
				panic(fmt.Sprintf("Error creating keyring %s: %s", path, err))
			}
		}
		writeKey(a.Key, serfLANKeyring)
		writeKey(a.Key, serfWANKeyring)
	}

	agent, err := NewAgent(a.Config)
	if err != nil {
		panic(fmt.Sprintf("Error creating agent: %s", err))
	}
	a.Agent = agent
	a.Agent.LogOutput = a.LogOutput
	a.Agent.LogWriter = a.LogWriter
	tenTimes := &retry.Counter{Count: 10, Wait: 100 * time.Millisecond}
	retry.RunWith(tenTimes, &panicFailer{}, func(r *retry.R) {
		err := a.Agent.Start()
		if err == nil {
			return
		}

		// retry with different ports on port conflict
		if strings.Contains(err.Error(), "bind: address already in use") {
			a.Agent.Shutdown()
			pickRandomPorts(a.Config)
			r.Fatal("port conflict")
		}

		// do not retry on other failures
		panic(fmt.Sprintf("Error starting agent: %s", err))
	})

	var out structs.IndexedNodes
	retry.Run(&panicFailer{}, func(r *retry.R) {
		if len(a.httpServers) == 0 {
			r.Fatal("waiting for server")
		}
		if a.Config.Bootstrap && a.Config.Server {
			// Ensure we have a leader and a node registration.
			args := &structs.DCSpecificRequest{Datacenter: a.Config.Datacenter}
			if err := a.RPC("Catalog.ListNodes", args, &out); err != nil {
				r.Fatalf("Catalog.ListNodes failed: %v", err)
			}
			if !out.QueryMeta.KnownLeader {
				r.Fatalf("No leader")
			}
			if out.Index == 0 {
				r.Fatalf("Consul index is 0")
			}
		} else {
			req, _ := http.NewRequest("GET", "/v1/agent/self", nil)
			resp := httptest.NewRecorder()
			_, err := a.httpServers[0].AgentSelf(resp, req)
			if err != nil || resp.Code != 200 {
				r.Fatal("failed OK respose", err)
			}
		}
	})
	a.dns = a.dnsServers[0]
	a.srv = a.httpServers[0]
	return a
}

// Shutdown stops the agent and removes the data directory if it is
// managed by the test agent.
func (a *TestAgent) Shutdown() error {
	defer func() {
		if a.DataDir != "" {
			os.RemoveAll(a.DataDir)
		}
	}()
	return a.Agent.Shutdown()
}

func (a *TestAgent) HTTPAddr() string {
	if a.srv == nil {
		return ""
	}
	return a.srv.Addr
}

func (a *TestAgent) Client() *api.Client {
	conf := api.DefaultConfig()
	conf.Address = a.HTTPAddr()
	c, err := api.NewClient(conf)
	if err != nil {
		panic(fmt.Sprintf("Error creating consul API client: %s", err))
	}
	return c
}

func (a *TestAgent) consulConfig() *consul.Config {
	c, err := a.Agent.consulConfig()
	if err != nil {
		panic(err)
	}
	return c
}

// pickRandomPorts selects random ports from fixed size random blocks of
// ports. This does not eliminate the chance for port conflict but
// reduces it significanltly with little overhead. Furthermore, asking
// the kernel for a random port by binding to port 0 prolongs the test
// execution (in our case +20sec) while also not fully eliminating the
// chance of port conflicts for concurrently executed test binaries.
// Instead of relying on one set of ports to be sufficient we retry
// starting the agent with different ports on port conflict.
func pickRandomPorts(c *Config) {
	port := 1030 + int(rand.Int31n(6440))*10
	c.Ports.DNS = port + 1
	c.Ports.HTTP = port + 2
	// when we enable HTTPS then we need to fix finding the
	// "first" HTTP server since that might be HTTPS server
	// c.Ports.HTTPS = port + 3
	c.Ports.SerfLan = port + 4
	c.Ports.SerfWan = port + 5
	c.Ports.Server = port + 6
}

// BoolTrue and BoolFalse exist to create a *bool value.
var BoolTrue = true
var BoolFalse = false

// TestConfig returns a unique default configuration for testing an
// agent.
func TestConfig() *Config {
	nodeID, err := uuid.GenerateUUID()
	if err != nil {
		panic(err)
	}

	cfg := DefaultConfig()
	pickRandomPorts(cfg)

	cfg.Version = version.Version
	cfg.VersionPrerelease = "c.d"

	cfg.NodeID = types.NodeID(nodeID)
	cfg.NodeName = "Node " + nodeID
	cfg.BindAddr = "127.0.0.1"
	cfg.AdvertiseAddr = "127.0.0.1"
	cfg.Datacenter = "dc1"
	cfg.Bootstrap = true
	cfg.Server = true

	ccfg := consul.DefaultConfig()
	cfg.ConsulConfig = ccfg

	ccfg.SerfLANConfig.MemberlistConfig.SuspicionMult = 3
	ccfg.SerfLANConfig.MemberlistConfig.ProbeTimeout = 100 * time.Millisecond
	ccfg.SerfLANConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
	ccfg.SerfLANConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond

	ccfg.SerfWANConfig.MemberlistConfig.SuspicionMult = 3
	ccfg.SerfWANConfig.MemberlistConfig.ProbeTimeout = 100 * time.Millisecond
	ccfg.SerfWANConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
	ccfg.SerfWANConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond

	ccfg.RaftConfig.LeaderLeaseTimeout = 20 * time.Millisecond
	ccfg.RaftConfig.HeartbeatTimeout = 40 * time.Millisecond
	ccfg.RaftConfig.ElectionTimeout = 40 * time.Millisecond

	ccfg.CoordinateUpdatePeriod = 100 * time.Millisecond
	ccfg.ServerHealthInterval = 10 * time.Millisecond
	return cfg
}

// TestACLConfig returns a default configuration for testing an agent
// with ACLs.
func TestACLConfig() *Config {
	cfg := TestConfig()
	cfg.ACLDatacenter = cfg.Datacenter
	cfg.ACLDefaultPolicy = "deny"
	cfg.ACLMasterToken = "root"
	cfg.ACLAgentToken = "root"
	cfg.ACLAgentMasterToken = "towel"
	cfg.ACLEnforceVersion8 = &BoolTrue
	return cfg
}
<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"os"
	"testing"

	"github.com/hashicorp/terraform/helper/copy"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/cli"
)

func TestStatePush_empty(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-good"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "replace.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_replaceMatch(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-replace-match"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "replace.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_replaceMatchStdin(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-replace-match"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "replace.tfstate")

	// Setup the replacement to come from stdin
	var buf bytes.Buffer
	if err := terraform.WriteState(expected, &buf); err != nil {
		t.Fatalf("err: %s")
	}
	defer testStdinPipe(t, &buf)()

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"-"}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_lineageMismatch(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-bad-lineage"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "local-state.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_serialNewer(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-serial-newer"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "local-state.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_serialOlder(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-serial-older"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "replace.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}
<commit_msg>command/state-push: fix go vet<commit_after>package command

import (
	"bytes"
	"os"
	"testing"

	"github.com/hashicorp/terraform/helper/copy"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/cli"
)

func TestStatePush_empty(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-good"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "replace.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_replaceMatch(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-replace-match"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "replace.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_replaceMatchStdin(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-replace-match"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "replace.tfstate")

	// Setup the replacement to come from stdin
	var buf bytes.Buffer
	if err := terraform.WriteState(expected, &buf); err != nil {
		t.Fatalf("err: %s", err)
	}
	defer testStdinPipe(t, &buf)()

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"-"}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_lineageMismatch(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-bad-lineage"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "local-state.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_serialNewer(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-serial-newer"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "local-state.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}

func TestStatePush_serialOlder(t *testing.T) {
	// Create a temporary working directory that is empty
	td := tempDir(t)
	copy.CopyDir(testFixturePath("state-push-serial-older"), td)
	defer os.RemoveAll(td)
	defer testChdir(t, td)()

	expected := testStateRead(t, "replace.tfstate")

	p := testProvider()
	ui := new(cli.MockUi)
	c := &StatePushCommand{
		Meta: Meta{
			ContextOpts: testCtxConfig(p),
			Ui:          ui,
		},
	}

	args := []string{"replace.tfstate"}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	actual := testStateRead(t, "local-state.tfstate")
	if !actual.Equal(expected) {
		t.Fatalf("bad: %#v", actual)
	}
}
<|endoftext|>