<commit_before>package runner

import (
	"fmt"
	"github.com/mesos/mesos-go"
	"github.com/vektorlab/mesos-cli/mesosfile"
)

type ErrTaskTerminal struct {
	task   mesos.TaskID
	status mesos.TaskStatus
}

func (e ErrTaskTerminal) Error() string {
	return fmt.Sprintf(
		"Task %s entered a terminal state %s %s",
		e.task.Value,
		e.status.State.String(),
		e.status.Message,
	)
}

// State is an in-memory structure for keeping
// track of tasks. The State is updated when
// provided a mesos.TaskStatus via the Update
// method. If a task is encounted that was not
// specified during the creation of State it will
// panic.
type State struct {
	groups  []*mesosfile.Group
	states  map[string]mesos.TaskState
	pending chan *mesosfile.Group
	updates chan mesos.TaskStatus
	last    mesos.TaskState
	restart bool
	sync    bool
	done    bool
}

func NewState(groups []*mesosfile.Group, restart, sync bool) *State {
	state := &State{
		groups:  groups,
		states:  map[string]mesos.TaskState{},
		pending: make(chan *mesosfile.Group, len(groups)),
		updates: make(chan mesos.TaskStatus),
		restart: restart,
		sync:    sync,
		last:    mesos.TASK_FINISHED,
	}
	for _, group := range groups {
		// Reset Task and executor UUIDs
		group.Reset()
		for _, task := range group.Tasks {
			// Set state to TASK_STARTING
			state.states[task.TaskID.Value] = mesos.TaskState(0)
		}
		// Push the group into pending chan
		state.pending <- group
	}
	return state
}

// Total returns the total number of task groups.
func (s *State) Total() int {
	return len(s.groups)
}

// Pending returns the next task group waiting
// to be scheduled. If a returned group is not
// scheduled the caller must return it via
// Append or it will be lost.
func (s *State) Pop() *mesosfile.Group {
	// If running task groups synchronously each subsequent group (and task) must reach
	// TASK_FINISHED before the next task is available for scheduling.
	if s.sync && s.last != mesos.TASK_FINISHED {
		return nil
	}
	select {
	case group := <-s.pending:
		if s.sync {
			// Reset last to TASK_STARTING to avoid a race condition
			s.last = mesos.TaskState(0)
		}
		return group
	default:
	}
	return nil
}

// Append pushes the group into the pending chan.
func (s *State) Append(group *mesosfile.Group) {
	s.pending <- group
}

// Update places the TaskStatus into the updates channel
func (s *State) Update(status mesos.TaskStatus) {
	s.updates <- status
}

func (s *State) Monitor() (err error) {
loop:
	for {
		select {
		case status := <-s.updates:
			// Check if the state is "terminal" or if the task exited normally but should be restarted
			if terminal(*status.State) || s.restart && *status.State == mesos.TASK_FINISHED {
				// TODO: Need to "backoff"
				if !s.restart {
					// If we will not restart the task return with ErrTaskTerminal
					err = ErrTaskTerminal{status.TaskID, status}
					break loop
				}
				// Tasks will be restarted
				group := s.find(status.TaskID.Value)
				// Remove all recorded states
				s.purge(group)
				// Reset UUIDs
				group.Reset()
				// Insert new states
				s.insert(group)
				// Push the group back into the pending chan
				s.pending <- group
				continue loop
			}
			// Update the state of this task
			s.states[status.TaskID.Value] = *status.State
			// Store the state of latest update
			s.last = *status.State
		}
		if s.finished() { // All tasks have finished
			break
		}
		if s.done {
			break // Done was toggled
		}
	}
	return err
}

// Toggle shutdown
func (s *State) Done() {
	s.done = true
}

func (s *State) find(id string) *mesosfile.Group {
	for _, group := range s.groups {
		if task := group.Find(id); task != nil {
			return group
		}
	}
	panic(fmt.Sprintf("orphaned task %s", id))
}

func (s *State) purge(g *mesosfile.Group) {
	for _, task := range g.Tasks {
		delete(s.states, task.TaskID.Value)
	}
}

func (s *State) insert(g *mesosfile.Group) {
	for _, task := range g.Tasks {
		s.states[task.TaskID.Value] = mesos.TaskState(0)
	}
}

// Finished checks if all tasks are in state TASK_FINISHED
func (s *State) finished() bool {
	for _, state := range s.states {
		if state != mesos.TASK_FINISHED {
			return false
		}
	}
	return true
}
<commit_msg>ignore orphaned tasks instead of panicing<commit_after>package runner

import (
	"fmt"
	"github.com/mesos/mesos-go"
	"github.com/vektorlab/mesos-cli/mesosfile"
)

type ErrTaskTerminal struct {
	task   mesos.TaskID
	status mesos.TaskStatus
}

func (e ErrTaskTerminal) Error() string {
	return fmt.Sprintf(
		"Task %s entered a terminal state %s %s",
		e.task.Value,
		e.status.State.String(),
		e.status.Message,
	)
}

// State is an in-memory structure for keeping
// track of tasks. The State is updated when
// provided a mesos.TaskStatus via the Update
// method. If a task is encounted that was not
// specified during the creation of State it will
// panic.
type State struct {
	groups  []*mesosfile.Group
	states  map[string]mesos.TaskState
	pending chan *mesosfile.Group
	updates chan mesos.TaskStatus
	last    mesos.TaskState
	restart bool
	sync    bool
	done    bool
}

func NewState(groups []*mesosfile.Group, restart, sync bool) *State {
	state := &State{
		groups:  groups,
		states:  map[string]mesos.TaskState{},
		pending: make(chan *mesosfile.Group, len(groups)),
		updates: make(chan mesos.TaskStatus),
		restart: restart,
		sync:    sync,
		last:    mesos.TASK_FINISHED,
	}
	for _, group := range groups {
		// Reset Task and executor UUIDs
		group.Reset()
		for _, task := range group.Tasks {
			// Set state to TASK_STARTING
			state.states[task.TaskID.Value] = mesos.TaskState(0)
		}
		// Push the group into pending chan
		state.pending <- group
	}
	return state
}

// Total returns the total number of task groups.
func (s *State) Total() int {
	return len(s.groups)
}

// Pending returns the next task group waiting
// to be scheduled. If a returned group is not
// scheduled the caller must return it via
// Append or it will be lost.
func (s *State) Pop() *mesosfile.Group {
	// If running task groups synchronously each subsequent group (and task) must reach
	// TASK_FINISHED before the next task is available for scheduling.
	if s.sync && s.last != mesos.TASK_FINISHED {
		return nil
	}
	select {
	case group := <-s.pending:
		if s.sync {
			// Reset last to TASK_STARTING to avoid a race condition
			s.last = mesos.TaskState(0)
		}
		return group
	default:
	}
	return nil
}

// Append pushes the group into the pending chan.
func (s *State) Append(group *mesosfile.Group) {
	s.pending <- group
}

// Update places the TaskStatus into the updates channel
func (s *State) Update(status mesos.TaskStatus) {
	s.updates <- status
}

func (s *State) Monitor() (err error) {
loop:
	for {
		select {
		case status := <-s.updates:
			group := s.find(status.TaskID.Value)
			// Discard updates from orphaned tasks
			if group == nil {
				continue loop
			}
			// Check if the state is "terminal" or if the task exited normally but should be restarted
			if terminal(*status.State) || s.restart && *status.State == mesos.TASK_FINISHED {
				// TODO: Need to "backoff"
				if !s.restart {
					// If we will not restart the task return with ErrTaskTerminal
					err = ErrTaskTerminal{status.TaskID, status}
					break loop
				}
				// Remove all recorded states
				s.purge(group)
				// Reset UUIDs
				group.Reset()
				// Insert new states
				s.insert(group)
				// Push the group back into the pending chan
				s.pending <- group
				continue loop
			}
			// Update the state of this task
			s.states[status.TaskID.Value] = *status.State
			// Store the state of latest update
			s.last = *status.State
		}
		if s.finished() { // All tasks have finished
			break
		}
		if s.done {
			break // Done was toggled
		}
	}
	return err
}

// Toggle shutdown
func (s *State) Done() {
	s.done = true
}

func (s *State) find(id string) (g *mesosfile.Group) {
	for _, group := range s.groups {
		if task := group.Find(id); task != nil {
			g = group
		}
	}
	return g
}

func (s *State) purge(g *mesosfile.Group) {
	for _, task := range g.Tasks {
		delete(s.states, task.TaskID.Value)
	}
}

func (s *State) insert(g *mesosfile.Group) {
	for _, task := range g.Tasks {
		s.states[task.TaskID.Value] = mesos.TaskState(0)
	}
}

// Finished checks if all tasks are in state TASK_FINISHED
func (s *State) finished() bool {
	for _, state := range s.states {
		if state != mesos.TASK_FINISHED {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/argusdusty/Ferret"
	"io/ioutil"
	"net/http"
	"strconv"
)

var port = flag.Int("port", 8080, "a serving TCP port")
var Correction = func(b []byte) [][]byte { return ferret.ErrorCorrect(b, ferret.LowercaseLetters) }
var LengthSorter = func(s string, v interface{}, l int, i int) float64 { return -float64(l + i) }
var FreqSorter = func(s string, v interface{}, l int, i int) float64 { return float64(v.(uint64)) }
var Converter = ferret.UnicodeToLowerASCII
var ValueIds = map[string]SearchItem{}
var SearchEngine *ferret.InvertedSuffix

func main() {
	fmt.Println("Hi, I am Rustic Search Server!")
	fmt.Println("Call rusticsearch -h for help with parameters")
	flag.Parse()

	http.HandleFunc("/", handler)
	Data, err := ioutil.ReadFile("search_index.csv")
	if err != nil {
		fmt.Println("search_index.csv not found :(")
		panic(err)
	}

	fmt.Println("Parsing search_index.csv...")
	Words := make([]string, 0)
	Values := make([]interface{}, 0)
	for i, Vals := range bytes.Split(Data, []byte("\n")) {
		WordFreq := bytes.Split(Vals, []byte("----------> "))
		if len(WordFreq) != 2 {
			fmt.Printf("Bollocks! search_index.csv line: %v breaks everything: \n \"%v\" \n I quit! \n", i, string(Vals))
			panic(Vals)
		}

		Words = append(Words, string(WordFreq[0]))
		// to add some priority mechanism in here in the future
		Values = append(Values, 10)
		ValueIds[string(WordFreq[0])] = SearchItem{string(WordFreq[0]), string(WordFreq[1])}
	}

	fmt.Println("Created index...")
	SearchEngine = ferret.New(Words, Words, Values, Converter)

	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~")
	fmt.Printf("   Starting server at port %v... \n", ":"+strconv.Itoa(*port))
	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~")
	err = http.ListenAndServe(":"+strconv.Itoa(*port), nil)
	if err != nil {
		fmt.Printf("Dang it! Error at ListenAndServe: %v \n", err)
		panic(err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	h := w.Header()
	h.Set("Access-Control-Allow-Origin", "*")
	h.Set("Access-Control-Allow-Methods", "POST, GET, PUT, PATCH, DELETE, OPTIONS")
	h.Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, API-Date, Host, Authorization, Key, User-Token")
	h.Set("Access-Control-Max-Age", "1728000")

	fmt.Println("ANOTHER REQUEST: %s", r.URL.Path)
	results, _ := SearchEngine.Query(r.URL.Path[1:], 5)
	output := make([]SearchItem, 0)
	for _, word := range results {
		fmt.Printf("~~~~~~~~~~~~: %v \n", string(word))
		output = append(output, ValueIds[string(word)])
		fmt.Println("Here is the value added: ", ValueIds[string(word)])
	}
	searchResults, err := json.Marshal(SearchResult{output})
	if err != nil {
		panic(err)
	}
	fmt.Fprintf(w, string(searchResults))
}
<commit_msg>id and names Mixed up<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/argusdusty/Ferret"
	"io/ioutil"
	"net/http"
	"strconv"
)

var port = flag.Int("port", 8080, "a serving TCP port")
var Correction = func(b []byte) [][]byte { return ferret.ErrorCorrect(b, ferret.LowercaseLetters) }
var LengthSorter = func(s string, v interface{}, l int, i int) float64 { return -float64(l + i) }
var FreqSorter = func(s string, v interface{}, l int, i int) float64 { return float64(v.(uint64)) }
var Converter = ferret.UnicodeToLowerASCII
var ValueIds = map[string]SearchItem{}
var SearchEngine *ferret.InvertedSuffix

func main() {
	fmt.Println("Hi, I am Rustic Search Server!")
	fmt.Println("Call rusticsearch -h for help with parameters")
	flag.Parse()

	http.HandleFunc("/", handler)
	Data, err := ioutil.ReadFile("search_index.csv")
	if err != nil {
		fmt.Println("search_index.csv not found :(")
		panic(err)
	}

	fmt.Println("Parsing search_index.csv...")
	Words := make([]string, 0)
	Values := make([]interface{}, 0)
	for i, Vals := range bytes.Split(Data, []byte("\n")) {
		WordFreq := bytes.Split(Vals, []byte("----------> "))
		if len(WordFreq) != 2 {
			fmt.Printf("Bollocks! search_index.csv line: %v breaks everything: \n \"%v\" \n I quit! \n", i, string(Vals))
			panic(Vals)
		}

		Words = append(Words, string(WordFreq[0]))
		// to add some priority mechanism in here in the future
		Values = append(Values, 10)
		ValueIds[string(WordFreq[0])] = SearchItem{string(WordFreq[1]), string(WordFreq[0])}
	}

	fmt.Println("Created index...")
	SearchEngine = ferret.New(Words, Words, Values, Converter)

	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~")
	fmt.Printf("   Starting server at port %v... \n", ":"+strconv.Itoa(*port))
	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~")
	err = http.ListenAndServe(":"+strconv.Itoa(*port), nil)
	if err != nil {
		fmt.Printf("Dang it! Error at ListenAndServe: %v \n", err)
		panic(err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	h := w.Header()
	h.Set("Access-Control-Allow-Origin", "*")
	h.Set("Access-Control-Allow-Methods", "POST, GET, PUT, PATCH, DELETE, OPTIONS")
	h.Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, API-Date, Host, Authorization, Key, User-Token")
	h.Set("Access-Control-Max-Age", "1728000")

	fmt.Println("ANOTHER REQUEST: %s", r.URL.Path)
	results, _ := SearchEngine.Query(r.URL.Path[1:], 5)
	output := make([]SearchItem, 0)
	for _, word := range results {
		fmt.Printf("~~~~~~~~~~~~: %v \n", string(word))
		output = append(output, ValueIds[string(word)])
		fmt.Println("Here is the value added: ", ValueIds[string(word)])
	}
	searchResults, err := json.Marshal(SearchResult{output})
	if err != nil {
		panic(err)
	}
	fmt.Fprintf(w, string(searchResults))
}
<|endoftext|>


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

package tcp_test

import (
	"net"
	"runtime"
	"testing"
	"time"

	"github.com/mikioh/tcp"
)

func TestKeepAlive(t *testing.T) {
	switch runtime.GOOS {
	case "freebsd", "linux":
	default:
		t.Skipf("%s/%s", runtime.GOOS, runtime.GOARCH)
	}

	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	go func() {
		for {
			c, err := ln.Accept()
			if err != nil {
				break
			}
			defer c.Close()
		}
	}()

	c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()
	tc, err := tcp.NewConn(c)
	if err != nil {
		t.Fatal(err)
	}
	defer tc.Close()
	if err := tc.SetKeepAlive(true); err != nil {
		t.Error(err)
	}
	if err := tc.SetKeepAliveIdleInterval(time.Second); err != nil {
		t.Error(err)
	}
	if err := tc.SetKeepAliveProbeInterval(time.Second); err != nil {
		t.Error(err)
	}
	if err := tc.SetKeepAliveProbes(1); err != nil {
		t.Error(err)
	}
}
<commit_msg>tcp: enable keepalive tests on darwin<commit_after>// Copyright 2014 Mikio Hara. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tcp_test

import (
	"net"
	"runtime"
	"testing"
	"time"

	"github.com/mikioh/tcp"
)

func TestKeepAlive(t *testing.T) {
	switch runtime.GOOS {
	case "darwin", "freebsd", "linux":
	default:
		t.Skipf("%s/%s", runtime.GOOS, runtime.GOARCH)
	}

	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	go func() {
		for {
			c, err := ln.Accept()
			if err != nil {
				break
			}
			defer c.Close()
		}
	}()

	c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()
	tc, err := tcp.NewConn(c)
	if err != nil {
		t.Fatal(err)
	}
	defer tc.Close()
	if err := tc.SetKeepAlive(true); err != nil {
		t.Error(err)
	}
	if err := tc.SetKeepAliveIdleInterval(time.Second); err != nil {
		t.Error(err)
	}
	if err := tc.SetKeepAliveProbeInterval(time.Second); err != nil {
		t.Error(err)
	}
	if err := tc.SetKeepAliveProbes(1); err != nil {
		t.Error(err)
	}
}
<|endoftext|>


<commit_before>package raftgorums

import (
	"golang.org/x/net/context"

	"github.com/relab/raft"
	"github.com/relab/raft/commonpb"
)

// ProposeConf implements raft.Raft.
func (r *Raft) ProposeConf(ctx context.Context, req *commonpb.ReconfRequest) (raft.Future, error) {
	cmd, err := req.Marshal()

	if err != nil {
		return nil, err
	}

	future, err := r.cmdToFuture(cmd, commonpb.EntryConfChange)

	// TODO Fix error returned here, NotLeader should be a status code.
	if err != nil {
		return nil, err
	}

	if !r.mem.startReconfiguration(req) {
		future.Respond(&commonpb.ReconfResponse{
			Status: commonpb.ReconfTimeout,
		})
		return future, nil
	}

	go r.replicate(req.ServerID, future)

	return future, nil
}

// ProposeCmd implements raft.Raft.
func (r *Raft) ProposeCmd(ctx context.Context, cmd []byte) (raft.Future, error) {
	future, err := r.cmdToFuture(cmd, commonpb.EntryNormal)

	if err != nil {
		return nil, err
	}

	select {
	case r.queue <- future:
		if r.metricsEnabled {
			rmetrics.writeReqs.Add(1)
		}
		return future, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// ReadCmd implements raft.Raft.
func (r *Raft) ReadCmd(ctx context.Context, cmd []byte) (raft.Future, error) {
	future, err := r.cmdToFuture(cmd, commonpb.EntryNormal)

	if err != nil {
		return nil, err
	}

	if r.metricsEnabled {
		rmetrics.readReqs.Add(1)
	}

	r.Lock()
	r.pendingReads = append(r.pendingReads, future)
	r.Unlock()

	return future, nil
}
<commit_msg>raftgorums/api.go: Skip replicate step when removing a server<commit_after>package raftgorums

import (
	"golang.org/x/net/context"

	"github.com/relab/raft"
	"github.com/relab/raft/commonpb"
)

// ProposeConf implements raft.Raft.
func (r *Raft) ProposeConf(ctx context.Context, req *commonpb.ReconfRequest) (raft.Future, error) {
	cmd, err := req.Marshal()

	if err != nil {
		return nil, err
	}

	future, err := r.cmdToFuture(cmd, commonpb.EntryConfChange)

	// TODO Fix error returned here, NotLeader should be a status code.
	if err != nil {
		return nil, err
	}

	if !r.mem.startReconfiguration(req) {
		future.Respond(&commonpb.ReconfResponse{
			Status: commonpb.ReconfTimeout,
		})
		return future, nil
	}

	switch req.ReconfType {
	case commonpb.ReconfAdd:
		go r.replicate(req.ServerID, future)
	case commonpb.ReconfRemove:
		r.queue <- future
	default:
		panic("malformed reconf request")
	}

	return future, nil
}

// ProposeCmd implements raft.Raft.
func (r *Raft) ProposeCmd(ctx context.Context, cmd []byte) (raft.Future, error) {
	future, err := r.cmdToFuture(cmd, commonpb.EntryNormal)

	if err != nil {
		return nil, err
	}

	select {
	case r.queue <- future:
		if r.metricsEnabled {
			rmetrics.writeReqs.Add(1)
		}
		return future, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// ReadCmd implements raft.Raft.
func (r *Raft) ReadCmd(ctx context.Context, cmd []byte) (raft.Future, error) {
	future, err := r.cmdToFuture(cmd, commonpb.EntryNormal)

	if err != nil {
		return nil, err
	}

	if r.metricsEnabled {
		rmetrics.readReqs.Add(1)
	}

	r.Lock()
	r.pendingReads = append(r.pendingReads, future)
	r.Unlock()

	return future, nil
}
<|endoftext|>


<commit_before>package memory

import (
	"sync"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestTreeFromPath(t *testing.T) {
	path := "foo.bar.baz"
	Convey("when treeFromPath is given a path", t, func() {
		tree := treeFromPath(path)
		So(len(tree.Items), ShouldEqual, 4)
		So(tree.Items[""], ShouldNotBeNil)
		So(tree.Items[""].Leaf(), ShouldBeFalse)
		So(tree.Items["foo"], ShouldNotBeNil)
		So(tree.Items["foo"].Leaf(), ShouldBeFalse)
		So(tree.Items["foo.bar"], ShouldNotBeNil)
		So(tree.Items["foo.bar"].Leaf(), ShouldBeFalse)
		So(tree.Items["foo.bar.baz"], ShouldNotBeNil)
		So(tree.Items["foo.bar.baz"].Leaf(), ShouldBeTrue)
	})
}

func TestFindCache(t *testing.T) {
	Convey("when findCache is empty", t, func() {
		c := NewFindCache(10, 1, time.Second*2)
		Convey("0 results should be returned", func() {
			result, ok := c.Get(1, "foo.bar.*")
			So(ok, ShouldBeFalse)
			So(result, ShouldHaveLength, 0)
		})
		Convey("when adding entries to the cache", func() {
			pattern := "foo.bar.*"
			results, err := find(treeFromPath("foo.bar.foo"), pattern)
			So(err, ShouldBeNil)
			So(results, ShouldHaveLength, 1)
			c.Add(1, "foo.bar.*", results)
			So(c.cache[1].Len(), ShouldEqual, 1)
			Convey("when getting cached pattern", func() {
				result, ok := c.Get(1, "foo.bar.*")
				So(ok, ShouldBeTrue)
				So(result, ShouldHaveLength, 1)
				Convey("After invalidating path that matches pattern", func() {
					c.InvalidateFor(1, "foo.bar.baz")
					So(c.cache[1].Len(), ShouldEqual, 0)
				})
				Convey("After invalidating path that doesn't match cached pattern", func() {
					c.InvalidateFor(1, "foo.foo.baz")
					So(c.cache[1].Len(), ShouldEqual, 1)
				})
			})
			Convey("when findCache invalidation falls behind", func() {
				c.Add(1, "foo.{a,b,c}*.*", results)
				c.Add(1, "foo.{a,b,e}*.*", results)
				c.Add(1, "foo.{a,b,f}*.*", results)
				c.Lock()
				var wg sync.WaitGroup
				wg.Add(10)
				for i := 0; i < 10; i++ {
					go func() {
						c.InvalidateFor(1, "foo.baz.foo.a.b.c.d.e.f.g.h")
						wg.Done()
					}()
				}
				c.Unlock()
				wg.Wait()

				So(len(c.cache), ShouldEqual, 0)
				Convey("when adding to cache in backoff", func() {
					c.Add(1, "foo.*.*", results)
					So(len(c.cache), ShouldEqual, 0)
					result, ok := c.Get(1, "foo.*.*")
					So(ok, ShouldBeFalse)
					So(result, ShouldHaveLength, 0)
				})
				Convey("when adding to cache after backoff time", func() {
					time.Sleep(time.Millisecond * 2500)
					c.Add(1, "foo.*.*", results)
					So(len(c.cache), ShouldEqual, 1)
					result, ok := c.Get(1, "foo.*.*")
					So(ok, ShouldBeTrue)
					So(result, ShouldHaveLength, 1)
				})
			})
		})
	})

}

func BenchmarkTreeFromPath(b *testing.B) {
	numPaths := 1000
	paths := getSeriesNames(10, numPaths, "benchmark")
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		p := i % numPaths
		treeFromPath(paths[p])
	}
}
<commit_msg>fix intermittant TestFindCache failures<commit_after>package memory

import (
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestTreeFromPath(t *testing.T) {
	path := "foo.bar.baz"
	Convey("when treeFromPath is given a path", t, func() {
		tree := treeFromPath(path)
		So(len(tree.Items), ShouldEqual, 4)
		So(tree.Items[""], ShouldNotBeNil)
		So(tree.Items[""].Leaf(), ShouldBeFalse)
		So(tree.Items["foo"], ShouldNotBeNil)
		So(tree.Items["foo"].Leaf(), ShouldBeFalse)
		So(tree.Items["foo.bar"], ShouldNotBeNil)
		So(tree.Items["foo.bar"].Leaf(), ShouldBeFalse)
		So(tree.Items["foo.bar.baz"], ShouldNotBeNil)
		So(tree.Items["foo.bar.baz"].Leaf(), ShouldBeTrue)
	})
}

func TestFindCache(t *testing.T) {
	Convey("when findCache is empty", t, func() {
		c := NewFindCache(10, 1, time.Second*2)
		Convey("0 results should be returned", func() {
			result, ok := c.Get(1, "foo.bar.*")
			So(ok, ShouldBeFalse)
			So(result, ShouldHaveLength, 0)
		})
		Convey("when adding entries to the cache", func() {
			pattern := "foo.bar.*"
			results, err := find(treeFromPath("foo.bar.foo"), pattern)
			So(err, ShouldBeNil)
			So(results, ShouldHaveLength, 1)
			c.Add(1, "foo.bar.*", results)
			So(c.cache[1].Len(), ShouldEqual, 1)
			Convey("when getting cached pattern", func() {
				result, ok := c.Get(1, "foo.bar.*")
				So(ok, ShouldBeTrue)
				So(result, ShouldHaveLength, 1)
				Convey("After invalidating path that matches pattern", func() {
					c.InvalidateFor(1, "foo.bar.baz")
					So(c.cache[1].Len(), ShouldEqual, 0)
				})
				Convey("After invalidating path that doesn't match cached pattern", func() {
					c.InvalidateFor(1, "foo.foo.baz")
					So(c.cache[1].Len(), ShouldEqual, 1)
				})
			})
			Convey("when findCache invalidation falls behind", func() {
				c.Add(1, "foo.{a,b,c}*.*", results)
				c.Add(1, "foo.{a,b,e}*.*", results)
				c.Add(1, "foo.{a,b,f}*.*", results)
				c.newSeries[1] <- struct{}{}
				c.InvalidateFor(1, "foo.baz.foo.a.b.c.d.e.f.g.h")

				So(len(c.cache), ShouldEqual, 0)
				Convey("when adding to cache in backoff", func() {
					c.Add(1, "foo.*.*", results)
					So(len(c.cache), ShouldEqual, 0)
					result, ok := c.Get(1, "foo.*.*")
					So(ok, ShouldBeFalse)
					So(result, ShouldHaveLength, 0)
				})
				Convey("when adding to cache after backoff time", func() {
					time.Sleep(time.Millisecond * 2500)
					c.Add(1, "foo.*.*", results)
					So(len(c.cache), ShouldEqual, 1)
					result, ok := c.Get(1, "foo.*.*")
					So(ok, ShouldBeTrue)
					So(result, ShouldHaveLength, 1)
				})
			})
		})
	})

}

func BenchmarkTreeFromPath(b *testing.B) {
	numPaths := 1000
	paths := getSeriesNames(10, numPaths, "benchmark")
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		p := i % numPaths
		treeFromPath(paths[p])
	}
}
<|endoftext|>


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

package lease_test

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

	"golang.org/x/net/context"

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

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

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

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

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

	size = crp.Wrapped.Size()
	return
}

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

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

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

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

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

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

// A range to read, the contents we expect to get back, and the error we expect
// to see, if any.
type readAtTestCase struct {
	start            int64
	limit            int64
	expectedErr      error
	expectedContents string
}

func runReadAtTestCases(
	rp lease.ReadProxy,
	cases []readAtTestCase) {
	for _, tc := range cases {
		AssertLe(tc.start, tc.limit)
		buf := make([]byte, tc.limit-tc.start)

		n, err := rp.ReadAt(context.Background(), buf, tc.start)
		AssertEq(tc.expectedErr, err)
		AssertEq(tc.expectedContents, string(buf[:n]))
	}
}

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

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

type MultiReadProxyTest struct {
	ctx context.Context

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

	leaser lease.FileLeaser
	proxy  *checkingReadProxy
}

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

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

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

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

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

func (t *MultiReadProxyTest) makeRefreshers() (refreshers []lease.Refresher) {
	for i, contents := range refresherContents {
		iCopy := i
		r := &funcRefresher{
			N: int64(len(contents)),
			F: func(ctx context.Context) (rc io.ReadCloser, err error) {
				rc = ioutil.NopCloser(strings.NewReader(contents))
				err = t.refresherErrors[iCopy]
				return
			},
		}

		refreshers = append(refreshers, r)
	}

	return
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	runReadAtTestCases(t.proxy, testCases)
}

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

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

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

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

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

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

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

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

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

	runReadAtTestCases(t.proxy, testCases)
}

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

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

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

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

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

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

package lease_test

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

	"golang.org/x/net/context"

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

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

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

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

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

	size = crp.Wrapped.Size()
	return
}

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

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

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

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

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

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

// A range to read, the contents we expect to get back, and the error we expect
// to see, if any.
type readAtTestCase struct {
	start            int64
	limit            int64
	expectedErr      error
	expectedContents string
}

func runReadAtTestCases(
	rp lease.ReadProxy,
	cases []readAtTestCase) {
	for _, tc := range cases {
		AssertLe(tc.start, tc.limit)
		buf := make([]byte, tc.limit-tc.start)

		n, err := rp.ReadAt(context.Background(), buf, tc.start)
		AssertEq(tc.expectedErr, err)
		AssertEq(tc.expectedContents, string(buf[:n]))
	}
}

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

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

type MultiReadProxyTest struct {
	ctx context.Context

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

	leaser lease.FileLeaser
	proxy  *checkingReadProxy
}

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

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

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

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

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

func (t *MultiReadProxyTest) makeRefreshers() (refreshers []lease.Refresher) {
	for i, contents := range refresherContents {
		iCopy := i
		r := &funcRefresher{
			N: int64(len(contents)),
			F: func(ctx context.Context) (rc io.ReadCloser, err error) {
				rc = ioutil.NopCloser(strings.NewReader(contents))
				err = t.refresherErrors[iCopy]
				return
			},
		}

		refreshers = append(refreshers, r)
	}

	return
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	runReadAtTestCases(t.proxy, testCases)
}

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

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

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

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

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

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

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

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

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

	runReadAtTestCases(t.proxy, testCases)
}

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

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

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

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

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

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


<commit_before>package workflows

import (
	"encoding/json"
	"fmt"
	"io"

	"github.com/olekukonko/tablewriter"
	"github.com/stelligent/mu/common"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

// NewEnvironmentViewer create a new workflow for showing an environment
func NewEnvironmentViewer(ctx *common.Context, format string, environmentName string, viewTasks bool, writer io.Writer) Executor {

	workflow := new(environmentWorkflow)

	var environmentViewer func() error
	if format == JSON {
		environmentViewer = workflow.environmentViewerJSON(ctx.Config.Namespace, environmentName, ctx.StackManager, ctx.StackManager, ctx.ClusterManager, writer)
	} else if format == SHELL {
		environmentViewer = workflow.environmentViewerSHELL(ctx.Config.Namespace, environmentName, ctx.StackManager, ctx.StackManager, ctx.ClusterManager, writer)
	} else {
		environmentViewer = workflow.environmentViewerCli(ctx.Config.Namespace, environmentName, ctx.StackManager, ctx.StackManager, ctx.ClusterManager, ctx.InstanceManager, ctx.TaskManager, ctx.KubernetesResourceManagerProvider, viewTasks, writer)
	}

	return newPipelineExecutor(
		environmentViewer,
	)
}

func (workflow *environmentWorkflow) environmentViewerJSON(namespace string, environmentName string, stackGetter common.StackGetter, stackLister common.StackLister, instanceLister common.ClusterInstanceLister, writer io.Writer) Executor {
	return func() error {
		lbStackName := common.CreateStackName(namespace, common.StackTypeLoadBalancer, environmentName)
		lbStack, _ := stackGetter.GetStack(lbStackName)

		clusterStackName := common.CreateStackName(namespace, common.StackTypeEnv, environmentName)
		clusterStack, _ := stackGetter.GetStack(clusterStackName)

		output := common.JSONOutput{}
		if lbStack != nil {
			output.Values[FirstValueIndex].Key = BaseURLKey
			output.Values[FirstValueIndex].Value = lbStack.Outputs[BaseURLValueKey]
		} else if clusterStack != nil {
			output.Values[FirstValueIndex].Key = BaseURLKey
			output.Values[FirstValueIndex].Value = clusterStack.Outputs[BaseURLValueKey]
		}

		enc := json.NewEncoder(writer)
		enc.Encode(&output)

		return nil
	}
}

func (workflow *environmentWorkflow) environmentViewerSHELL(namespace string, environmentName string, stackGetter common.StackGetter, stackLister common.StackLister, instanceLister common.ClusterInstanceLister, writer io.Writer) Executor {
	return func() error {
		lbStackName := common.CreateStackName(namespace, common.StackTypeLoadBalancer, environmentName)
		lbStack, _ := stackGetter.GetStack(lbStackName)

		clusterStackName := common.CreateStackName(namespace, common.StackTypeEnv, environmentName)
		clusterStack, _ := stackGetter.GetStack(clusterStackName)

		output := common.JSONOutput{}
		if lbStack != nil {
			output.Values[FirstValueIndex].Key = BaseURLKey
			output.Values[FirstValueIndex].Value = lbStack.Outputs[BaseURLValueKey]
		} else if clusterStack != nil {
			output.Values[FirstValueIndex].Key = BaseURLKey
			output.Values[FirstValueIndex].Value = clusterStack.Outputs[BaseURLValueKey]
		}

		for _, val := range output.Values {
			fmt.Fprintf(writer, "%s=%s\n", val.Key, val.Value)
		}

		return nil
	}
}

func (workflow *environmentWorkflow) environmentViewerCli(namespace string, environmentName string, stackGetter common.StackGetter, stackLister common.StackLister, clusterInstanceLister common.ClusterInstanceLister, instanceLister common.InstanceLister, taskManager common.TaskManager, kubernetesResourceManagerProvider common.KubernetesResourceManagerProvider, viewTasks bool, writer io.Writer) Executor {
	return func() error {
		lbStackName := common.CreateStackName(namespace, common.StackTypeLoadBalancer, environmentName)
		lbStack, err := stackGetter.GetStack(lbStackName)

		clusterStackName := common.CreateStackName(namespace, common.StackTypeEnv, environmentName)
		clusterStack, err := stackGetter.GetStack(clusterStackName)

		vpcStackName := common.CreateStackName(namespace, common.StackTypeVpc, environmentName)
		vpcStack, _ := stackGetter.GetStack(vpcStackName)

		fmt.Fprintf(writer, HeaderValueFormat, Bold(EnvironmentHeader), environmentName)
		if clusterStack != nil {
			fmt.Fprintf(writer, StackFormat, Bold(ClusterStack), clusterStack.Name, colorizeStackStatus(clusterStack.Status))
		}

		if vpcStack == nil {
			fmt.Fprintf(writer, UnmanagedStackFormat, Bold(VPCStack))
		} else {
			fmt.Fprintf(writer, StackFormat, Bold(VPCStack), vpcStack.Name, colorizeStackStatus(vpcStack.Status))
			fmt.Fprintf(writer, HeaderValueFormat, Bold(BastionHost), vpcStack.Outputs[BastionHostKey])
		}

		if lbStack != nil {
			fmt.Fprintf(writer, HeaderValueFormat, Bold(BaseURLHeader), lbStack.Outputs[BaseURLValueKey])
		} else if clusterStack != nil {
			fmt.Fprintf(writer, HeaderValueFormat, Bold(BaseURLHeader), clusterStack.Outputs[BaseURLValueKey])
		}

		if clusterStack != nil && clusterStack.Tags["provider"] == string(common.EnvProviderEcs) {
			fmt.Fprintf(writer, HeadNewlineHeader, Bold(ContainerInstances))
			containerInstances, err := clusterInstanceLister.ListInstances(clusterStack.Outputs[ECSClusterKey])
			if err != nil {
				return err
			}

			instanceIds := make([]string, len(containerInstances))
			for i, containerInstance := range containerInstances {
				instanceIds[i] = common.StringValue(containerInstance.Ec2InstanceId)
			}
			instances, err := instanceLister.ListInstances(instanceIds...)
			if err != nil {
				return err
			}

			instanceViews := buildInstanceViewForECS(containerInstances, instances)
			printInstanceTable(writer, instanceViews)
		} else if clusterStack != nil && clusterStack.Tags["provider"] == string(common.EnvProviderEks) {

			workflow.environment = &common.Environment{
				Name: environmentName,
			}
			workflow.connectKubernetes(namespace, kubernetesResourceManagerProvider)()

			nodes, err := workflow.kubernetesResourceManager.ListResources("v1", "Node", "")
			if err != nil {
				return err
			}
			instanceViews := buildInstanceViewForEKS(nodes)
			printInstanceTable(writer, instanceViews)
		}

		fmt.Fprint(writer, NewLine)
		fmt.Fprintf(writer, HeadNewlineHeader, Bold(ServicesHeader))
		stacks, err := stackLister.ListStacks(common.StackTypeService, namespace)
		if err != nil {
			return err
		}
		table := buildServiceTable(stacks, environmentName, writer)
		table.Render()

		if viewTasks {
			buildContainerTable(namespace, taskManager, stacks, environmentName, writer)
		}

		fmt.Fprint(writer, NewLine)

		return nil
	}
}

func buildContainerTable(namespace string, taskManager common.TaskManager, stacks []*common.Stack, environmentName string, writer io.Writer) {
	for _, stackValues := range stacks {
		if stackValues.Tags[EnvTagKey] != environmentName {
			continue
		}
		doViewTasks(namespace, taskManager, writer, stacks, stackValues.Tags[SvcTagKey])
	}
}

func buildServiceTable(stacks []*common.Stack, environmentName string, writer io.Writer) *tablewriter.Table {
	table := CreateTableSection(writer, ServiceTableHeader)

	for _, stackValues := range stacks {
		if stackValues.Tags[EnvTagKey] != environmentName {
			continue
		}

		table.Append([]string{
			Bold(stackValues.Tags[SvcTagKey]),
			stackValues.Tags["revision"],
			fmt.Sprintf(KeyValueFormat, colorizeStackStatus(stackValues.Status), stackValues.StatusReason),
			stackValues.LastUpdateTime.Local().Format(LastUpdateTime),
		})
	}

	return table
}

func buildInstanceViewForECS(containerInstances []common.ContainerInstance, instances []common.Instance) []*InstanceView {
	instanceIps := make(map[string]string)
	for _, instance := range instances {
		instanceIps[common.StringValue(instance.InstanceId)] = common.StringValue(instance.PrivateIpAddress)
	}

	instanceViews := make([]*InstanceView, len(containerInstances))

	for i, instance := range containerInstances {
		instanceType := UnknownValue
		availZone := UnknownValue
		amiID := UnknownValue
		for _, attr := range instance.Attributes {
			switch common.StringValue(attr.Name) {
			case ECSAvailabilityZoneKey:
				availZone = common.StringValue(attr.Value)
			case ECSInstanceTypeKey:
				instanceType = common.StringValue(attr.Value)
			case ECSAMIKey:
				amiID = common.StringValue(attr.Value)
			}
		}
		var cpuAvail int64
		var memAvail int64
		for _, resource := range instance.RemainingResources {
			switch common.StringValue(resource.Name) {
			case CPU:
				cpuAvail = common.Int64Value(resource.IntegerValue)
			case MEMORY:
				memAvail = common.Int64Value(resource.IntegerValue)
			}
		}
		instanceViews[i] = &InstanceView{
			common.StringValue(instance.Ec2InstanceId),
			instanceType,
			amiID,
			instanceIps[common.StringValue(instance.Ec2InstanceId)],
			availZone,
			common.BoolValue(instance.AgentConnected),
			common.StringValue(instance.Status),
			common.Int64Value(instance.RunningTasksCount),
			cpuAvail,
			memAvail,
		}
	}

	return instanceViews
}

func buildInstanceViewForEKS(nodes *unstructured.UnstructuredList) []*InstanceView {
	instanceViews := make([]*InstanceView, len(nodes.Items))
	for i, node := range nodes.Items {
		var ip string
		addresses := common.MapGetSlice(node.Object, "status", "addresses")
		for _, address := range addresses {
			if common.MapGetString(address, "type") == "InternalIP" {
				ip = common.MapGetString(address, "address")
			}
		}
		instanceViews[i] = &InstanceView{
			common.MapGetString(node.Object, "spec", "externalID"),
			"",
			common.MapGetString(node.Object, "status", "nodeInfo", "kernelVersion"),
			ip,
			common.MapGetString(node.Object, "metadata", "labels", "failure-domain.beta.kubernetes.io/zone"),
			true,
			"",
			0,
			0,
			0,
		}
	}

	return instanceViews
}

func printInstanceTable(writer io.Writer, instances []*InstanceView) {
	table := CreateTableSection(writer, EnvironmentAMITableHeader)

	for _, instance := range instances {
		table.Append([]string{
			instance.instanceID,
			instance.instanceType,
			instance.amiID,
			instance.instanceIP,
			instance.availZone,
			fmt.Sprintf(BoolStringFormat, instance.ready),
			instance.status,
			fmt.Sprintf(IntStringFormat, instance.taskCount),
			fmt.Sprintf(IntStringFormat, instance.cpuAvail),
			fmt.Sprintf(IntStringFormat, instance.memAvail),
		})
	}

	table.Render()
}

type InstanceView struct {
	instanceID   string
	instanceType string
	amiID        string
	instanceIP   string
	availZone    string
	ready        bool
	status       string
	taskCount    int64
	cpuAvail     int64
	memAvail     int64
}
<commit_msg>resolve lint issue from missing comment<commit_after>package workflows

import (
	"encoding/json"
	"fmt"
	"io"

	"github.com/olekukonko/tablewriter"
	"github.com/stelligent/mu/common"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

// NewEnvironmentViewer create a new workflow for showing an environment
func NewEnvironmentViewer(ctx *common.Context, format string, environmentName string, viewTasks bool, writer io.Writer) Executor {

	workflow := new(environmentWorkflow)

	var environmentViewer func() error
	if format == JSON {
		environmentViewer = workflow.environmentViewerJSON(ctx.Config.Namespace, environmentName, ctx.StackManager, ctx.StackManager, ctx.ClusterManager, writer)
	} else if format == SHELL {
		environmentViewer = workflow.environmentViewerSHELL(ctx.Config.Namespace, environmentName, ctx.StackManager, ctx.StackManager, ctx.ClusterManager, writer)
	} else {
		environmentViewer = workflow.environmentViewerCli(ctx.Config.Namespace, environmentName, ctx.StackManager, ctx.StackManager, ctx.ClusterManager, ctx.InstanceManager, ctx.TaskManager, ctx.KubernetesResourceManagerProvider, viewTasks, writer)
	}

	return newPipelineExecutor(
		environmentViewer,
	)
}

func (workflow *environmentWorkflow) environmentViewerJSON(namespace string, environmentName string, stackGetter common.StackGetter, stackLister common.StackLister, instanceLister common.ClusterInstanceLister, writer io.Writer) Executor {
	return func() error {
		lbStackName := common.CreateStackName(namespace, common.StackTypeLoadBalancer, environmentName)
		lbStack, _ := stackGetter.GetStack(lbStackName)

		clusterStackName := common.CreateStackName(namespace, common.StackTypeEnv, environmentName)
		clusterStack, _ := stackGetter.GetStack(clusterStackName)

		output := common.JSONOutput{}
		if lbStack != nil {
			output.Values[FirstValueIndex].Key = BaseURLKey
			output.Values[FirstValueIndex].Value = lbStack.Outputs[BaseURLValueKey]
		} else if clusterStack != nil {
			output.Values[FirstValueIndex].Key = BaseURLKey
			output.Values[FirstValueIndex].Value = clusterStack.Outputs[BaseURLValueKey]
		}

		enc := json.NewEncoder(writer)
		enc.Encode(&output)

		return nil
	}
}

func (workflow *environmentWorkflow) environmentViewerSHELL(namespace string, environmentName string, stackGetter common.StackGetter, stackLister common.StackLister, instanceLister common.ClusterInstanceLister, writer io.Writer) Executor {
	return func() error {
		lbStackName := common.CreateStackName(namespace, common.StackTypeLoadBalancer, environmentName)
		lbStack, _ := stackGetter.GetStack(lbStackName)

		clusterStackName := common.CreateStackName(namespace, common.StackTypeEnv, environmentName)
		clusterStack, _ := stackGetter.GetStack(clusterStackName)

		output := common.JSONOutput{}
		if lbStack != nil {
			output.Values[FirstValueIndex].Key = BaseURLKey
			output.Values[FirstValueIndex].Value = lbStack.Outputs[BaseURLValueKey]
		} else if clusterStack != nil {
			output.Values[FirstValueIndex].Key = BaseURLKey
			output.Values[FirstValueIndex].Value = clusterStack.Outputs[BaseURLValueKey]
		}

		for _, val := range output.Values {
			fmt.Fprintf(writer, "%s=%s\n", val.Key, val.Value)
		}

		return nil
	}
}

func (workflow *environmentWorkflow) environmentViewerCli(namespace string, environmentName string, stackGetter common.StackGetter, stackLister common.StackLister, clusterInstanceLister common.ClusterInstanceLister, instanceLister common.InstanceLister, taskManager common.TaskManager, kubernetesResourceManagerProvider common.KubernetesResourceManagerProvider, viewTasks bool, writer io.Writer) Executor {
	return func() error {
		lbStackName := common.CreateStackName(namespace, common.StackTypeLoadBalancer, environmentName)
		lbStack, err := stackGetter.GetStack(lbStackName)

		clusterStackName := common.CreateStackName(namespace, common.StackTypeEnv, environmentName)
		clusterStack, err := stackGetter.GetStack(clusterStackName)

		vpcStackName := common.CreateStackName(namespace, common.StackTypeVpc, environmentName)
		vpcStack, _ := stackGetter.GetStack(vpcStackName)

		fmt.Fprintf(writer, HeaderValueFormat, Bold(EnvironmentHeader), environmentName)
		if clusterStack != nil {
			fmt.Fprintf(writer, StackFormat, Bold(ClusterStack), clusterStack.Name, colorizeStackStatus(clusterStack.Status))
		}

		if vpcStack == nil {
			fmt.Fprintf(writer, UnmanagedStackFormat, Bold(VPCStack))
		} else {
			fmt.Fprintf(writer, StackFormat, Bold(VPCStack), vpcStack.Name, colorizeStackStatus(vpcStack.Status))
			fmt.Fprintf(writer, HeaderValueFormat, Bold(BastionHost), vpcStack.Outputs[BastionHostKey])
		}

		if lbStack != nil {
			fmt.Fprintf(writer, HeaderValueFormat, Bold(BaseURLHeader), lbStack.Outputs[BaseURLValueKey])
		} else if clusterStack != nil {
			fmt.Fprintf(writer, HeaderValueFormat, Bold(BaseURLHeader), clusterStack.Outputs[BaseURLValueKey])
		}

		if clusterStack != nil && clusterStack.Tags["provider"] == string(common.EnvProviderEcs) {
			fmt.Fprintf(writer, HeadNewlineHeader, Bold(ContainerInstances))
			containerInstances, err := clusterInstanceLister.ListInstances(clusterStack.Outputs[ECSClusterKey])
			if err != nil {
				return err
			}

			instanceIds := make([]string, len(containerInstances))
			for i, containerInstance := range containerInstances {
				instanceIds[i] = common.StringValue(containerInstance.Ec2InstanceId)
			}
			instances, err := instanceLister.ListInstances(instanceIds...)
			if err != nil {
				return err
			}

			instanceViews := buildInstanceViewForECS(containerInstances, instances)
			printInstanceTable(writer, instanceViews)
		} else if clusterStack != nil && clusterStack.Tags["provider"] == string(common.EnvProviderEks) {

			workflow.environment = &common.Environment{
				Name: environmentName,
			}
			workflow.connectKubernetes(namespace, kubernetesResourceManagerProvider)()

			nodes, err := workflow.kubernetesResourceManager.ListResources("v1", "Node", "")
			if err != nil {
				return err
			}
			instanceViews := buildInstanceViewForEKS(nodes)
			printInstanceTable(writer, instanceViews)
		}

		fmt.Fprint(writer, NewLine)
		fmt.Fprintf(writer, HeadNewlineHeader, Bold(ServicesHeader))
		stacks, err := stackLister.ListStacks(common.StackTypeService, namespace)
		if err != nil {
			return err
		}
		table := buildServiceTable(stacks, environmentName, writer)
		table.Render()

		if viewTasks {
			buildContainerTable(namespace, taskManager, stacks, environmentName, writer)
		}

		fmt.Fprint(writer, NewLine)

		return nil
	}
}

func buildContainerTable(namespace string, taskManager common.TaskManager, stacks []*common.Stack, environmentName string, writer io.Writer) {
	for _, stackValues := range stacks {
		if stackValues.Tags[EnvTagKey] != environmentName {
			continue
		}
		doViewTasks(namespace, taskManager, writer, stacks, stackValues.Tags[SvcTagKey])
	}
}

func buildServiceTable(stacks []*common.Stack, environmentName string, writer io.Writer) *tablewriter.Table {
	table := CreateTableSection(writer, ServiceTableHeader)

	for _, stackValues := range stacks {
		if stackValues.Tags[EnvTagKey] != environmentName {
			continue
		}

		table.Append([]string{
			Bold(stackValues.Tags[SvcTagKey]),
			stackValues.Tags["revision"],
			fmt.Sprintf(KeyValueFormat, colorizeStackStatus(stackValues.Status), stackValues.StatusReason),
			stackValues.LastUpdateTime.Local().Format(LastUpdateTime),
		})
	}

	return table
}

func buildInstanceViewForECS(containerInstances []common.ContainerInstance, instances []common.Instance) []*InstanceView {
	instanceIps := make(map[string]string)
	for _, instance := range instances {
		instanceIps[common.StringValue(instance.InstanceId)] = common.StringValue(instance.PrivateIpAddress)
	}

	instanceViews := make([]*InstanceView, len(containerInstances))

	for i, instance := range containerInstances {
		instanceType := UnknownValue
		availZone := UnknownValue
		amiID := UnknownValue
		for _, attr := range instance.Attributes {
			switch common.StringValue(attr.Name) {
			case ECSAvailabilityZoneKey:
				availZone = common.StringValue(attr.Value)
			case ECSInstanceTypeKey:
				instanceType = common.StringValue(attr.Value)
			case ECSAMIKey:
				amiID = common.StringValue(attr.Value)
			}
		}
		var cpuAvail int64
		var memAvail int64
		for _, resource := range instance.RemainingResources {
			switch common.StringValue(resource.Name) {
			case CPU:
				cpuAvail = common.Int64Value(resource.IntegerValue)
			case MEMORY:
				memAvail = common.Int64Value(resource.IntegerValue)
			}
		}
		instanceViews[i] = &InstanceView{
			common.StringValue(instance.Ec2InstanceId),
			instanceType,
			amiID,
			instanceIps[common.StringValue(instance.Ec2InstanceId)],
			availZone,
			common.BoolValue(instance.AgentConnected),
			common.StringValue(instance.Status),
			common.Int64Value(instance.RunningTasksCount),
			cpuAvail,
			memAvail,
		}
	}

	return instanceViews
}

func buildInstanceViewForEKS(nodes *unstructured.UnstructuredList) []*InstanceView {
	instanceViews := make([]*InstanceView, len(nodes.Items))
	for i, node := range nodes.Items {
		var ip string
		addresses := common.MapGetSlice(node.Object, "status", "addresses")
		for _, address := range addresses {
			if common.MapGetString(address, "type") == "InternalIP" {
				ip = common.MapGetString(address, "address")
			}
		}
		instanceViews[i] = &InstanceView{
			common.MapGetString(node.Object, "spec", "externalID"),
			"",
			common.MapGetString(node.Object, "status", "nodeInfo", "kernelVersion"),
			ip,
			common.MapGetString(node.Object, "metadata", "labels", "failure-domain.beta.kubernetes.io/zone"),
			true,
			"",
			0,
			0,
			0,
		}
	}

	return instanceViews
}

func printInstanceTable(writer io.Writer, instances []*InstanceView) {
	table := CreateTableSection(writer, EnvironmentAMITableHeader)

	for _, instance := range instances {
		table.Append([]string{
			instance.instanceID,
			instance.instanceType,
			instance.amiID,
			instance.instanceIP,
			instance.availZone,
			fmt.Sprintf(BoolStringFormat, instance.ready),
			instance.status,
			fmt.Sprintf(IntStringFormat, instance.taskCount),
			fmt.Sprintf(IntStringFormat, instance.cpuAvail),
			fmt.Sprintf(IntStringFormat, instance.memAvail),
		})
	}

	table.Render()
}

// InstanceView representation of view
type InstanceView struct {
	instanceID   string
	instanceType string
	amiID        string
	instanceIP   string
	availZone    string
	ready        bool
	status       string
	taskCount    int64
	cpuAvail     int64
	memAvail     int64
}
<|endoftext|>


<commit_before>package parallel

import (
	"errors"
	"io"
	"launchpad.net/tomb"
	"sync"
)

var (
	ErrStopped = errors.New("try was stopped")
	ErrClosed  = errors.New("try was closed")
)

type Try struct {
	tomb          tomb.Tomb
	closeMutex    sync.Mutex
	close         chan struct{}
	limiter       chan struct{}
	start         chan func()
	result        chan result
	combineErrors func(err0, err1 error) error
	maxParallel   int
	endResult     io.Closer
}

// NewTry returns an object that runs functions concurrently until one
// succeeds. The result of the first function that returns without an
// error is available from the Result method. If maxParallel is
// positive, it limits the nunber of concurrently running functions.
//
// The function combineErrors(oldErr, newErr) is called to determine
// the error return (see the Result method). The first time it is called,
// oldErr will be nil; subsequently oldErr will be the error previously
// returned by combineErrors. If combineErrors is nil, the last
// encountered error is chosen.
func NewTry(maxParallel int, combineErrors func(err0, err1 error) error) *Try {
	if combineErrors == nil {
		combineErrors = chooseLastError
	}
	t := &Try{
		combineErrors: combineErrors,
		maxParallel:   maxParallel,
		close:         make(chan struct{}, 1),
		result:        make(chan result),
		start:         make(chan func()),
	}
	if t.maxParallel > 0 {
		t.limiter = make(chan struct{}, t.maxParallel)
		for i := 0; i < t.maxParallel; i++ {
			t.limiter <- struct{}{}
		}
	}
	go func() {
		defer t.tomb.Done()
		val, err := t.loop()
		t.endResult = val
		t.tomb.Kill(err)
	}()
	return t
}

func chooseLastError(err0, err1 error) error {
	return err1
}

type result struct {
	val io.Closer
	err error
}

func (t *Try) loop() (io.Closer, error) {
	var err error
	closed := false
	nrunning := 0
	for {
		select {
		case f := <-t.start:
			nrunning++
			go f()
		case r := <-t.result:
			if r.err == nil {
				return r.val, r.err
			}
			err = t.combineErrors(err, r.err)
			nrunning--
			if closed && nrunning == 0 {
				return nil, err
			}
		case <-t.tomb.Dying():
			if err == nil {
				return nil, ErrStopped
			}
			return nil, err
		case <-t.close:
			closed = true
			if nrunning == 0 {
				return nil, err
			}
		}
	}
}

// Start requests the given function to be started, waiting until there
// are less than maxParallel functions running if necessary. It returns
// an error if the function has not been started (ErrClosed if the Try
// has been closed, and ErrStopped if the try is finishing).
//
// The function should listen on the stop channel and return if it
// receives a value, though this is advisory only - the Try does not
// wait for all started functions to return before completing.
//
// If the function returns a nil error but some earlier try was
// successful (that is, the returned value is being discarded),
// its returned value will be closed by calling its Close method.
func (t *Try) Start(try func(stop <-chan struct{}) (io.Closer, error)) error {
	if t.limiter != nil {
		// Wait for availability slot.
		select {
		case <-t.limiter:
		case <-t.tomb.Dying():
			return ErrStopped
		case <-t.close:
			return ErrClosed
		}
	}
	dying := t.tomb.Dying()
	f := func() {
		val, err := try(dying)
		if t.limiter != nil {
			// Signal availability slot is now free.
			t.limiter <- struct{}{}
		}
		// Deliver result.
		select {
		case t.result <- result{val, err}:
		case <-dying:
			if err == nil {
				val.Close()
			}
		}
	}
	select {
	case t.start <- f:
		return nil
	case <-dying:
		return ErrStopped
	case <-t.close:
		return ErrClosed
	}
}

// Close closes the Try. No more functions will be started
// if Start is called, and the Try will terminate when all
// outstanding functions have completed (or earlier
// if one succeeds)
func (t *Try) Close() {
	t.closeMutex.Lock()
	defer t.closeMutex.Unlock()
	select {
	case <-t.close:
	default:
		close(t.close)
	}
}

// Dead returns a channel that is closed when the
// Try completes.
func (t *Try) Dead() <-chan struct{} {
	return t.tomb.Dead()
}

// Wait waits for the Try to complete and returns the same
// error returned by Result.
func (t *Try) Wait() error {
	return t.tomb.Wait()
}

// Result waits for the Try to complete and returns the result of the
// first successful function started by Start.
//
// If no function succeeded, the last error returned by
// combineErrors is returned. If there were no errors or
// combineErrors returned nil, ErrStopped is returned.
func (t *Try) Result() (io.Closer, error) {
	err := t.tomb.Wait()
	return t.endResult, err
}

// Kill stops the try and all its currently executing functions.
func (t *Try) Kill() {
	t.tomb.Kill(nil)
}
<commit_msg>utils/parallel: fix close bug<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package parallel

import (
	"errors"
	"io"
	"sync"

	"launchpad.net/tomb"
)

var (
	ErrStopped = errors.New("try was stopped")
	ErrClosed  = errors.New("try was closed")
)

// Try represents an attempt made concurrently
// by a number of goroutines.
type Try struct {
	tomb          tomb.Tomb
	closeMutex    sync.Mutex
	close         chan struct{}
	limiter       chan struct{}
	start         chan func()
	result        chan result
	combineErrors func(err0, err1 error) error
	maxParallel   int
	endResult     io.Closer
}

// NewTry returns an object that runs functions concurrently until one
// succeeds. The result of the first function that returns without an
// error is available from the Result method. If maxParallel is
// positive, it limits the number of concurrently running functions.
//
// The function combineErrors(oldErr, newErr) is called to determine
// the error return (see the Result method). The first time it is called,
// oldErr will be nil; subsequently oldErr will be the error previously
// returned by combineErrors. If combineErrors is nil, the last
// encountered error is chosen.
func NewTry(maxParallel int, combineErrors func(err0, err1 error) error) *Try {
	if combineErrors == nil {
		combineErrors = chooseLastError
	}
	t := &Try{
		combineErrors: combineErrors,
		maxParallel:   maxParallel,
		close:         make(chan struct{}, 1),
		result:        make(chan result),
		start:         make(chan func()),
	}
	if t.maxParallel > 0 {
		t.limiter = make(chan struct{}, t.maxParallel)
		for i := 0; i < t.maxParallel; i++ {
			t.limiter <- struct{}{}
		}
	}
	go func() {
		defer t.tomb.Done()
		val, err := t.loop()
		t.endResult = val
		t.tomb.Kill(err)
	}()
	return t
}

func chooseLastError(err0, err1 error) error {
	return err1
}

type result struct {
	val io.Closer
	err error
}

func (t *Try) loop() (io.Closer, error) {
	var err error
	close := t.close
	nrunning := 0
	for {
		select {
		case f := <-t.start:
			nrunning++
			go f()
		case r := <-t.result:
			if r.err == nil {
				return r.val, r.err
			}
			err = t.combineErrors(err, r.err)
			nrunning--
			if close == nil && nrunning == 0 {
				return nil, err
			}
		case <-t.tomb.Dying():
			if err == nil {
				return nil, ErrStopped
			}
			return nil, err
		case <-close:
			close = nil
			if nrunning == 0 {
				return nil, err
			}
		}
	}
}

// Start requests the given function to be started, waiting until there
// are less than maxParallel functions running if necessary. It returns
// an error if the function has not been started (ErrClosed if the Try
// has been closed, and ErrStopped if the try is finishing).
//
// The function should listen on the stop channel and return if it
// receives a value, though this is advisory only - the Try does not
// wait for all started functions to return before completing.
//
// If the function returns a nil error but some earlier try was
// successful (that is, the returned value is being discarded),
// its returned value will be closed by calling its Close method.
func (t *Try) Start(try func(stop <-chan struct{}) (io.Closer, error)) error {
	if t.limiter != nil {
		// Wait for availability slot.
		select {
		case <-t.limiter:
		case <-t.tomb.Dying():
			return ErrStopped
		case <-t.close:
			return ErrClosed
		}
	}
	dying := t.tomb.Dying()
	f := func() {
		val, err := try(dying)
		if t.limiter != nil {
			// Signal availability slot is now free.
			t.limiter <- struct{}{}
		}
		// Deliver result.
		select {
		case t.result <- result{val, err}:
		case <-dying:
			if err == nil {
				val.Close()
			}
		}
	}
	select {
	case t.start <- f:
		return nil
	case <-dying:
		return ErrStopped
	case <-t.close:
		return ErrClosed
	}
}

// Close closes the Try. No more functions will be started
// if Start is called, and the Try will terminate when all
// outstanding functions have completed (or earlier
// if one succeeds)
func (t *Try) Close() {
	t.closeMutex.Lock()
	defer t.closeMutex.Unlock()
	select {
	case <-t.close:
	default:
		close(t.close)
	}
}

// Dead returns a channel that is closed when the
// Try completes.
func (t *Try) Dead() <-chan struct{} {
	return t.tomb.Dead()
}

// Wait waits for the Try to complete and returns the same
// error returned by Result.
func (t *Try) Wait() error {
	return t.tomb.Wait()
}

// Result waits for the Try to complete and returns the result of the
// first successful function started by Start.
//
// If no function succeeded, the last error returned by
// combineErrors is returned. If there were no errors or
// combineErrors returned nil, ErrStopped is returned.
func (t *Try) Result() (io.Closer, error) {
	err := t.tomb.Wait()
	return t.endResult, err
}

// Kill stops the try and all its currently executing functions.
func (t *Try) Kill() {
	t.tomb.Kill(nil)
}
<|endoftext|>


<commit_before>// +build linux

package kcp

import (
	"net"
	"sync/atomic"

	"github.com/pkg/errors"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

// the read loop for a client session
func (s *UDPSession) readLoop() {
	var src string
	msgs := make([]ipv4.Message, batchSize)
	for k := range msgs {
		msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
	}

	for {
		if count, err := s.xconn.ReadBatch(msgs, 0); err == nil {
			for i := 0; i < count; i++ {
				msg := &msgs[i]
				// make sure the packet is from the same source
				if src == "" { // set source address if nil
					src = msg.Addr.String()
				} else if msg.Addr.String() != src {
					atomic.AddUint64(&DefaultSnmp.InErrs, 1)
					continue
				}

				if msg.N < s.headerSize+IKCP_OVERHEAD {
					atomic.AddUint64(&DefaultSnmp.InErrs, 1)
					continue
				}

				// source and size has validated
				s.packetInput(msg.Buffers[0][:msg.N])
			}
		} else {
			s.notifyReadError(errors.WithStack(err))
			return
		}
	}
}

// monitor incoming data for all connections of server
func (l *Listener) monitor() {
	addr, _ := net.ResolveUDPAddr("udp", l.conn.LocalAddr().String())
	var xconn batchConn
	if addr.IP.To4() != nil {
		xconn = ipv4.NewPacketConn(l.conn)
	} else {
		xconn = ipv6.NewPacketConn(l.conn)
	}

	msgs := make([]ipv4.Message, batchSize)
	for k := range msgs {
		msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
	}

	for {
		if count, err := xconn.ReadBatch(msgs, 0); err == nil {
			for i := 0; i < count; i++ {
				msg := &msgs[i]
				if msg.N >= l.headerSize+IKCP_OVERHEAD {
					l.packetInput(msg.Buffers[0][:msg.N], msg.Addr)
				} else {
					atomic.AddUint64(&DefaultSnmp.InErrs, 1)
				}
			}
		} else {
			l.notifyReadError(errors.WithStack(err))
			return
		}
	}
}
<commit_msg>fix compile<commit_after>// +build linux

package kcp

import (
	"net"
	"sync/atomic"

	"github.com/pkg/errors"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

// the read loop for a client session
func (s *UDPSession) readLoop() {
	var src string
	msgs := make([]ipv4.Message, batchSize)
	for k := range msgs {
		msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
	}

	for {
		if count, err := s.xconn.ReadBatch(msgs, 0); err == nil {
			for i := 0; i < count; i++ {
				msg := &msgs[i]
				// make sure the packet is from the same source
				if src == "" { // set source address if nil
					src = msg.Addr.String()
				} else if msg.Addr.String() != src {
					atomic.AddUint64(&DefaultSnmp.InErrs, 1)
					continue
				}

				if msg.N < s.headerSize+IKCP_OVERHEAD {
					atomic.AddUint64(&DefaultSnmp.InErrs, 1)
					continue
				}

				// source and size has validated
				s.packetInput(msg.Buffers[0][:msg.N])
			}
		} else {
			s.notifyReadError(errors.WithStack(err))
			return
		}
	}
}

// monitor incoming data for all connections of server
func (l *Listener) monitor() {
	addr, err := net.ResolveUDPAddr("udp", l.conn.LocalAddr().String())
	var xconn batchConn
	if err != nil {
		xconn = ipv4.NewPacketConn(l.conn)
	} else if addr.IP.To4() != nil {
		xconn = ipv4.NewPacketConn(l.conn)
	} else {
		xconn = ipv6.NewPacketConn(l.conn)
	}

	msgs := make([]ipv4.Message, batchSize)
	for k := range msgs {
		msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
	}

	for {
		if count, err := xconn.ReadBatch(msgs, 0); err == nil {
			for i := 0; i < count; i++ {
				msg := &msgs[i]
				if msg.N >= l.headerSize+IKCP_OVERHEAD {
					l.packetInput(msg.Buffers[0][:msg.N], msg.Addr)
				} else {
					atomic.AddUint64(&DefaultSnmp.InErrs, 1)
				}
			}
		} else {
			l.notifyReadError(errors.WithStack(err))
			return
		}
	}
}
<|endoftext|>


<commit_before>package control

import (
	"testing"

	"github.com/golang/protobuf/proto"
	. "github.com/smartystreets/goconvey/convey"
)

var (
	scheduleA = &Schedule{
		Interval: []*Schedule_Interval{
			&Schedule_Interval{
				Begin: &Schedule_Time{
					Hour:   proto.Int32(9),
					Minute: proto.Int32(0),
				},
				End: &Schedule_Time{
					Hour:   proto.Int32(10),
					Minute: proto.Int32(30),
				},
				Type: Schedule_Interval_ON.Enum(),
			},
		},
	}

	scheduleB = &Schedule{
		Interval: []*Schedule_Interval{
			&Schedule_Interval{
				Begin: &Schedule_Time{
					Hour:   proto.Int32(9),
					Minute: proto.Int32(0),
				},
				End: &Schedule_Time{
					Hour:   proto.Int32(10),
					Minute: proto.Int32(30),
				},
				Type: Schedule_Interval_ON.Enum(),
			},
			&Schedule_Interval{
				Begin: &Schedule_Time{
					Hour:   proto.Int32(11),
					Minute: proto.Int32(0),
				},
				End: &Schedule_Time{
					Hour:   proto.Int32(12),
					Minute: proto.Int32(0),
				},
				Type: Schedule_Interval_OFF.Enum(),
			},
		},
	}
)

func TestBuildTree(t *testing.T) {
	Convey("Init", t, func() {
		tree := NewSchedule(scheduleA)
		So(tree, ShouldNotBeNil)
	})

	Convey("Contains", t, func() {
		tree := NewSchedule(scheduleB)
		So(tree, ShouldNotBeNil)
		So(tree.Query(9, 1), ShouldEqual, Schedule_Interval_ON)
		So(tree.Query(9, 0), ShouldEqual, Schedule_Interval_UNKNOWN)
		So(tree.Query(10, 29), ShouldEqual, Schedule_Interval_ON)
		So(tree.Query(10, 30), ShouldEqual, Schedule_Interval_UNKNOWN)
		So(tree.Query(11, 30), ShouldEqual, Schedule_Interval_OFF)
	})
}
<commit_msg>Fix tests<commit_after>package control

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var (
	scheduleA = &Schedule{
		Interval: []*Schedule_Interval{
			&Schedule_Interval{
				Begin: &Schedule_Time{
					Hour:   9,
					Minute: 0,
				},
				End: &Schedule_Time{
					Hour:   10,
					Minute: 30,
				},
				TargetTemperature: 15,
			},
		},
	}

	scheduleB = &Schedule{
		Interval: []*Schedule_Interval{
			&Schedule_Interval{
				Begin: &Schedule_Time{
					Hour:   9,
					Minute: 0,
				},
				End: &Schedule_Time{
					Hour:   10,
					Minute: 30,
				},
				TargetTemperature: 12,
			},
			&Schedule_Interval{
				Begin: &Schedule_Time{
					Hour:   11,
					Minute: 0,
				},
				End: &Schedule_Time{
					Hour:   12,
					Minute: 0,
				},
				TargetTemperature: 42,
			},
		},
	}
)

func TestBuildTree(t *testing.T) {
	Convey("Init", t, func() {
		tree := NewSchedule(scheduleA)
		So(tree, ShouldNotBeNil)
	})

	Convey("Contains", t, func() {
		tree := NewSchedule(scheduleB)
		So(tree, ShouldNotBeNil)
		So(tree.Query(9, 1), ShouldEqual, 12)
		So(tree.Query(9, 0), ShouldEqual, -1)
		So(tree.Query(10, 29), ShouldEqual, 12)
		So(tree.Query(10, 30), ShouldEqual, -1)
		So(tree.Query(11, 30), ShouldEqual, 42)
	})
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"net/http"

	"encoding/json"

	log "github.com/meifamily/logrus"
	"github.com/julienschmidt/httprouter"
	"github.com/meifamily/ptt-alertor/line"
	"github.com/meifamily/ptt-alertor/messenger"
	user "github.com/meifamily/ptt-alertor/models/user/redis"
)

type platformFunc map[string]func([]*user.User, string)

var ptFunc = platformFunc{
	"line":      broadcastLine,
	"messenger": broadcastMessenger,
}

func Broadcast(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	type requestBody struct {
		Platforms []string `json:"platforms"`
		Content   string   `json:"content"`
	}
	body := requestBody{}
	err := json.NewDecoder(r.Body).Decode(&body)
	if err != nil {
		log.WithError(err).Error("Decode Notify Body Failed")
	}
	users := new(user.User).All()
	for _, platform := range body.Platforms {
		if f, ok := ptFunc[platform]; ok {
			go f(users, body.Content)
		} else {
			http.Error(w, "platform "+platform+" is not valid", http.StatusBadRequest)
			return
		}
	}
	fmt.Fprintln(w, "OK")
}

func broadcastLine(users []*user.User, text string) {
	for _, user := range users {
		if user.Profile.LineAccessToken != "" {
			go line.Notify(user.Profile.LineAccessToken, text)
		}
	}
}

func broadcastMessenger(users []*user.User, text string) {
	m := messenger.New()
	for _, user := range users {
		if user.Profile.Messenger != "" {
			go m.SendTextMessage(user.Profile.Messenger, text)
		}
	}
}
<commit_msg>add duration on broadcast<commit_after>package controllers

import (
	"fmt"
	"net/http"

	"encoding/json"

	"time"

	"github.com/julienschmidt/httprouter"
	log "github.com/meifamily/logrus"
	"github.com/meifamily/ptt-alertor/line"
	"github.com/meifamily/ptt-alertor/messenger"
	user "github.com/meifamily/ptt-alertor/models/user/redis"
)

type platformFunc map[string]func([]*user.User, string)

var ptFunc = platformFunc{
	"line":      broadcastLine,
	"messenger": broadcastMessenger,
}

func Broadcast(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	type requestBody struct {
		Platforms []string `json:"platforms"`
		Content   string   `json:"content"`
	}
	body := requestBody{}
	err := json.NewDecoder(r.Body).Decode(&body)
	if err != nil {
		log.WithError(err).Error("Decode Notify Body Failed")
	}
	users := new(user.User).All()
	for _, platform := range body.Platforms {
		if f, ok := ptFunc[platform]; ok {
			go f(users, body.Content)
		} else {
			http.Error(w, "platform "+platform+" is not valid", http.StatusBadRequest)
			return
		}
	}
	fmt.Fprintln(w, "OK")
}

func broadcastLine(users []*user.User, text string) {
	for i, user := range users {
		if user.Profile.LineAccessToken != "" {
			go line.Notify(user.Profile.LineAccessToken, text)
		}
		if i%500 == 0 {
			time.Sleep(500 * time.Millisecond)
		}
	}
}

func broadcastMessenger(users []*user.User, text string) {
	m := messenger.New()
	for i, user := range users {
		if user.Profile.Messenger != "" {
			go m.SendTextMessage(user.Profile.Messenger, text)
		}
		if i%500 == 0 {
			time.Sleep(500 * time.Millisecond)
		}
	}
}
<|endoftext|>


<commit_before>// +build freebsd netbsd openbsd dragonfly darwin

package immortal

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"testing"
)

func TestNewScanDir(t *testing.T) {
	_, err := NewScanDir("/tmp")
	if err != nil {
		t.Error(err)
	}
}

func TestNewScanDirNonexistent(t *testing.T) {
	_, err := NewScanDir("/dev/null/non-existent")
	if err == nil {
		t.Error("Expecting error")
	}
}

type mockController struct {
	t      *testing.T
	i, j   int
	expect []struct {
		socket      string
		signal      []string
		signalErr   bool
		cmd, runErr string
	}
	status chan string
}

func (mc *mockController) GetStatus(socket string) (*Status, error) {
	status := &Status{}
	return status, nil
}

func (mc *mockController) SendSignal(socket, signal string) (*SignalResponse, error) {
	defer func() {
		mc.status <- signal
	}()
	return nil, nil
}

func (mc *mockController) FindServices(dir string) ([]*ServiceStatus, error) {
	return nil, nil
}

func (mc *mockController) PurgeServices(dir string) error {
	return nil
}

func (mc *mockController) Run(command string) ([]byte, error) {
	defer func() {
		mc.status <- "Run"
	}()
	return nil, nil
}

func TestScanner(t *testing.T) {
	dir, err := ioutil.TempDir("", "scaner")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir) // clean up
	s, err := NewScanDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	ctl := &mockController{
		status: make(chan string),
	}
	if err = ioutil.WriteFile(filepath.Join(dir, "run.yml"), []byte("stage 0"), 0644); err != nil {
		t.Fatal(err)
	}

	// start scanner loop
	go s.Start(ctl)

	var status string
	status = <-ctl.status
	expect(t, status, "Run")
	expect(t, "2bf41d668dd3b0909d58f982aff35a25", s.services["run"])

	// change service contents, a restart (exit, start) is expected
	if err = ioutil.WriteFile(filepath.Join(dir, "run.yml"), []byte("stage 1"), 0644); err != nil {
		t.Fatal(err)
	}
	status = <-ctl.status
	expect(t, status, "halt")
	status = <-ctl.status
	expect(t, status, "start")

	// remove service, exit
	if err := os.Remove(filepath.Join(dir, "run.yml")); err != nil {
		t.Fatal(err)
	}

}
<commit_msg>	modified:   scandir_test.go<commit_after>// +build freebsd netbsd openbsd dragonfly darwin

package immortal

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"testing"
)

func TestNewScanDir(t *testing.T) {
	_, err := NewScanDir("/tmp")
	if err != nil {
		t.Error(err)
	}
}

func TestNewScanDirNonexistent(t *testing.T) {
	_, err := NewScanDir("/dev/null/non-existent")
	if err == nil {
		t.Error("Expecting error")
	}
}

type mockController struct {
	t      *testing.T
	i, j   int
	expect []struct {
		socket      string
		signal      []string
		signalErr   bool
		cmd, runErr string
	}
	status chan string
}

func (mc *mockController) GetStatus(socket string) (*Status, error) {
	status := &Status{}
	return status, nil
}

func (mc *mockController) SendSignal(socket, signal string) (*SignalResponse, error) {
	defer func() {
		mc.status <- signal
	}()
	return nil, nil
}

func (mc *mockController) FindServices(dir string) ([]*ServiceStatus, error) {
	return nil, nil
}

func (mc *mockController) PurgeServices(dir string) error {
	return nil
}

func (mc *mockController) Run(command string) ([]byte, error) {
	defer func() {
		mc.status <- "Run"
	}()
	return nil, nil
}

func TestScanner(t *testing.T) {
	dir, err := ioutil.TempDir("", "scaner")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir) // clean up
	s, err := NewScanDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	ctl := &mockController{
		status: make(chan string),
	}

	// start scanner loop
	go s.Start(ctl)

	if err = ioutil.WriteFile(filepath.Join(dir, "run.yml"), []byte("stage 0"), 0644); err != nil {
		t.Fatal(err)
	}

	var status string
	status = <-ctl.status
	expect(t, status, "Run")
	expect(t, "2bf41d668dd3b0909d58f982aff35a25", s.services["run"])

	// change service contents, a restart (exit, start) is expected
	if err = ioutil.WriteFile(filepath.Join(dir, "run.yml"), []byte("stage 1"), 0644); err != nil {
		t.Fatal(err)
	}
	status = <-ctl.status
	expect(t, status, "halt")
	status = <-ctl.status
	expect(t, status, "start")

	// remove service, exit
	if err := os.Remove(filepath.Join(dir, "run.yml")); err != nil {
		t.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package main

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

type ScanResult struct {
	scan  bool
	text  string
	isErr bool
}

func ppResults(results []ScanResult) string {
	lines := make([]string, len(results))
	for i := 0; i < len(results); i++ {
		lines[i] = fmt.Sprintf("%#v", results[i])
		lines[i] = strings.TrimPrefix(lines[i], "main.ScanResult")
	}
	return strings.Join(lines, "\n")
}

type ScanTest struct {
	selector Selector
	src      string
	result   []ScanResult
}

type DummyAll struct {
	dropHeaders bool
}

func (d *DummyAll) DropHeaders() bool {
	return d.dropHeaders
}

func (d *DummyAll) ParseHeaders(headers []string) error {
	return nil
}

func (d *DummyAll) Select(recode []string) ([]string, error) {
	return recode, nil
}

var scanTests = []ScanTest{
	{
		selector: &DummyAll{dropHeaders: false},
		src: `
`[1:],
		result: []ScanResult{
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: false},
		src: `
1,1,1
2,4,8
`[1:],
		result: []ScanResult{
			{scan: true, text: "1\t1\t1", isErr: false},
			{scan: true, text: "2\t4\t8", isErr: false},
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: false},
		src: `
1,2,3
1,2,3,4,5,6
`[1:],
		result: []ScanResult{
			{scan: true, text: "1\t2\t3", isErr: false},
			{scan: false, text: "", isErr: true},
			{scan: false, text: "", isErr: true},
		},
	},
	{
		selector: &DummyAll{dropHeaders: true},
		src: `
`[1:],
		result: []ScanResult{
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: true},
		src: `
a,b,c
`[1:],
		result: []ScanResult{
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: true},
		src: `
a,b,c
1,1,1
2,4,8
`[1:],
		result: []ScanResult{
			{scan: true, text: "1\t1\t1", isErr: false},
			{scan: true, text: "2\t4\t8", isErr: false},
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: true},
		src: `
a,b,c
1,2,3
1,2,3,4,5,6
`[1:],
		result: []ScanResult{
			{scan: true, text: "1\t2\t3", isErr: false},
			{scan: false, text: "", isErr: true},
			{scan: false, text: "", isErr: true},
		},
	},
}

func TestScan(t *testing.T) {
	for _, test := range scanTests {
		r := strings.NewReader(test.src)
		c := NewCSVScanner(test.selector, r)

		expect := test.result
		actual := make([]ScanResult, len(test.result))
		for i := 0; i < len(test.result); i++ {
			scan := c.Scan()
			actual[i] = ScanResult{
				scan:  scan,
				text:  c.Text(),
				isErr: c.Err() != nil,
			}
		}
		if !reflect.DeepEqual(actual, expect) {
			t.Errorf("src:\n%sgot:\n%s\nwant:\n%s",
				test.src,
				ppResults(actual), ppResults(expect))
		}
	}
}

func TestBytes(t *testing.T) {
	selector := &DummyAll{dropHeaders: false}
	r := strings.NewReader("100,200,300\n")
	c := NewCSVScanner(selector, r)
	c.Scan()

	expect := c.Text()
	actual := string(c.Bytes())
	if actual != expect {
		t.Errorf("expected string(c.Byte()) to equal c.Text()")
	}
}

var delimiterTests = []struct {
	delimiter rune
	src       string
	dst       []string
}{
	{
		delimiter: ' ',
		src: `
aaa bbb ccc
ddd eee fff
`[1:],
		dst: []string{
			"aaa\tbbb\tccc",
			"ddd\teee\tfff",
		},
	},
	{
		delimiter: '/',
		src: `
aaa/bbb/ccc/ddd
eee/fff/ggg/hhh
`[1:],
		dst: []string{
			"aaa\tbbb\tccc\tddd",
			"eee\tfff\tggg\thhh",
		},
	},
}

func TestScanWithDelimiter(t *testing.T) {
	selector := &DummyAll{}
	for _, test := range delimiterTests {
		r := strings.NewReader(test.src)
		c := NewCSVScanner(selector, r)
		c.SetDelimiter(test.delimiter)

		expect := test.dst
		actual := make([]string, 0)
		for c.Scan() {
			actual = append(actual, c.Text())
		}
		if c.Err() != nil {
			t.Errorf("delimiter:%q\nsrc:\n%sgot: %v, want nil",
				test.delimiter, test.src, c.Err())
			continue
		}
		if !reflect.DeepEqual(actual, expect) {
			t.Errorf("delimiter:%q\nsrc:\n%sgot:\n%s\nwant:\n%s",
				test.delimiter,
				test.src,
				strings.Join(actual, "\n"),
				strings.Join(expect, "\n"))
		}
	}
}

var outputDelimiterTests = []struct {
	outputDelimiter string
	src             string
	dst             []string
}{
	{
		outputDelimiter: "...",
		src: `
aaa,bbb,ccc
ddd,eee,fff
`[1:],
		dst: []string{
			"aaa...bbb...ccc",
			"ddd...eee...fff",
		},
	},
	{
		outputDelimiter: "→",
		src: `
aaa,bbb,ccc,ddd
eee,fff,ggg,hhh
`[1:],
		dst: []string{
			"aaa→bbb→ccc→ddd",
			"eee→fff→ggg→hhh",
		},
	},
}

func TestScanWithOutputDelimiter(t *testing.T) {
	selector := &DummyAll{}
	for _, test := range outputDelimiterTests {
		r := strings.NewReader(test.src)
		c := NewCSVScanner(selector, r)
		c.SetOutputDelimiter(test.outputDelimiter)

		expect := test.dst
		actual := make([]string, 0)
		for c.Scan() {
			actual = append(actual, c.Text())
		}
		if c.Err() != nil {
			t.Errorf("output-delimiter:%q\nsrc:\n%s"+
				"got: %v, want nil",
				test.outputDelimiter, test.src, c.Err())
			continue
		}
		if !reflect.DeepEqual(actual, expect) {
			t.Errorf("output-delimiter:%q\nsrc:\n%s"+
				"got:\n%s\nwant:\n%s",
				test.outputDelimiter,
				test.src,
				strings.Join(actual, "\n"),
				strings.Join(expect, "\n"))
		}
	}
}
<commit_msg>Add test for error that occur at Selector.ParseHeaders<commit_after>package main

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

type ScanResult struct {
	scan  bool
	text  string
	isErr bool
}

func ppResults(results []ScanResult) string {
	lines := make([]string, len(results))
	for i := 0; i < len(results); i++ {
		lines[i] = fmt.Sprintf("%#v", results[i])
		lines[i] = strings.TrimPrefix(lines[i], "main.ScanResult")
	}
	return strings.Join(lines, "\n")
}

type ScanTest struct {
	selector Selector
	src      string
	result   []ScanResult
}

type DummyAll struct {
	dropHeaders              bool
	causeErrorAtParseHeaders bool
}

func (d *DummyAll) DropHeaders() bool {
	return d.dropHeaders
}

func (d *DummyAll) ParseHeaders(headers []string) error {
	if d.causeErrorAtParseHeaders {
		return fmt.Errorf("parse error")
	}
	return nil
}

func (d *DummyAll) Select(recode []string) ([]string, error) {
	return recode, nil
}

var scanTests = []ScanTest{
	{
		selector: &DummyAll{dropHeaders: false},
		src: `
`[1:],
		result: []ScanResult{
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: false},
		src: `
1,1,1
2,4,8
`[1:],
		result: []ScanResult{
			{scan: true, text: "1\t1\t1", isErr: false},
			{scan: true, text: "2\t4\t8", isErr: false},
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: false},
		src: `
1,2,3
1,2,3,4,5,6
`[1:],
		result: []ScanResult{
			{scan: true, text: "1\t2\t3", isErr: false},
			{scan: false, text: "", isErr: true},
			{scan: false, text: "", isErr: true},
		},
	},
	{
		selector: &DummyAll{dropHeaders: true},
		src: `
`[1:],
		result: []ScanResult{
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: true},
		src: `
a,b,c
`[1:],
		result: []ScanResult{
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: true},
		src: `
a,b,c
1,1,1
2,4,8
`[1:],
		result: []ScanResult{
			{scan: true, text: "1\t1\t1", isErr: false},
			{scan: true, text: "2\t4\t8", isErr: false},
			{scan: false, text: "", isErr: false},
		},
	},
	{
		selector: &DummyAll{dropHeaders: true},
		src: `
a,b,c
1,2,3
1,2,3,4,5,6
`[1:],
		result: []ScanResult{
			{scan: true, text: "1\t2\t3", isErr: false},
			{scan: false, text: "", isErr: true},
			{scan: false, text: "", isErr: true},
		},
	},
	{
		selector: &DummyAll{causeErrorAtParseHeaders: true},
		src: `
1,1,1
2,4,8
`[1:],
		result: []ScanResult{
			{scan: false, text: "", isErr: true},
			{scan: false, text: "", isErr: true},
			{scan: false, text: "", isErr: true},
		},
	},
}

func TestScan(t *testing.T) {
	for _, test := range scanTests {
		r := strings.NewReader(test.src)
		c := NewCSVScanner(test.selector, r)

		expect := test.result
		actual := make([]ScanResult, len(test.result))
		for i := 0; i < len(test.result); i++ {
			scan := c.Scan()
			actual[i] = ScanResult{
				scan:  scan,
				text:  c.Text(),
				isErr: c.Err() != nil,
			}
		}
		if !reflect.DeepEqual(actual, expect) {
			t.Errorf("src:\n%sgot:\n%s\nwant:\n%s",
				test.src,
				ppResults(actual), ppResults(expect))
		}
	}
}

func TestBytes(t *testing.T) {
	selector := &DummyAll{dropHeaders: false}
	r := strings.NewReader("100,200,300\n")
	c := NewCSVScanner(selector, r)
	c.Scan()

	expect := c.Text()
	actual := string(c.Bytes())
	if actual != expect {
		t.Errorf("expected string(c.Byte()) to equal c.Text()")
	}
}

var delimiterTests = []struct {
	delimiter rune
	src       string
	dst       []string
}{
	{
		delimiter: ' ',
		src: `
aaa bbb ccc
ddd eee fff
`[1:],
		dst: []string{
			"aaa\tbbb\tccc",
			"ddd\teee\tfff",
		},
	},
	{
		delimiter: '/',
		src: `
aaa/bbb/ccc/ddd
eee/fff/ggg/hhh
`[1:],
		dst: []string{
			"aaa\tbbb\tccc\tddd",
			"eee\tfff\tggg\thhh",
		},
	},
}

func TestScanWithDelimiter(t *testing.T) {
	selector := &DummyAll{}
	for _, test := range delimiterTests {
		r := strings.NewReader(test.src)
		c := NewCSVScanner(selector, r)
		c.SetDelimiter(test.delimiter)

		expect := test.dst
		actual := make([]string, 0)
		for c.Scan() {
			actual = append(actual, c.Text())
		}
		if c.Err() != nil {
			t.Errorf("delimiter:%q\nsrc:\n%sgot: %v, want nil",
				test.delimiter, test.src, c.Err())
			continue
		}
		if !reflect.DeepEqual(actual, expect) {
			t.Errorf("delimiter:%q\nsrc:\n%sgot:\n%s\nwant:\n%s",
				test.delimiter,
				test.src,
				strings.Join(actual, "\n"),
				strings.Join(expect, "\n"))
		}
	}
}

var outputDelimiterTests = []struct {
	outputDelimiter string
	src             string
	dst             []string
}{
	{
		outputDelimiter: "...",
		src: `
aaa,bbb,ccc
ddd,eee,fff
`[1:],
		dst: []string{
			"aaa...bbb...ccc",
			"ddd...eee...fff",
		},
	},
	{
		outputDelimiter: "→",
		src: `
aaa,bbb,ccc,ddd
eee,fff,ggg,hhh
`[1:],
		dst: []string{
			"aaa→bbb→ccc→ddd",
			"eee→fff→ggg→hhh",
		},
	},
}

func TestScanWithOutputDelimiter(t *testing.T) {
	selector := &DummyAll{}
	for _, test := range outputDelimiterTests {
		r := strings.NewReader(test.src)
		c := NewCSVScanner(selector, r)
		c.SetOutputDelimiter(test.outputDelimiter)

		expect := test.dst
		actual := make([]string, 0)
		for c.Scan() {
			actual = append(actual, c.Text())
		}
		if c.Err() != nil {
			t.Errorf("output-delimiter:%q\nsrc:\n%s"+
				"got: %v, want nil",
				test.outputDelimiter, test.src, c.Err())
			continue
		}
		if !reflect.DeepEqual(actual, expect) {
			t.Errorf("output-delimiter:%q\nsrc:\n%s"+
				"got:\n%s\nwant:\n%s",
				test.outputDelimiter,
				test.src,
				strings.Join(actual, "\n"),
				strings.Join(expect, "\n"))
		}
	}
}
<|endoftext|>