<commit_before>package commands

import (
	"fmt"

	"github.com/ambientsound/gompd/mpd"
	"github.com/ambientsound/pms/input/lexer"
	"github.com/ambientsound/pms/song"
	"github.com/ambientsound/pms/songlist"
	"github.com/ambientsound/pms/widgets"
)

// Add adds songs to MPD's queue.
type Add struct {
	messages       chan string
	songlistWidget func() *widgets.SonglistWidget
	queue          func() *songlist.Queue
	song           *song.Song
	songlist       songlist.Songlist
}

func NewAdd(messages chan string, songlistWidget func() *widgets.SonglistWidget, queue func() *songlist.Queue) *Add {
	return &Add{
		messages:       messages,
		songlistWidget: songlistWidget,
		queue:          queue,
	}
}

func (cmd *Add) Reset() {
	cmd.song = nil
	cmd.songlist = nil
}

func (cmd *Add) Execute(t lexer.Token) error {
	var err error

	switch t.Class {
	case lexer.TokenIdentifier:
		if cmd.song != nil {
			return fmt.Errorf("Cannot add multiple paths on the same command line.")
		}
		cmd.song = song.New()
		cmd.song.SetTags(mpd.Attrs{
			"file": t.String(),
		})

	case lexer.TokenEnd:
		songlistWidget := cmd.songlistWidget()
		queue := cmd.queue()

		switch {
		case cmd.song == nil:
			selection := songlistWidget.Selection()
			if selection.Len() == 0 {
				return fmt.Errorf("No selection, cannot add without any parameters.")
			}
			err := queue.AddList(selection)
			if err != nil {
				break
			}
			if err == nil {
				songlistWidget.ClearSelection()
				songlistWidget.MoveCursor(1)
				len := selection.Len()
				if len == 1 {
					song := selection.Songs()[0]
					cmd.messages <- fmt.Sprintf("Added to queue: %s", song.StringTags["file"])
				} else {
					cmd.messages <- fmt.Sprintf("Added %d songs to queue.", len)
				}
			}

		default:
			err = queue.Add(cmd.song)
			if err == nil {
				cmd.messages <- fmt.Sprintf("Added to queue: %s", cmd.song.StringTags["file"])
			}
		}

	default:
		return fmt.Errorf("Unknown input '%s', expected END", string(t.Runes))
	}

	return err
}
<commit_msg>Optimize code in 'add' command<commit_after>package commands

import (
	"fmt"

	"github.com/ambientsound/gompd/mpd"
	"github.com/ambientsound/pms/input/lexer"
	"github.com/ambientsound/pms/song"
	"github.com/ambientsound/pms/songlist"
	"github.com/ambientsound/pms/widgets"
)

// Add adds songs to MPD's queue.
type Add struct {
	messages       chan string
	songlistWidget func() *widgets.SonglistWidget
	queue          func() *songlist.Queue
	song           *song.Song
	songlist       songlist.Songlist
}

func NewAdd(messages chan string, songlistWidget func() *widgets.SonglistWidget, queue func() *songlist.Queue) *Add {
	return &Add{
		messages:       messages,
		songlistWidget: songlistWidget,
		queue:          queue,
	}
}

func (cmd *Add) Reset() {
	cmd.song = nil
	cmd.songlist = nil
}

func (cmd *Add) Execute(t lexer.Token) error {
	var err error

	switch t.Class {
	case lexer.TokenIdentifier:
		if cmd.song != nil {
			return fmt.Errorf("Cannot add multiple paths on the same command line.")
		}
		cmd.song = song.New()
		cmd.song.SetTags(mpd.Attrs{
			"file": t.String(),
		})

	case lexer.TokenEnd:
		songlistWidget := cmd.songlistWidget()
		queue := cmd.queue()

		switch {
		case cmd.song == nil:
			selection := songlistWidget.Selection()
			if selection.Len() == 0 {
				return fmt.Errorf("No selection, cannot add without any parameters.")
			}
			err = queue.AddList(selection)
			if err != nil {
				break
			}
			songlistWidget.ClearSelection()
			songlistWidget.MoveCursor(1)
			len := selection.Len()
			if len == 1 {
				song := selection.Songs()[0]
				cmd.messages <- fmt.Sprintf("Added to queue: %s", song.StringTags["file"])
			} else {
				cmd.messages <- fmt.Sprintf("Added %d songs to queue.", len)
			}

		default:
			err = queue.Add(cmd.song)
			if err == nil {
				cmd.messages <- fmt.Sprintf("Added to queue: %s", cmd.song.StringTags["file"])
			}
		}

	default:
		return fmt.Errorf("Unknown input '%s', expected END", string(t.Runes))
	}

	return err
}
<|endoftext|>


<commit_before>package instagram

import (
	"fmt"
	"net/http"
	"net/url"
)

type RealtimeService struct {
	client *Client
}

// Realtime represents a realtime subscription on Instagram's service.
type Realtime struct {
	ID          string `json:"id,omitempty"`
	Type        string `json:"type,omitempty"`
	Object      string `json:"object,omitempty"`
	ObjectID    string `json:"object_id,omitempty"`
	Aspect      string `json:"aspect,omitempty"`
	CallbackURL string `json:"callback_url,omitempty"`
}

// MediaComments gets a full list of comments on a media.
//
// Instagram API docs: http://instagram.com/developer/endpoints/comments/#get_media_comments
func (s *RealtimeService) SubscribeToTag(tag, callbackURL, verifyToken string) (*Realtime, error) {
	u := "subscriptions/"

	params := url.Values{
		"aspect":        {"media"},
		"object":        {"tag"},
		"object_id":     {tag},
		"callback_url":  {callbackURL},
		"client_id":     {s.client.ClientID},
		"client_secret": {s.client.ClientSecret},
		"verify_token":  {verifyToken},
	}

	req, err := s.client.NewRequest("POST", u, params.Encode())
	if err != nil {
		return nil, err
	}

	realtime := new(Realtime)

	_, err = s.client.Do(req, realtime)
	if err != nil {
		return nil, err
	}

	return realtime, err
}

//An example RealTimeSubscribe ResponseWriter. This can be plugged directly into
// any standard http server. Note, however, that this particular implementation does
// no checking that the verifyToken is correct.
func ServeInstagramRealtimeSubscribe(w http.ResponseWriter, r *http.Request) {
	//You would think this would be "PostFormValue", but Instagram is sending the
	// challenge and response data as query strings in a POST, not as POST elements in a POST
	verify := r.FormValue("hub.challenge")

	fmt.Fprintf(w, verify)
}
<commit_msg>Adding a method to check active subscriptions<commit_after>package instagram

import (
	"fmt"
	"net/http"
	"net/url"
)

type RealtimeService struct {
	client *Client
}

// Realtime represents a realtime subscription on Instagram's service.
type Realtime struct {
	ID          string `json:"id,omitempty"`
	Type        string `json:"type,omitempty"`
	Object      string `json:"object,omitempty"`
	ObjectID    string `json:"object_id,omitempty"`
	Aspect      string `json:"aspect,omitempty"`
	CallbackURL string `json:"callback_url,omitempty"`
}

type RealtimeResponse struct {
	SubscriptionID int64  `json:"subscription_id,omitempty"`
	Object         string `json:"object,omitempty"`
	ObjectID       string `json:"object_id,omitempty"`
	ChangedAspect  string `json:"changed_aspect,omitempty"`
	Time           int64  `json:"time,omitempty"`
}

//ListSubscriptions ists the realtime subscriptions that are already active for your account
func (s *RealtimeService) ListSubscriptions() ([]Realtime, error) {
	u := "subscriptions/"

	req, err := s.client.NewRequest("GET", u, "")
	if err != nil {
		return nil, err
	}

	realtime := new([]Realtime)

	_, err = s.client.Do(req, realtime)
	if err != nil {
		return nil, err
	}

	return *realtime, err
}

// SubscribeToTag initiates the subscription to realtime updates about tag `tag`
//
// Instagram API docs: http://instagram.com/developer/realtime/
func (s *RealtimeService) SubscribeToTag(tag, callbackURL, verifyToken string) (*Realtime, error) {
	u := "subscriptions/"

	params := url.Values{
		"aspect":        {"media"},
		"object":        {"tag"},
		"object_id":     {tag},
		"callback_url":  {callbackURL},
		"client_id":     {s.client.ClientID},
		"client_secret": {s.client.ClientSecret},
		"verify_token":  {verifyToken},
	}

	req, err := s.client.NewRequest("POST", u, params.Encode())
	if err != nil {
		return nil, err
	}

	realtime := new(Realtime)

	_, err = s.client.Do(req, realtime)
	if err != nil {
		return nil, err
	}

	return realtime, err
}

//An example RealTimeSubscribe ResponseWriter. This can be plugged directly into
// any standard http server. Note, however, that this particular implementation does
// no checking that the verifyToken is correct.
func ServeInstagramRealtimeSubscribe(w http.ResponseWriter, r *http.Request) {
	verify := r.FormValue("hub.challenge")

	fmt.Fprintf(w, verify)
}
<|endoftext|>


<commit_before>package ringo

import (
	"runtime"
	"sync/atomic"
)

// MultiPublishNode is shared by multiple thread/go routines for publishing events to the ring buffer.
// Because multiple routines must compete for next index, a single lock is maintained.
type MultiPublishNode struct {
	cachepad1  [8]int64
	sequence   int64 // Write counter and index to the next ring buffer entry.
	cachepad2  [8]int64
	committed  int64 // Keeps track of the number of written events to the ring.
	cachepad3  [8]int64
	dependency *int64 // The consumer's committed register we are dependent to finish before proceeding.
	cachepad4  [8]int64
	buffSize   int64 // Size of the ring buffer.
	cachepad5  [8]int64
}

// Factory function for returning a new instance of a MultiPublishNode.
func MultiPublishNodeNew(size int64) *MultiPublishNode {
	return &MultiPublishNode{
		buffSize: size,
	}
}

// Reserve returns the next new index.
func (m *MultiPublishNode) Reserve() int64 {
	for {
		previous := m.sequence // Get the previous counter.
		next := previous + 1   // and increment it. Hold it for later.
		// Wait for room in the buffer if it is full.
		for previous-*m.dependency == m.buffSize {
			runtime.Gosched()
		}
		// Try and store the new increment. If it was changed by another routine, loop and try again.
		if atomic.CompareAndSwapInt64(&m.sequence, previous, next) {
			return previous
		}
	}
}

// Commit increments the commit register to indicate an entry has been stored.
func (m *MultiPublishNode) Commit() {
	m.committed++
}

// Committed returns a pointer to the committed counter.
func (m *MultiPublishNode) Committed() *int64 {
	return &m.committed
}

// SetDependency set the commtted counter that must complete work before we can proceed.
func (m *MultiPublishNode) SetDependency(d *int64) {
	m.dependency = d
}
<commit_msg>minor tuning<commit_after>package ringo

import (
	"runtime"
	"sync/atomic"
)

// MultiPublishNode is shared by multiple thread/go routines for publishing events to the ring buffer.
// Because multiple routines must compete for next index, a single lock is maintained.
type MultiPublishNode struct {
	cachepad1  [8]int64
	sequence   int64 // Write counter and index to the next ring buffer entry.
	cachepad2  [8]int64
	committed  int64 // Keeps track of the number of written events to the ring.
	cachepad3  [8]int64
	dependency *int64 // The consumer's committed register we are dependent to finish before proceeding.
	cachepad4  [8]int64
	buffSize   int64 // Size of the ring buffer.
	cachepad5  [8]int64
}

// Factory function for returning a new instance of a MultiPublishNode.
func MultiPublishNodeNew(size int64) *MultiPublishNode {
	return &MultiPublishNode{
		buffSize: size,
	}
}

// Reserve returns the next new index.
func (m *MultiPublishNode) Reserve() int64 {
	for {
		previous := m.sequence // Get the previous counter.
		// Wait for room in the buffer if it is full.
		for previous-*m.dependency == m.buffSize {
			runtime.Gosched()
		}
		// Try and store the new increment. If it was changed by another routine, loop and try again.
		if atomic.CompareAndSwapInt64(&m.sequence, previous, previous+1) {
			return previous
		}
	}
}

// Commit increments the commit register to indicate an entry has been stored.
func (m *MultiPublishNode) Commit() {
	m.committed++
}

// Committed returns a pointer to the committed counter.
func (m *MultiPublishNode) Committed() *int64 {
	return &m.committed
}

// SetDependency set the commtted counter that must complete work before we can proceed.
func (m *MultiPublishNode) SetDependency(d *int64) {
	m.dependency = d
}
<|endoftext|>


<commit_before>package checkers

import (
	"testing"

	"github.com/cl0ne/go-checkers/point"
)

func TestNewBoard(t *testing.T) {
	uselessBoard := NewBoard(0)
	if uselessBoard == nil {
		t.Error("Zero-sized boards aren't very useful, but got nil.")
	}
	if uselessBoard.Size() != 0 {
		t.Error("Zero-sized board after creation got size of", uselessBoard.Size())
	}

	invalidBoard := NewBoard(-1)
	if invalidBoard != nil {
		t.Error("Boards of negative size not allowed, got size of", invalidBoard.Size())
	}

	board8x8 := NewBoard(8)
	if board8x8 == nil {
		t.Error("Can't create 8x8 board got nil.")
	}
	if board8x8.Size() != 8 {
		t.Error("Size of created 8x8 board is", board8x8.Size())
	}
}

func TestBoardOperations(t *testing.T) {
	board := NewBoard(8)
	t.Run("IsEmpty", func(t *testing.T) {
		checker := newChecker(3, 3, true)
		p := point.Point{X: 3, Y: 3}
		board.placeChecker(p.X, p.Y, &checker)
		if board.IsEmpty(p.X, p.Y) {
			t.Error("Cell", p, "shouldn't be empty.")
		}
		p.X, p.Y = 4, 6
		if !board.IsEmpty(p.X, p.Y) {
			t.Error("Cell", p, "should be empty.")
		}
	})

	t.Run("ContainsPos", func(t *testing.T) {
		point := point.Point{X: 7, Y: 8}
		contains := board.ContainsPos(7, 8)
		if contains {
			t.Error("Cell", point, "is outside of the board.")
		}
		point.X, point.Y = 3, 2
		contains = board.ContainsPos(3, 2)
		if !contains {
			t.Error("Cell", point, "is within the board.")
		}
	})

	whiteCell := point.Point{X: 2, Y: 1}
	blackCell := point.Point{X: 4, Y: 0}

	t.Run("IsBlackSquare", func(t *testing.T) {
		if !board.IsBlackSquare(blackCell) {
			t.Error("Cell", blackCell, "must be black.")
		}
		if board.IsBlackSquare(whiteCell) {
			t.Error("Cell", whiteCell, "must be white.")
		}
	})

	t.Run("IsWhiteSquare", func(t *testing.T) {
		if !board.IsWhiteSquare(whiteCell) {
			t.Error("Cell", whiteCell, "must be white.")
		}
		if board.IsWhiteSquare(blackCell) {
			t.Error("Cell", blackCell, "must be black.")
		}
	})

	t.Run("TakeChecker", func(t *testing.T) {
		p := point.Point{X: 2, Y: 2}
		checker := newChecker(p.X, p.Y, true)
		board.placeChecker(p.X, p.Y, &checker)
		c := board.takeChecker(p.X, p.Y)
		if c == nil {
			t.Error("Can't take checker at", p, ": cell is empty.")
		}
	})

	t.Run("GetChecker", func(t *testing.T) {
		p := point.Point{X: 2, Y: 4}
		checker := newChecker(p.X, p.Y, false)
		board.placeChecker(p.X, p.Y, &checker)
		c := board.GetChecker(p.X, p.Y)
		if c == nil {
			t.Error("Checker at", p, "is missing.")
		}
		p.X, p.Y = 5, 5
		c = board.GetChecker(p.X, p.Y)
		if c != nil {
			t.Error("Cell", p, "should be empty")
		}
	})

	t.Run("MoveChecker", func(t *testing.T) {
		from := point.Point{X: 3, Y: 4}
		to := point.Point{X: 4, Y: 3}
		board.moveChecker(from, to)
		c := board.GetChecker(3, 4)
		if c != nil {
			t.Error("Checker wasn't moved from", from, "to", to)
		}
	})
}
<commit_msg>Fix test of moving checker<commit_after>package checkers

import (
	"testing"

	"github.com/cl0ne/go-checkers/point"
)

func TestNewBoard(t *testing.T) {
	uselessBoard := NewBoard(0)
	if uselessBoard == nil {
		t.Error("Zero-sized boards aren't very useful, but got nil.")
	}
	if uselessBoard.Size() != 0 {
		t.Error("Zero-sized board after creation got size of", uselessBoard.Size())
	}

	invalidBoard := NewBoard(-1)
	if invalidBoard != nil {
		t.Error("Boards of negative size not allowed, got size of", invalidBoard.Size())
	}

	board8x8 := NewBoard(8)
	if board8x8 == nil {
		t.Error("Can't create 8x8 board got nil.")
	}
	if board8x8.Size() != 8 {
		t.Error("Size of created 8x8 board is", board8x8.Size())
	}
}

func TestBoardOperations(t *testing.T) {
	board := NewBoard(8)
	t.Run("IsEmpty", func(t *testing.T) {
		checker := newChecker(3, 3, true)
		p := point.Point{X: 3, Y: 3}
		board.placeChecker(p.X, p.Y, &checker)
		if board.IsEmpty(p.X, p.Y) {
			t.Error("Cell", p, "shouldn't be empty.")
		}
		p.X, p.Y = 4, 6
		if !board.IsEmpty(p.X, p.Y) {
			t.Error("Cell", p, "should be empty.")
		}
	})

	t.Run("ContainsPos", func(t *testing.T) {
		point := point.Point{X: 7, Y: 8}
		contains := board.ContainsPos(7, 8)
		if contains {
			t.Error("Cell", point, "is outside of the board.")
		}
		point.X, point.Y = 3, 2
		contains = board.ContainsPos(3, 2)
		if !contains {
			t.Error("Cell", point, "is within the board.")
		}
	})

	whiteCell := point.Point{X: 2, Y: 1}
	blackCell := point.Point{X: 4, Y: 0}

	t.Run("IsBlackSquare", func(t *testing.T) {
		if !board.IsBlackSquare(blackCell) {
			t.Error("Cell", blackCell, "must be black.")
		}
		if board.IsBlackSquare(whiteCell) {
			t.Error("Cell", whiteCell, "must be white.")
		}
	})

	t.Run("IsWhiteSquare", func(t *testing.T) {
		if !board.IsWhiteSquare(whiteCell) {
			t.Error("Cell", whiteCell, "must be white.")
		}
		if board.IsWhiteSquare(blackCell) {
			t.Error("Cell", blackCell, "must be black.")
		}
	})

	t.Run("TakeChecker", func(t *testing.T) {
		p := point.Point{X: 2, Y: 2}
		checker := newChecker(p.X, p.Y, true)
		board.placeChecker(p.X, p.Y, &checker)
		c := board.takeChecker(p.X, p.Y)
		if c == nil {
			t.Error("Can't take checker at", p, ": cell is empty.")
		}
	})

	t.Run("GetChecker", func(t *testing.T) {
		p := point.Point{X: 2, Y: 4}
		checker := newChecker(p.X, p.Y, false)
		board.placeChecker(p.X, p.Y, &checker)
		c := board.GetChecker(p.X, p.Y)
		if c == nil {
			t.Error("Checker at", p, "is missing.")
		}
		p.X, p.Y = 5, 5
		c = board.GetChecker(p.X, p.Y)
		if c != nil {
			t.Error("Cell", p, "should be empty")
		}
	})

	t.Run("MoveChecker", func(t *testing.T) {
		from := point.Point{X: 3, Y: 4}
    checker := newChecker(from.X, from.Y, true)
		to := point.Point{X: 4, Y: 3}
    board.placeChecker(from.X, from.Y, &checker)
		board.moveChecker(from, to)
		c := board.GetChecker(3, 4)
		if c != nil {
			t.Error("Checker wasn't moved from", from, "to", to)
		}
	})
}
<|endoftext|>


<commit_before>/**
 * Requirements:
 * 	libreadline-dev
 */
package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/coreos/go-etcd/etcd"
	"github.com/headzoo/etcdsh/config"
	"github.com/headzoo/etcdsh/handlers"
)

// Main method.
func main() {
	config := config.New()

	help := flag.Bool("help", false, "Prints command line options and exit.")
	version := flag.Bool("version", false, "Prints the etcdsh version and exit.")
	flag.StringVar(&config.Machine, "machine", config.Machine, "Connect to this etcd server.")
	flag.BoolVar(&config.Colors, "colors", config.Colors, "Use colors in display.")
	flag.Parse()

	if *help {
		printHelp()
		os.Exit(0)
	}
	if *version {
		printVersion()
		os.Exit(0)
	}

	fmt.Printf("Connecting to %s\n", config.Machine)
	client := etcd.NewClient([]string{config.Machine})

	controller := handlers.NewController(config, client, os.Stdout, os.Stderr, os.Stdin)
	controller.Add(handlers.NewLsHandler(controller))
	controller.Add(handlers.NewSetHandler(controller))
	controller.Add(handlers.NewHelpHandler(controller))
	controller.Add(handlers.NewCdHandler(controller))
	controller.Add(handlers.NewGetHandler(controller))
	code := controller.Start()

	os.Exit(code)
}

// printHelp prints the command line help information.
func printHelp() {
	printVersion()
	fmt.Println("USAGE:")
	fmt.Println("\tetcdsh [OPTIONS]")

	fmt.Println("")
	fmt.Println("OPTIONS:")
	flag.VisitAll(func(f *flag.Flag) {
		fmt.Printf("\t-%-10s%s\n", f.Name, f.Usage)
	})

	fmt.Println("")
	fmt.Println("EXAMPLES:")
	fmt.Println("\tetcdsh -machine='http://192.168.1.23:4001'")

	fmt.Println("")
}

// printVersion prints the app version information.
func printVersion() {
	fmt.Printf("etcdsh %s - An interactive shell for the etcd server.\n\n", handlers.Version)
}
<commit_msg>Added -ps1 and -ps2 options<commit_after>/**
 * Requirements:
 * 	libreadline-dev
 */
package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/coreos/go-etcd/etcd"
	"github.com/headzoo/etcdsh/config"
	"github.com/headzoo/etcdsh/handlers"
)

// Main method.
func main() {
	conf := config.New()

	help := flag.Bool("help", false, "Prints command line options and exit.")
	version := flag.Bool("version", false, "Prints the etcdsh version and exit.")
	flag.StringVar(&conf.Machine, "machine", conf.Machine, "Connect to this etcd server.")
	flag.StringVar(&conf.PS1, "ps1", conf.PS1, "First prompt format")
	flag.StringVar(&conf.PS2, "ps2", conf.PS2, "Second prompt format")
	flag.BoolVar(&conf.Colors, "colors", conf.Colors, "Use colors in display.")
	flag.Parse()

	if *help {
		printHelp()
		os.Exit(0)
	}
	if *version {
		printVersion()
		os.Exit(0)
	}

	fmt.Printf("Connecting to %s\n", conf.Machine)
	client := etcd.NewClient([]string{conf.Machine})

	controller := handlers.NewController(conf, client, os.Stdout, os.Stderr, os.Stdin)
	controller.Add(handlers.NewLsHandler(controller))
	controller.Add(handlers.NewSetHandler(controller))
	controller.Add(handlers.NewHelpHandler(controller))
	controller.Add(handlers.NewCdHandler(controller))
	controller.Add(handlers.NewGetHandler(controller))
	code := controller.Start()

	os.Exit(code)
}

// printHelp prints the command line help information.
func printHelp() {
	printVersion()
	fmt.Println("USAGE:")
	fmt.Println("\tetcdsh [OPTIONS]")

	fmt.Println("")
	fmt.Println("OPTIONS:")
	flag.VisitAll(func(f *flag.Flag) {
		fmt.Printf("\t-%-10s%s\n", f.Name, f.Usage)
	})

	fmt.Println("")
	fmt.Println("EXAMPLES:")
	fmt.Println("\tetcdsh -machine='http://192.168.1.23:4001'")

	fmt.Println("")
}

// printVersion prints the app version information.
func printVersion() {
	fmt.Printf("etcdsh %s - An interactive shell for the etcd server.\n\n", handlers.Version)
}
<|endoftext|>


<commit_before>// Minimalistic BDD-style assertions for Go (inspired by expect.js)
package expect

import "testing"

type Expect struct {
	To  *To
	Not *not
}

type not struct {
	To *To
}

// Return new expect function with `To, To.Be, To.Have` assertions
func New(t *testing.T) func(v interface{}) *Expect {
	return func(v interface{}) *Expect {
		// Be, Not.Be
		var be, nbe Be
		be = Be{t, &be, v, true}
		nbe = Be{t, &nbe, v, false}
		// Have, Not.Have
		var have, nhave Have
		have = Have{t, &have, v, true}
		nhave = Have{t, &nhave, v, false}
		// To, Not.To
		var to, nto To
		to = To{t, &be, &have, &to, v, true}
		nto = To{t, &nbe, &nhave, &nto, v, false}
		return &Expect{
			To:  &to,
			Not: &not{&nto},
		}
	}
}
<commit_msg>Export Not Type<commit_after>// Minimalistic BDD-style assertions for Go (inspired by expect.js)
package expect

import "testing"

type Expect struct {
	To  *To
	Not *Not
}

type Not struct {
	To *To
}

// Return new expect function with `To, To.Be, To.Have` assertions
func New(t *testing.T) func(v interface{}) *Expect {
	return func(v interface{}) *Expect {
		// Be, Not.Be
		var be, nbe Be
		be = Be{t, &be, v, true}
		nbe = Be{t, &nbe, v, false}
		// Have, Not.Have
		var have, nhave Have
		have = Have{t, &have, v, true}
		nhave = Have{t, &nhave, v, false}
		// To, Not.To
		var to, nto To
		to = To{t, &be, &have, &to, v, true}
		nto = To{t, &nbe, &nhave, &nto, v, false}
		return &Expect{
			To:  &to,
			Not: &Not{&nto},
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gamelost/bot3server/module/fight"
	"github.com/gamelost/bot3server/module/mongo"
	// "github.com/gamelost/bot3server/module/logger"
	"github.com/gamelost/bot3server/module/help"
	"github.com/gamelost/bot3server/module/inconceivable"
	"github.com/gamelost/bot3server/module/slap"
	"github.com/gamelost/bot3server/server"
	//"github.com/gamelost/bot3server/module/sleep"
	"github.com/gamelost/bot3server/module/cah"
	"github.com/gamelost/bot3server/module/nextwedding"
	// "github.com/gamelost/bot3server/module/panic"
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	"github.com/gamelost/bot3server/module/boulderingtime"
	"github.com/gamelost/bot3server/module/catfacts"
	"github.com/gamelost/bot3server/module/dice"
	"github.com/gamelost/bot3server/module/remindme"
	wuconditions "github.com/gamelost/bot3server/module/weather/conditions"
	wuforecast "github.com/gamelost/bot3server/module/weather/forecast"
	"github.com/gamelost/bot3server/module/zed"
	nsq "github.com/gamelost/go-nsq"
	"github.com/twinj/uuid"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

const DEFAULT_CONFIG_FILENAME = "bot3server.config"
const CONFIG_CAT_DEFAULT = "default"
const CONFIG_CAT_PLUGINS = "plugins"

// nsq specific constants
const CONFIG_CAT_NSQ = "nsq"
const CONFIG_BOT3SERVER_INPUT = "bot3server-input"
const CONFIG_BOT3SERVER_OUTPUT = "bot3server-output"
const CONFIG_OUTPUT_WRITER_ADDR = "output-writer-address"
const CONFIG_LOOKUPD_ADDR = "lookupd-address"
const TOPIC_MAIN = "main"

var conf *iniconf.ConfigFile

func main() {

	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	config, err := iniconf.ReadConfigFile(DEFAULT_CONFIG_FILENAME)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	bot3serverInput, _ := config.GetString(CONFIG_CAT_DEFAULT, CONFIG_BOT3SERVER_INPUT)
	outputWriterAddress, _ := config.GetString(CONFIG_CAT_NSQ, CONFIG_OUTPUT_WRITER_ADDR)
	lookupdAddress, _ := config.GetString(CONFIG_CAT_NSQ, CONFIG_LOOKUPD_ADDR)

	// set up listener instance
	incomingFromIRC, err := nsq.NewReader(bot3serverInput, TOPIC_MAIN)
	if err != nil {
		panic(err)
		sigChan <- syscall.SIGINT
	}

	// set up channels
	outgoingToNSQChan := make(chan *server.BotResponse)

	outputWriter := nsq.NewWriter(outputWriterAddress)

	// set up heartbeat ticker
	heartbeatTicker := time.NewTicker(1 * time.Second)

	// initialize the handlers
	bot3server := &Bot3Server{Config: config, OutgoingChan: outgoingToNSQChan}
	bot3server.Initialize()

	incomingFromIRC.AddHandler(bot3server)
	incomingFromIRC.ConnectToLookupd(lookupdAddress)

	go bot3server.HandleOutgoing(outgoingToNSQChan, heartbeatTicker.C, outputWriter, bot3server.UniqueID)

	log.Printf("Done starting up. UUID:[%s]. Waiting on quit signal.", bot3server.UniqueID.String)
	<-sigChan
}

type Bot3Server struct {
	Initialized  bool
	Config       *iniconf.ConfigFile
	Handlers     map[string]server.BotHandler
	OutgoingChan chan *server.BotResponse
	UniqueID     uuid.UUID
}

func (bs *Bot3Server) Initialize() {
	// run only once
	if !bs.Initialized {

		bs.UniqueID = uuid.NewV1()
		bs.initServices()
		bs.Initialized = true
	}
}

func (bs *Bot3Server) AddHandler(key string, h server.BotHandler) {
	plugins, err := bs.Config.GetString(CONFIG_CAT_PLUGINS, "enabled")
	// If plugins string does not exist, assume that all plugins
	// are enabled.
	if err == nil {
		if !strings.Contains(" "+plugins+" ", " "+key+" ") {
			return
		}
	}
	bs.Handlers[key] = h
}

func (bs *Bot3Server) GetHandler(key string) server.BotHandler {
	return bs.Handlers[key]
}

func (bs *Bot3Server) initServices() error {

	bs.Handlers = make(map[string]server.BotHandler)

	// implement all services
	bs.AddHandler("fight", (new(fight.FightService)).NewService(bs.Config))
	bs.AddHandler("cah", (new(cah.CahService)).NewService(bs.Config))
	bs.AddHandler("mongo", (new(mongo.MongoService)).NewService(bs.Config))
	bs.AddHandler("slap", (new(slap.SlapService)).NewService(bs.Config))
	bs.AddHandler("inconceivable", (new(inconceivable.InconceivableService)).NewService(bs.Config))
	bs.AddHandler("help", (new(help.HelpService)).NewService(bs.Config))
	bs.AddHandler("remindme", (new(remindme.RemindMeService)).NewService(bs.Config))
	bs.AddHandler("nextwedding", (new(nextwedding.NextWeddingService)).NewService(bs.Config))
	bs.AddHandler("weather", (new(wuconditions.WeatherConditionsService)).NewService(bs.Config))
	bs.AddHandler("forecast", (new(wuforecast.WeatherForecastService)).NewService(bs.Config))
	bs.AddHandler("zed", (new(zed.ZedsDeadService)).NewService(bs.Config))
	bs.AddHandler("boulderingtime", (new(boulderingtime.BoulderingTimeService)).NewService(bs.Config))
	bs.AddHandler("dice", (new(dice.DiceService)).NewService(bs.Config))
	bs.AddHandler("catfacts", (new(catfacts.CatFactsService)).NewService(bs.Config))
	return nil
}

func (bs *Bot3Server) HandleMessage(message *nsq.Message) error {

	//	bs.IncomingChan <- message
	var req = &server.BotRequest{}
	json.Unmarshal(message.Body, req)
	go bs.HandleIncoming(req)
	return nil
}

func (bs *Bot3Server) HandleOutgoing(outgoingToNSQChan chan *server.BotResponse, heartbeatTicker <-chan time.Time, outputWriter *nsq.Writer, serverID uuid.UUID) {

	for {
		select {
		case msg := <-outgoingToNSQChan:
			val, _ := json.Marshal(msg)
			outputWriter.Publish("bot3server-output", val)
			break
		case t := <-heartbeatTicker:
			hb := &server.Bot3ServerHeartbeat{ServerID: serverID.String(), Timestamp: t}
			val, _ := json.Marshal(hb)
			outputWriter.Publish("bot3server-heartbeat", val)
			break
		}
	}
}

func (bs *Bot3Server) HandleIncoming(botRequest *server.BotRequest) error {

	// since we dont want the entire botapi to crash if a module throws an error or panic
	// we'll trap it here and log/notify the owner.

	// mabye a good idea to automatically-disable the module if it panics too frequently

	// throwout malformed requests
	if len(botRequest.ChatText) < 1 {
		return nil
	}

	// deferred method to protect against panics
	defer func(rawline string) {
		// if module panics, log it and discard
		if x := recover(); x != nil {
			log.Printf("Trapped panic. Rawline is:[%s]: %v\n", rawline, x)
		}
	}(botRequest.Text())

	// check if command before processing
	if botRequest.RequestIsCommand() {

		command := botRequest.Command()

		handler := bs.GetHandler(command)

		if handler != nil {
			//log.Printf("Assigning handler for: %s\n", command)
			botResponse := &server.BotResponse{Target: botRequest.Channel, Identifier: botRequest.Identifier}
			handler.Handle(botRequest, botResponse)
			bs.OutgoingChan <- botResponse
		}
	}

	// return error object (nil for now)
	return nil
}
<commit_msg>switching back to mainstream go-nsq, updating code to match new API changes<commit_after>package main

import (
	"github.com/gamelost/bot3server/module/fight"
	"github.com/gamelost/bot3server/module/mongo"
	// "github.com/gamelost/bot3server/module/logger"
	"github.com/gamelost/bot3server/module/help"
	"github.com/gamelost/bot3server/module/inconceivable"
	"github.com/gamelost/bot3server/module/slap"
	"github.com/gamelost/bot3server/server"
	//"github.com/gamelost/bot3server/module/sleep"
	"github.com/gamelost/bot3server/module/cah"
	"github.com/gamelost/bot3server/module/nextwedding"
	// "github.com/gamelost/bot3server/module/panic"
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	nsq "github.com/bitly/go-nsq"
	"github.com/gamelost/bot3server/module/boulderingtime"
	"github.com/gamelost/bot3server/module/catfacts"
	"github.com/gamelost/bot3server/module/dice"
	"github.com/gamelost/bot3server/module/remindme"
	wuconditions "github.com/gamelost/bot3server/module/weather/conditions"
	wuforecast "github.com/gamelost/bot3server/module/weather/forecast"
	"github.com/gamelost/bot3server/module/zed"
	"github.com/twinj/uuid"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

const DEFAULT_CONFIG_FILENAME = "bot3server.config"
const CONFIG_CAT_DEFAULT = "default"
const CONFIG_CAT_PLUGINS = "plugins"

// nsq specific constants
const CONFIG_CAT_NSQ = "nsq"
const CONFIG_BOT3SERVER_INPUT = "bot3server-input"
const CONFIG_BOT3SERVER_OUTPUT = "bot3server-output"
const CONFIG_OUTPUT_WRITER_ADDR = "output-writer-address"
const CONFIG_LOOKUPD_ADDR = "lookupd-address"
const TOPIC_MAIN = "main"

var conf *iniconf.ConfigFile

func main() {

	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	config, err := iniconf.ReadConfigFile(DEFAULT_CONFIG_FILENAME)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	bot3serverInput, _ := config.GetString(CONFIG_CAT_DEFAULT, CONFIG_BOT3SERVER_INPUT)
	outputWriterAddress, _ := config.GetString(CONFIG_CAT_NSQ, CONFIG_OUTPUT_WRITER_ADDR)
	lookupdAddress, _ := config.GetString(CONFIG_CAT_NSQ, CONFIG_LOOKUPD_ADDR)

	// set up listener instance
	incomingFromIRC, err := nsq.NewConsumer(bot3serverInput, TOPIC_MAIN, nsq.NewConfig())
	if err != nil {
		panic(err)
		sigChan <- syscall.SIGINT
	}

	// set up channels
	outgoingToNSQChan := make(chan *server.BotResponse)

	outputWriter, err := nsq.NewProducer(outputWriterAddress, nsq.NewConfig())
	if err != nil {
		panic(err)
		sigChan <- syscall.SIGINT
	}

	// set up heartbeat ticker
	heartbeatTicker := time.NewTicker(1 * time.Second)

	// initialize the handlers
	bot3server := &Bot3Server{Config: config, OutgoingChan: outgoingToNSQChan}
	bot3server.Initialize()

	incomingFromIRC.SetHandler(bot3server)
	incomingFromIRC.ConnectToNSQLookupd(lookupdAddress)

	go bot3server.HandleOutgoing(outgoingToNSQChan, heartbeatTicker.C, outputWriter, bot3server.UniqueID)

	log.Printf("Done starting up. UUID:[%s]. Waiting on quit signal.", bot3server.UniqueID.String)
	<-sigChan
}

type Bot3Server struct {
	Initialized  bool
	Config       *iniconf.ConfigFile
	Handlers     map[string]server.BotHandler
	OutgoingChan chan *server.BotResponse
	UniqueID     uuid.UUID
}

func (bs *Bot3Server) Initialize() {
	// run only once
	if !bs.Initialized {

		bs.UniqueID = uuid.NewV1()
		bs.initServices()
		bs.Initialized = true
	}
}

func (bs *Bot3Server) AddHandler(key string, h server.BotHandler) {
	plugins, err := bs.Config.GetString(CONFIG_CAT_PLUGINS, "enabled")
	// If plugins string does not exist, assume that all plugins
	// are enabled.
	if err == nil {
		if !strings.Contains(" "+plugins+" ", " "+key+" ") {
			return
		}
	}
	bs.Handlers[key] = h
}

func (bs *Bot3Server) GetHandler(key string) server.BotHandler {
	return bs.Handlers[key]
}

func (bs *Bot3Server) initServices() error {

	bs.Handlers = make(map[string]server.BotHandler)

	// implement all services
	bs.AddHandler("fight", (new(fight.FightService)).NewService(bs.Config))
	bs.AddHandler("cah", (new(cah.CahService)).NewService(bs.Config))
	bs.AddHandler("mongo", (new(mongo.MongoService)).NewService(bs.Config))
	bs.AddHandler("slap", (new(slap.SlapService)).NewService(bs.Config))
	bs.AddHandler("inconceivable", (new(inconceivable.InconceivableService)).NewService(bs.Config))
	bs.AddHandler("help", (new(help.HelpService)).NewService(bs.Config))
	bs.AddHandler("remindme", (new(remindme.RemindMeService)).NewService(bs.Config))
	bs.AddHandler("nextwedding", (new(nextwedding.NextWeddingService)).NewService(bs.Config))
	bs.AddHandler("weather", (new(wuconditions.WeatherConditionsService)).NewService(bs.Config))
	bs.AddHandler("forecast", (new(wuforecast.WeatherForecastService)).NewService(bs.Config))
	bs.AddHandler("zed", (new(zed.ZedsDeadService)).NewService(bs.Config))
	bs.AddHandler("boulderingtime", (new(boulderingtime.BoulderingTimeService)).NewService(bs.Config))
	bs.AddHandler("dice", (new(dice.DiceService)).NewService(bs.Config))
	bs.AddHandler("catfacts", (new(catfacts.CatFactsService)).NewService(bs.Config))
	return nil
}

func (bs *Bot3Server) HandleMessage(message *nsq.Message) error {

	//	bs.IncomingChan <- message
	var req = &server.BotRequest{}
	json.Unmarshal(message.Body, req)
	go bs.HandleIncoming(req)
	return nil
}

func (bs *Bot3Server) HandleOutgoing(outgoingToNSQChan chan *server.BotResponse, heartbeatTicker <-chan time.Time, outputWriter *nsq.Producer, serverID uuid.UUID) {

	for {
		select {
		case msg := <-outgoingToNSQChan:
			val, _ := json.Marshal(msg)
			outputWriter.Publish("bot3server-output", val)
			break
		case t := <-heartbeatTicker:
			hb := &server.Bot3ServerHeartbeat{ServerID: serverID.String(), Timestamp: t}
			val, _ := json.Marshal(hb)
			outputWriter.Publish("bot3server-heartbeat", val)
			break
		}
	}
}

func (bs *Bot3Server) HandleIncoming(botRequest *server.BotRequest) error {

	// since we dont want the entire botapi to crash if a module throws an error or panic
	// we'll trap it here and log/notify the owner.

	// mabye a good idea to automatically-disable the module if it panics too frequently

	// throwout malformed requests
	if len(botRequest.ChatText) < 1 {
		return nil
	}

	// deferred method to protect against panics
	defer func(rawline string) {
		// if module panics, log it and discard
		if x := recover(); x != nil {
			log.Printf("Trapped panic. Rawline is:[%s]: %v\n", rawline, x)
		}
	}(botRequest.Text())

	// check if command before processing
	if botRequest.RequestIsCommand() {

		command := botRequest.Command()

		handler := bs.GetHandler(command)

		if handler != nil {
			//log.Printf("Assigning handler for: %s\n", command)
			botResponse := &server.BotResponse{Target: botRequest.Channel, Identifier: botRequest.Identifier}
			handler.Handle(botRequest, botResponse)
			bs.OutgoingChan <- botResponse
		}
	}

	// return error object (nil for now)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gamelost/bot3server/module/fight"
	// "github.com/gamelost/bot3server/module/logger"
	"github.com/gamelost/bot3server/server"
	//"github.com/gamelost/bot3server/module/helloworld"
	"github.com/gamelost/bot3server/module/help"
	"github.com/gamelost/bot3server/module/inconceivable"
	"github.com/gamelost/bot3server/module/slap"
	//"github.com/gamelost/bot3server/module/sleep"
	"github.com/gamelost/bot3server/module/cah"
	"github.com/gamelost/bot3server/module/nextwedding"
	// "github.com/gamelost/bot3server/module/panic"
	"github.com/gamelost/bot3server/module/dice"
	"github.com/gamelost/bot3server/module/remindme"
	"github.com/gamelost/bot3server/module/zed"
	//"github.com/gamelost/bot3server/module/testcommands"
	"github.com/gamelost/bot3server/module/boulderingtime"
	"github.com/gamelost/bot3server/module/catfacts"
	wuconditions "github.com/gamelost/bot3server/module/weather/conditions"
	wuforecast "github.com/gamelost/bot3server/module/weather/forecast"
	//"github.com/gamelost/bot3server/module/howlongbeforeicanquityouintel"
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	nsq "github.com/gamelost/go-nsq"
	"github.com/twinj/uuid"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
	"strings"
)

const DEFAULT_CONFIG_FILENAME = "bot3server.config"
const CONFIG_CAT_DEFAULT = "default"
const CONFIG_CAT_PLUGINS = "plugins"

// nsq specific constants
const CONFIG_CAT_NSQ = "nsq"
const CONFIG_BOT3SERVER_INPUT = "bot3server-input"
const CONFIG_BOT3SERVER_OUTPUT = "bot3server-output"
const CONFIG_OUTPUT_WRITER_ADDR = "output-writer-address"
const CONFIG_LOOKUPD_ADDR = "lookupd-address"
const TOPIC_MAIN = "main"

var conf *iniconf.ConfigFile

func main() {

	// create unique UUID on startup
	newUUID := uuid.NewV1()

	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	config, err := iniconf.ReadConfigFile(DEFAULT_CONFIG_FILENAME)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}
	conf = config

	bot3serverInput, _ := config.GetString(CONFIG_CAT_DEFAULT, CONFIG_BOT3SERVER_INPUT)
	outputWriterAddress, _ := config.GetString(CONFIG_CAT_NSQ, CONFIG_OUTPUT_WRITER_ADDR)
	lookupdAddress, _ := config.GetString(CONFIG_CAT_NSQ, CONFIG_LOOKUPD_ADDR)

	// set up listener instance
	incomingFromIRC, err := nsq.NewReader(bot3serverInput, TOPIC_MAIN)
	if err != nil {
		panic(err)
		sigChan <- syscall.SIGINT
	}

	// set up channels
	outgoingToNSQChan := make(chan *server.BotResponse)

	outputWriter := nsq.NewWriter(outputWriterAddress)

	// set up heartbeat ticker
	heartbeatTicker := time.NewTicker(1 * time.Second)

	// initialize the handlers
	botApp := &BotApp{Config: config, OutgoingChan: outgoingToNSQChan, UniqueID: newUUID}
	botApp.initServices()

	incomingFromIRC.AddHandler(botApp)
	incomingFromIRC.ConnectToLookupd(lookupdAddress)

	go HandleOutgoingToNSQ(outgoingToNSQChan, heartbeatTicker.C, outputWriter, newUUID)

	log.Printf("Done starting up. UUID:[%s]. Waiting on quit signal.", newUUID.String())
	<-sigChan
}

func HandleOutgoingToNSQ(outgoingToNSQChan chan *server.BotResponse, heartbeatTicker <-chan time.Time, outputWriter *nsq.Writer, serverID uuid.UUID) {

	for {
		select {
		case msg := <-outgoingToNSQChan:
			val, _ := json.Marshal(msg)
			outputWriter.Publish("bot3server-output", val)
			break
		case t := <-heartbeatTicker:
			hb := &server.Bot3ServerHeartbeat{ServerID: serverID.String(), Timestamp: t}
			val, _ := json.Marshal(hb)
			outputWriter.Publish("bot3server-heartbeat", val)
			break
		}
	}
}

type BotApp struct {
	Config       *iniconf.ConfigFile
	Handlers     map[string]server.BotHandler
	OutgoingChan chan *server.BotResponse
	UniqueID     uuid.UUID
}

func (ba *BotApp) AddHandler(key string, h server.BotHandler) {
	log.Printf("Add Handler for %s.", key)
	plugins, err := conf.GetString(CONFIG_CAT_PLUGINS, "enabled")
	log.Printf("err: %r", err)
	// If plugins string does not exist, assume that all plugins
	// are enabled.
	if err == nil {
	  log.Printf("Plugins enabled: %s", plugins)
	  if !strings.Contains(" " + plugins + " ", " " + key + " ") {
		return
	  }
	}
	ba.Handlers[key] = h
}

func (ba *BotApp) GetHandler(key string) server.BotHandler {
	return ba.Handlers[key]
}

func (ba *BotApp) initServices() error {

	ba.Handlers = make(map[string]server.BotHandler)

	// implement all services
        ba.AddHandler("fight", (new(fight.FightService)).NewService())
        ba.AddHandler("cah", (new(cah.CahService)).NewService())
	ba.AddHandler("slap", (new(slap.SlapService)).NewService())
	ba.AddHandler("inconceivable", (new(inconceivable.InconceivableService)).NewService())
	ba.AddHandler("help", (new(help.HelpService)).NewService())
	ba.AddHandler("remindme", (new(remindme.RemindMeService)).NewService())
	ba.AddHandler("nextwedding", (new(nextwedding.NextWeddingService)).NewService())
	ba.AddHandler("weather", (new(wuconditions.WeatherConditionsService)).NewService())
	ba.AddHandler("forecast", (new(wuforecast.WeatherForecastService)).NewService())
	ba.AddHandler("zed", (new(zed.ZedsDeadService)).NewService())
	ba.AddHandler("boulderingtime", (new(boulderingtime.BoulderingTimeService)).NewService())
	ba.AddHandler("dice", (new(dice.DiceService)).NewService())
	ba.AddHandler("catfacts", (new(catfacts.CatFactsService)).NewService())
	return nil
}

func (ba *BotApp) HandleMessage(message *nsq.Message) error {

	//	ba.IncomingChan <- message
	var req = &server.BotRequest{}
	json.Unmarshal(message.Body, req)
	go ba.HandleIncoming(req)
	return nil
}

func (ba *BotApp) HandleIncoming(botRequest *server.BotRequest) error {

	// since we dont want the entire botapi to crash if a module throws an error or panic
	// we'll trap it here and log/notify the owner.

	// mabye a good idea to automatically-disable the module if it panics too frequently

	// throwout malformed requests
	if botRequest.RawLine == nil {
		return nil
	}

	// deferred method to protect against panics
	defer func(rawline string) {
		// if module panics, log it and discard
		if x := recover(); x != nil {
			log.Printf("Trapped panic. Rawline is:[%s]: %v\n", rawline, x)
		}
	}(botRequest.RawLine.Text())

	// log all lines
	// logger.Log(botRequest)

	// check if command before processing
	if botRequest.RequestIsCommand() {

		command := botRequest.Command()

		handler := ba.GetHandler(command)

		if handler != nil {
			//log.Printf("Assigning handler for: %s\n", command)
			botResponse := &server.BotResponse{Target: botRequest.RawLine.Target()}
			handler.Handle(botRequest, botResponse)
			ba.OutgoingChan <- botResponse
		}
	}

	// return error object (nil for now)
	return nil
}
<commit_msg>gofmt<commit_after>package main

import (
	"github.com/gamelost/bot3server/module/fight"
	// "github.com/gamelost/bot3server/module/logger"
	"github.com/gamelost/bot3server/server"
	//"github.com/gamelost/bot3server/module/helloworld"
	"github.com/gamelost/bot3server/module/help"
	"github.com/gamelost/bot3server/module/inconceivable"
	"github.com/gamelost/bot3server/module/slap"
	//"github.com/gamelost/bot3server/module/sleep"
	"github.com/gamelost/bot3server/module/cah"
	"github.com/gamelost/bot3server/module/nextwedding"
	// "github.com/gamelost/bot3server/module/panic"
	"github.com/gamelost/bot3server/module/dice"
	"github.com/gamelost/bot3server/module/remindme"
	"github.com/gamelost/bot3server/module/zed"
	//"github.com/gamelost/bot3server/module/testcommands"
	"github.com/gamelost/bot3server/module/boulderingtime"
	"github.com/gamelost/bot3server/module/catfacts"
	wuconditions "github.com/gamelost/bot3server/module/weather/conditions"
	wuforecast "github.com/gamelost/bot3server/module/weather/forecast"
	//"github.com/gamelost/bot3server/module/howlongbeforeicanquityouintel"
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	nsq "github.com/gamelost/go-nsq"
	"github.com/twinj/uuid"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

const DEFAULT_CONFIG_FILENAME = "bot3server.config"
const CONFIG_CAT_DEFAULT = "default"
const CONFIG_CAT_PLUGINS = "plugins"

// nsq specific constants
const CONFIG_CAT_NSQ = "nsq"
const CONFIG_BOT3SERVER_INPUT = "bot3server-input"
const CONFIG_BOT3SERVER_OUTPUT = "bot3server-output"
const CONFIG_OUTPUT_WRITER_ADDR = "output-writer-address"
const CONFIG_LOOKUPD_ADDR = "lookupd-address"
const TOPIC_MAIN = "main"

var conf *iniconf.ConfigFile

func main() {

	// create unique UUID on startup
	newUUID := uuid.NewV1()

	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	config, err := iniconf.ReadConfigFile(DEFAULT_CONFIG_FILENAME)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}
	conf = config

	bot3serverInput, _ := config.GetString(CONFIG_CAT_DEFAULT, CONFIG_BOT3SERVER_INPUT)
	outputWriterAddress, _ := config.GetString(CONFIG_CAT_NSQ, CONFIG_OUTPUT_WRITER_ADDR)
	lookupdAddress, _ := config.GetString(CONFIG_CAT_NSQ, CONFIG_LOOKUPD_ADDR)

	// set up listener instance
	incomingFromIRC, err := nsq.NewReader(bot3serverInput, TOPIC_MAIN)
	if err != nil {
		panic(err)
		sigChan <- syscall.SIGINT
	}

	// set up channels
	outgoingToNSQChan := make(chan *server.BotResponse)

	outputWriter := nsq.NewWriter(outputWriterAddress)

	// set up heartbeat ticker
	heartbeatTicker := time.NewTicker(1 * time.Second)

	// initialize the handlers
	botApp := &BotApp{Config: config, OutgoingChan: outgoingToNSQChan, UniqueID: newUUID}
	botApp.initServices()

	incomingFromIRC.AddHandler(botApp)
	incomingFromIRC.ConnectToLookupd(lookupdAddress)

	go HandleOutgoingToNSQ(outgoingToNSQChan, heartbeatTicker.C, outputWriter, newUUID)

	log.Printf("Done starting up. UUID:[%s]. Waiting on quit signal.", newUUID.String())
	<-sigChan
}

func HandleOutgoingToNSQ(outgoingToNSQChan chan *server.BotResponse, heartbeatTicker <-chan time.Time, outputWriter *nsq.Writer, serverID uuid.UUID) {

	for {
		select {
		case msg := <-outgoingToNSQChan:
			val, _ := json.Marshal(msg)
			outputWriter.Publish("bot3server-output", val)
			break
		case t := <-heartbeatTicker:
			hb := &server.Bot3ServerHeartbeat{ServerID: serverID.String(), Timestamp: t}
			val, _ := json.Marshal(hb)
			outputWriter.Publish("bot3server-heartbeat", val)
			break
		}
	}
}

type BotApp struct {
	Config       *iniconf.ConfigFile
	Handlers     map[string]server.BotHandler
	OutgoingChan chan *server.BotResponse
	UniqueID     uuid.UUID
}

func (ba *BotApp) AddHandler(key string, h server.BotHandler) {
	log.Printf("Add Handler for %s.", key)
	plugins, err := conf.GetString(CONFIG_CAT_PLUGINS, "enabled")
	log.Printf("err: %r", err)
	// If plugins string does not exist, assume that all plugins
	// are enabled.
	if err == nil {
		log.Printf("Plugins enabled: %s", plugins)
		if !strings.Contains(" "+plugins+" ", " "+key+" ") {
			return
		}
	}
	ba.Handlers[key] = h
}

func (ba *BotApp) GetHandler(key string) server.BotHandler {
	return ba.Handlers[key]
}

func (ba *BotApp) initServices() error {

	ba.Handlers = make(map[string]server.BotHandler)

	// implement all services
	ba.AddHandler("fight", (new(fight.FightService)).NewService())
	ba.AddHandler("cah", (new(cah.CahService)).NewService())
	ba.AddHandler("slap", (new(slap.SlapService)).NewService())
	ba.AddHandler("inconceivable", (new(inconceivable.InconceivableService)).NewService())
	ba.AddHandler("help", (new(help.HelpService)).NewService())
	ba.AddHandler("remindme", (new(remindme.RemindMeService)).NewService())
	ba.AddHandler("nextwedding", (new(nextwedding.NextWeddingService)).NewService())
	ba.AddHandler("weather", (new(wuconditions.WeatherConditionsService)).NewService())
	ba.AddHandler("forecast", (new(wuforecast.WeatherForecastService)).NewService())
	ba.AddHandler("zed", (new(zed.ZedsDeadService)).NewService())
	ba.AddHandler("boulderingtime", (new(boulderingtime.BoulderingTimeService)).NewService())
	ba.AddHandler("dice", (new(dice.DiceService)).NewService())
	ba.AddHandler("catfacts", (new(catfacts.CatFactsService)).NewService())
	return nil
}

func (ba *BotApp) HandleMessage(message *nsq.Message) error {

	//	ba.IncomingChan <- message
	var req = &server.BotRequest{}
	json.Unmarshal(message.Body, req)
	go ba.HandleIncoming(req)
	return nil
}

func (ba *BotApp) HandleIncoming(botRequest *server.BotRequest) error {

	// since we dont want the entire botapi to crash if a module throws an error or panic
	// we'll trap it here and log/notify the owner.

	// mabye a good idea to automatically-disable the module if it panics too frequently

	// throwout malformed requests
	if botRequest.RawLine == nil {
		return nil
	}

	// deferred method to protect against panics
	defer func(rawline string) {
		// if module panics, log it and discard
		if x := recover(); x != nil {
			log.Printf("Trapped panic. Rawline is:[%s]: %v\n", rawline, x)
		}
	}(botRequest.RawLine.Text())

	// log all lines
	// logger.Log(botRequest)

	// check if command before processing
	if botRequest.RequestIsCommand() {

		command := botRequest.Command()

		handler := ba.GetHandler(command)

		if handler != nil {
			//log.Printf("Assigning handler for: %s\n", command)
			botResponse := &server.BotResponse{Target: botRequest.RawLine.Target()}
			handler.Handle(botRequest, botResponse)
			ba.OutgoingChan <- botResponse
		}
	}

	// return error object (nil for now)
	return nil
}
<|endoftext|>


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

package caddyauth

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"flag"
	"fmt"
	"os"
	"os/signal"

	"github.com/caddyserver/caddy/v2"
	caddycmd "github.com/caddyserver/caddy/v2/cmd"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/scrypt"
	"golang.org/x/crypto/ssh/terminal"
)

func init() {
	caddycmd.RegisterCommand(caddycmd.Command{
		Name:  "hash-password",
		Func:  cmdHashPassword,
		Usage: "[--algorithm <name>] [--salt <string>] [--plaintext <password>]",
		Short: "Hashes a password and writes base64",
		Long: `
Convenient way to hash a plaintext password. The resulting
hash is written to stdout as a base64 string.

--plaintext, when omitted, will be read from stdin. If
Caddy is attached to a controlling tty, the plaintext will
not be echoed.

--algorithm may be bcrypt or scrypt. If script, the default
parameters are used.

Use the --salt flag for algorithms which require a salt to
be provided (scrypt).
`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("hash-password", flag.ExitOnError)
			fs.String("algorithm", "bcrypt", "Name of the hash algorithm")
			fs.String("plaintext", "", "The plaintext password")
			fs.String("salt", "", "The password salt")
			return fs
		}(),
	})
}

func cmdHashPassword(fs caddycmd.Flags) (int, error) {
	var err error

	algorithm := fs.String("algorithm")
	plaintext := []byte(fs.String("plaintext"))
	salt := []byte(fs.String("salt"))

	if len(plaintext) == 0 {
		fd := int(os.Stdin.Fd())
		if terminal.IsTerminal(fd) {
			// ensure the terminal state is restored on SIGINT
			state, _ := terminal.GetState(fd)
			c := make(chan os.Signal)
			signal.Notify(c, os.Interrupt)
			go func() {
				<-c
				_ = terminal.Restore(fd, state)
				os.Exit(caddy.ExitCodeFailedStartup)
			}()
			defer signal.Stop(c)

			fmt.Fprint(os.Stderr, "Enter password: ")
			plaintext, err = terminal.ReadPassword(fd)
			fmt.Fprintln(os.Stderr)
			if err != nil {
				return caddy.ExitCodeFailedStartup, err
			}

			fmt.Fprint(os.Stderr, "Confirm password: ")
			confirmation, err := terminal.ReadPassword(fd)
			fmt.Fprintln(os.Stderr)
			if err != nil {
				return caddy.ExitCodeFailedStartup, err
			}

			if !bytes.Equal(plaintext, confirmation) {
				return caddy.ExitCodeFailedStartup, fmt.Errorf("password does not match")
			}
		} else {
			rd := bufio.NewReader(os.Stdin)
			plaintext, err = rd.ReadBytes('\n')
			if err != nil {
				return caddy.ExitCodeFailedStartup, err
			}

			plaintext = plaintext[:len(plaintext)-1] // Trailing newline
		}

		if len(plaintext) == 0 {
			return caddy.ExitCodeFailedStartup, fmt.Errorf("plaintext is required")
		}
	}

	var hash []byte
	switch algorithm {
	case "bcrypt":
		hash, err = bcrypt.GenerateFromPassword(plaintext, bcrypt.DefaultCost)
	case "scrypt":
		def := ScryptHash{}
		def.SetDefaults()
		hash, err = scrypt.Key(plaintext, salt, def.N, def.R, def.P, def.KeyLength)
	default:
		return caddy.ExitCodeFailedStartup, fmt.Errorf("unrecognized hash algorithm: %s", algorithm)
	}
	if err != nil {
		return caddy.ExitCodeFailedStartup, err
	}

	hashBase64 := base64.StdEncoding.EncodeToString(hash)

	fmt.Println(hashBase64)

	return 0, nil
}
<commit_msg>caddyauth: hash-password: Set bcrypt cost to 14 (#3580)<commit_after>// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package caddyauth

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"flag"
	"fmt"
	"os"
	"os/signal"

	"github.com/caddyserver/caddy/v2"
	caddycmd "github.com/caddyserver/caddy/v2/cmd"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/scrypt"
	"golang.org/x/crypto/ssh/terminal"
)

func init() {
	caddycmd.RegisterCommand(caddycmd.Command{
		Name:  "hash-password",
		Func:  cmdHashPassword,
		Usage: "[--algorithm <name>] [--salt <string>] [--plaintext <password>]",
		Short: "Hashes a password and writes base64",
		Long: `
Convenient way to hash a plaintext password. The resulting
hash is written to stdout as a base64 string.

--plaintext, when omitted, will be read from stdin. If
Caddy is attached to a controlling tty, the plaintext will
not be echoed.

--algorithm may be bcrypt or scrypt. If script, the default
parameters are used.

Use the --salt flag for algorithms which require a salt to
be provided (scrypt).
`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("hash-password", flag.ExitOnError)
			fs.String("algorithm", "bcrypt", "Name of the hash algorithm")
			fs.String("plaintext", "", "The plaintext password")
			fs.String("salt", "", "The password salt")
			return fs
		}(),
	})
}

func cmdHashPassword(fs caddycmd.Flags) (int, error) {
	var err error

	algorithm := fs.String("algorithm")
	plaintext := []byte(fs.String("plaintext"))
	salt := []byte(fs.String("salt"))

	if len(plaintext) == 0 {
		fd := int(os.Stdin.Fd())
		if terminal.IsTerminal(fd) {
			// ensure the terminal state is restored on SIGINT
			state, _ := terminal.GetState(fd)
			c := make(chan os.Signal)
			signal.Notify(c, os.Interrupt)
			go func() {
				<-c
				_ = terminal.Restore(fd, state)
				os.Exit(caddy.ExitCodeFailedStartup)
			}()
			defer signal.Stop(c)

			fmt.Fprint(os.Stderr, "Enter password: ")
			plaintext, err = terminal.ReadPassword(fd)
			fmt.Fprintln(os.Stderr)
			if err != nil {
				return caddy.ExitCodeFailedStartup, err
			}

			fmt.Fprint(os.Stderr, "Confirm password: ")
			confirmation, err := terminal.ReadPassword(fd)
			fmt.Fprintln(os.Stderr)
			if err != nil {
				return caddy.ExitCodeFailedStartup, err
			}

			if !bytes.Equal(plaintext, confirmation) {
				return caddy.ExitCodeFailedStartup, fmt.Errorf("password does not match")
			}
		} else {
			rd := bufio.NewReader(os.Stdin)
			plaintext, err = rd.ReadBytes('\n')
			if err != nil {
				return caddy.ExitCodeFailedStartup, err
			}

			plaintext = plaintext[:len(plaintext)-1] // Trailing newline
		}

		if len(plaintext) == 0 {
			return caddy.ExitCodeFailedStartup, fmt.Errorf("plaintext is required")
		}
	}

	var hash []byte
	switch algorithm {
	case "bcrypt":
		hash, err = bcrypt.GenerateFromPassword(plaintext, 14)
	case "scrypt":
		def := ScryptHash{}
		def.SetDefaults()
		hash, err = scrypt.Key(plaintext, salt, def.N, def.R, def.P, def.KeyLength)
	default:
		return caddy.ExitCodeFailedStartup, fmt.Errorf("unrecognized hash algorithm: %s", algorithm)
	}
	if err != nil {
		return caddy.ExitCodeFailedStartup, err
	}

	hashBase64 := base64.StdEncoding.EncodeToString(hash)

	fmt.Println(hashBase64)

	return 0, nil
}
<|endoftext|>


<commit_before>package common

import (
	"context"
	"testing"
	"time"

	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

func TestStepShutdown_impl(t *testing.T) {
	var _ multistep.Step = new(StepShutdown)
}

func TestStepShutdown_noShutdownCommand(t *testing.T) {
	state := testState(t)
	step := new(StepShutdown)

	comm := new(packer.MockCommunicator)
	state.Put("communicator", comm)
	state.Put("vmName", "foo")

	driver := state.Get("driver").(*DriverMock)

	// Test the run
	if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
		t.Fatalf("bad action: %#v", action)
	}
	if _, ok := state.GetOk("error"); ok {
		t.Fatal("should NOT have error")
	}

	// Test that Stop was just called
	if driver.StopName != "foo" {
		t.Fatal("should call stop")
	}
	if comm.StartCalled {
		t.Fatal("comm start should not be called")
	}
}

func TestStepShutdown_shutdownCommand(t *testing.T) {
	state := testState(t)
	step := new(StepShutdown)
	step.Command = "poweroff"
	step.Timeout = 1 * time.Second

	comm := new(packer.MockCommunicator)
	state.Put("communicator", comm)
	state.Put("vmName", "foo")

	driver := state.Get("driver").(*DriverMock)
	driver.IsRunningReturn = true

	go func() {
		time.Sleep(10 * time.Millisecond)
		driver.Lock()
		defer driver.Unlock()
		driver.IsRunningReturn = false
	}()

	// Test the run
	if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
		t.Fatalf("bad action: %#v", action)
	}
	if _, ok := state.GetOk("error"); ok {
		t.Fatal("should NOT have error")
	}

	// Test that Stop was just called
	if driver.StopName != "" {
		t.Fatal("should not call stop")
	}
	if comm.StartCmd.Command != step.Command {
		t.Fatal("comm start should be called")
	}
}

func TestStepShutdown_shutdownTimeout(t *testing.T) {
	state := testState(t)
	step := new(StepShutdown)
	step.Command = "poweroff"
	step.Timeout = 1 * time.Second

	comm := new(packer.MockCommunicator)
	state.Put("communicator", comm)
	state.Put("vmName", "foo")

	driver := state.Get("driver").(*DriverMock)
	driver.IsRunningReturn = true

	go func() {
		time.Sleep(2 * time.Second)
		driver.Lock()
		defer driver.Unlock()
		driver.IsRunningReturn = false
	}()

	// Test the run
	if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
		t.Fatalf("bad action: %#v", action)
	}
	if _, ok := state.GetOk("error"); !ok {
		t.Fatal("should have error")
	}
}

func TestStepShutdown_shutdownDelay(t *testing.T) {
	state := testState(t)
	step := new(StepShutdown)
	step.Command = "poweroff"
	step.Timeout = 5 * time.Second
	step.Delay = 2 * time.Second

	comm := new(packer.MockCommunicator)
	state.Put("communicator", comm)
	state.Put("vmName", "foo")

	driver := state.Get("driver").(*DriverMock)
	driver.IsRunningReturn = true
	start := time.Now()

	go func() {
		time.Sleep(10 * time.Millisecond)
		driver.Lock()
		defer driver.Unlock()
		driver.IsRunningReturn = false
	}()

	// Test the run

	if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
		t.Fatalf("bad action: %#v", action)
	}
	testDuration := time.Since(start).Seconds()
	if testDuration < 2.5 || testDuration > 2.6 {
		t.Fatal("incorrect duration")
	}

	if _, ok := state.GetOk("error"); ok {
		t.Fatal("should NOT have error")
	}

	step.Delay = 0

	driver.IsRunningReturn = true
	start = time.Now()

	go func() {
		time.Sleep(10 * time.Millisecond)
		driver.Lock()
		defer driver.Unlock()
		driver.IsRunningReturn = false
	}()

	// Test the run
	if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
		t.Fatalf("bad action: %#v", action)
	}
	testDuration = time.Since(start).Seconds()
	if testDuration > 0.6 {
		t.Fatal("incorrect duration")
	}

	if _, ok := state.GetOk("error"); ok {
		t.Fatal("should NOT have error")
	}

}
<commit_msg>builder/virtualbox/common/step_shutdown_test.go: compare durations  instead of floats & ouput duration<commit_after>package common

import (
	"context"
	"testing"
	"time"

	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

func TestStepShutdown_impl(t *testing.T) {
	var _ multistep.Step = new(StepShutdown)
}

func TestStepShutdown_noShutdownCommand(t *testing.T) {
	state := testState(t)
	step := new(StepShutdown)

	comm := new(packer.MockCommunicator)
	state.Put("communicator", comm)
	state.Put("vmName", "foo")

	driver := state.Get("driver").(*DriverMock)

	// Test the run
	if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
		t.Fatalf("bad action: %#v", action)
	}
	if _, ok := state.GetOk("error"); ok {
		t.Fatal("should NOT have error")
	}

	// Test that Stop was just called
	if driver.StopName != "foo" {
		t.Fatal("should call stop")
	}
	if comm.StartCalled {
		t.Fatal("comm start should not be called")
	}
}

func TestStepShutdown_shutdownCommand(t *testing.T) {
	state := testState(t)
	step := new(StepShutdown)
	step.Command = "poweroff"
	step.Timeout = 1 * time.Second

	comm := new(packer.MockCommunicator)
	state.Put("communicator", comm)
	state.Put("vmName", "foo")

	driver := state.Get("driver").(*DriverMock)
	driver.IsRunningReturn = true

	go func() {
		time.Sleep(10 * time.Millisecond)
		driver.Lock()
		defer driver.Unlock()
		driver.IsRunningReturn = false
	}()

	// Test the run
	if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
		t.Fatalf("bad action: %#v", action)
	}
	if _, ok := state.GetOk("error"); ok {
		t.Fatal("should NOT have error")
	}

	// Test that Stop was just called
	if driver.StopName != "" {
		t.Fatal("should not call stop")
	}
	if comm.StartCmd.Command != step.Command {
		t.Fatal("comm start should be called")
	}
}

func TestStepShutdown_shutdownTimeout(t *testing.T) {
	state := testState(t)
	step := new(StepShutdown)
	step.Command = "poweroff"
	step.Timeout = 1 * time.Second

	comm := new(packer.MockCommunicator)
	state.Put("communicator", comm)
	state.Put("vmName", "foo")

	driver := state.Get("driver").(*DriverMock)
	driver.IsRunningReturn = true

	go func() {
		time.Sleep(2 * time.Second)
		driver.Lock()
		defer driver.Unlock()
		driver.IsRunningReturn = false
	}()

	// Test the run
	if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
		t.Fatalf("bad action: %#v", action)
	}
	if _, ok := state.GetOk("error"); !ok {
		t.Fatal("should have error")
	}
}

func TestStepShutdown_shutdownDelay(t *testing.T) {
	state := testState(t)
	step := new(StepShutdown)
	step.Command = "poweroff"
	step.Timeout = 5 * time.Second
	step.Delay = 2 * time.Second

	comm := new(packer.MockCommunicator)
	state.Put("communicator", comm)
	state.Put("vmName", "foo")

	driver := state.Get("driver").(*DriverMock)
	driver.IsRunningReturn = true
	start := time.Now()

	go func() {
		time.Sleep(10 * time.Millisecond)
		driver.Lock()
		defer driver.Unlock()
		driver.IsRunningReturn = false
	}()

	// Test the run

	if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
		t.Fatalf("bad action: %#v", action)
	}
	testDuration := time.Since(start)
	if testDuration < 2500*time.Millisecond || testDuration > 2600*time.Millisecond {
		t.Fatalf("incorrect duration %s", testDuration)
	}

	if _, ok := state.GetOk("error"); ok {
		t.Fatal("should NOT have error")
	}

	step.Delay = 0

	driver.IsRunningReturn = true
	start = time.Now()

	go func() {
		time.Sleep(10 * time.Millisecond)
		driver.Lock()
		defer driver.Unlock()
		driver.IsRunningReturn = false
	}()

	// Test the run
	if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
		t.Fatalf("bad action: %#v", action)
	}
	testDuration = time.Since(start)
	if testDuration > 600*time.Millisecond {
		t.Fatalf("incorrect duration %s", testDuration)
	}

	if _, ok := state.GetOk("error"); ok {
		t.Fatal("should NOT have error")
	}

}
<|endoftext|>


<commit_before>// Package path is a set of helper functions for work with paths in goal package.
package path

import (
	"go/build"
	"os"
	"path/filepath"
	"strings"

	"github.com/colegion/goal/log"
)

// goalDir returns an absolute path of the directory where a package of goal
// toolkit is installed (it should be located at "$GOPATH/github.com/colegion/goal/$pkg").
//
// For example, to find out the path of goal's root directory use:
//	path.goalDir()
// To identify the path of goal/generation/handlers subpackage do the following:
//	path.goalDir("generation", "handlers")
func goalDir(pkgs ...string) string {
	p := filepath.Join(
		PackageDir(""), goalImport(pkgs...),
	)
	return filepath.ToSlash(p)
}

// goalImport is equivalent of the goalDir except it returns
// a go import path rather than a path to a directory.
func goalImport(pkgs ...string) string {
	p := "github.com/colegion/goal"
	for i := range pkgs {
		p = filepath.Join(p, pkgs[i])
	}
	return filepath.ToSlash(p)
}

// WorkingDir returns a path to the directory where goal program was run.
// It panics in case of error.
//
// So, if we are moving to some directory and starting goal program there:
//	cd /home/user/somedir
//	goal ...
// the WorkingDir() should return "/home/user/somedir"
//	path := WorkingDir() // Output: "/home/user/somedir"
func WorkingDir() string {
	p, err := os.Getwd()
	log.AssertNil(err)
	return filepath.ToSlash(p)
}

// AbsoluteImport gets an import path and returns its absolute representation.
// So, if it is already absolute or empty it is returned as is.
// Otherwise, it's assumed the path is relative to the current working directory.
// If something goes wrong AbsoluteImport panics.
func AbsoluteImport(path string) string {
	// If path is empty, do nothing.
	if path == "" {
		return path
	}

	// If it is an absolute path, remove the starting slashes, if any.
	path = filepath.ToSlash(path)
	if !strings.HasPrefix(path, ".") {
		return strings.TrimLeft(path, "/")
	}

	// Get absolute import path by removing "$GOPATH/src" at the beginning
	// of working dir + relative path.
	gopath := PackageDir("")
	pkgPath := filepath.ToSlash(filepath.Join(WorkingDir(), path))
	if !strings.HasPrefix(pkgPath, gopath) { // If there is no $GOPATH at the beginning.
		log.Error.Panicf("Your project must be located inside of $GOPATH.")
	}
	imp := Prefixless(pkgPath, gopath)

	// Import path never starts with a slash.
	return Prefixless(imp, "/")
}

// PackageDir gets a golang import path and returns its full path.
func PackageDir(imp string) string {
	gopaths := filepath.SplitList(build.Default.GOPATH)
	return filepath.ToSlash(
		filepath.Join(gopaths[0], "src", imp), // We are always using the first GOPATH in a list.
	)
}

// Prefixless cuts a prefix of a path and returns the result
// that is cleaned.
func Prefixless(path, prefix string) string {
	return filepath.ToSlash(
		filepath.Clean(strings.TrimPrefix(path, prefix)),
	)
}
<commit_msg>Reworked path package<commit_after>// Package path is a wrapper around standard path, path/filepath,
// os, and go/build packages for work with paths and
// import paths.
package path

import (
	"errors"
	"fmt"
	"go/build"
	"os"
	"path"
	"path/filepath"
	"strings"
)

// Path is a path in its normalized form
// (with unix style separators).
type Path struct {
	s   string // Normalized (unix like) form of a path.
	pkg bool   // Whether the path is an import path of a package.
}

// New allocates and returns a new Path.
func New(p string) *Path {
	return &Path{
		s: path.Clean(filepath.ToSlash(p)),
	}
}

// String returns path as is if it is a package import path,
// or using a platform specific separators otherwise, e.g.
// as "path\\to\\smth" on Windows.
func (p *Path) String() string {
	if p.pkg {
		return p.s
	}
	return filepath.FromSlash(p.s)
}

// Absolute returns path as an absolute one.
// It returns an error if current directory cannot
// be detected.
func (p *Path) Absolute() (*Path, error) {
	// If the path is already absolute, return it as is.
	if path.IsAbs(p.s) {
		return &Path{s: p.s}, nil
	}

	// Otherwise, join with the current directory.
	curr, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	return &Path{
		s: path.Join(filepath.ToSlash(curr), p.s),
	}, nil
}

// Import returns the path as a correct absolute package import path.
func (p *Path) Import() (*Path, error) {
	// If the path is empty, return an error.
	if len(p.s) == 0 {
		return nil, errors.New("undefined path")
	}

	// Get an absolute form of the path.
	abs, err := p.Absolute()
	if err != nil {
		return nil, err
	}

	// Check every $GOPATH whether some of them
	// is a part of the path of absolute form.
	gopaths := filepath.SplitList(build.Default.GOPATH)
	for i := 0; i < len(gopaths); i++ {
		// Getting a normalized form (i.e. Unix style) of "$GOPATH/src".
		gopath := path.Join(filepath.ToSlash(gopaths[i]), "src")

		// Checking whether "$GOPATH/src" is a part of the absolute path.
		if res := strings.TrimPrefix(abs.s, gopath); res != abs.s {
			// Return the "$GOPATH/src"-less version of the path.
			// Make sure it has no starting or ending slashes
			// (they are not allowed in package import paths).
			return &Path{
				s:   strings.Trim(res, "/"),
				pkg: true,
			}, nil
		}
	}

	// If no import path returned so far,
	// requested path is not inside "$GOPATH/src".
	return nil, fmt.Errorf(`path "%s" is not inside "$GOPATH/src"`, p.String())
}
<|endoftext|>


<commit_before>package bitio

import (
	"bytes"
	"io"
	"math/rand"
	"testing"
	"time"
)

func TestReader(t *testing.T) {
	data := []byte{3, 255, 0xcc, 0x1a, 0xbc, 0xde, 0x80, 0x01, 0x02, 0xf8, 0x08, 0xf0}

	r := NewReader(bytes.NewBuffer(data))

	if b, err := r.ReadByte(); b != 3 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", b, 3, err)
	}
	if i, err := r.ReadBits(8); i != 255 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 255, err)
	}

	if i, err := r.ReadBits(4); i != 0xc || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 0xc, err)
	}

	if i, err := r.ReadBits(8); i != 0xc1 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 0xc1, err)
	}

	if i, err := r.ReadBits(20); i != 0xabcde || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 0xabcde, err)
	}

	if b, err := r.ReadBool(); !b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, false, err)
	}
	if b, err := r.ReadBool(); b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, true, err)
	}

	if n := r.Align(); n != 6 {
		t.Errorf("Got %v, want %v", n, 6)
	}

	s := make([]byte, 2)
	if n, err := r.Read(s); n != 2 || err != nil || !bytes.Equal(s, []byte{0x01, 0x02}) {
		t.Errorf("Got %v, want %v, error: %v", s, []byte{0x01, 0x02}, err)
	}

	if i, err := r.ReadBits(4); i != 0xf || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 0xf, err)
	}

	if n, err := r.Read(s); n != 2 || err != nil || !bytes.Equal(s, []byte{0x80, 0x8f}) {
		t.Errorf("Got %v, want %v, error: %v", s, []byte{0x80, 0x8f}, err)
	}
}

func TestWriter(t *testing.T) {
	b := &bytes.Buffer{}

	w := NewWriter(b)

	expected := []byte{0xc1, 0x7f, 0xac, 0x89, 0x24, 0x78, 0x01, 0x02, 0xf8, 0x08, 0xf0}

	errs := []error{}
	errs = append(errs, w.WriteByte(0xc1))
	errs = append(errs, w.WriteBool(false))
	errs = append(errs, w.WriteBits(0x3f, 6))
	errs = append(errs, w.WriteBool(true))
	errs = append(errs, w.WriteByte(0xac))
	errs = append(errs, w.WriteBits(0x01, 1))
	errs = append(errs, w.WriteBits(0x1248f, 20))

	if n, err := w.Align(); n != 3 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 3, err)
	}

	if n, err := w.Write([]byte{0x01, 0x02}); n != 2 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 2, err)
	}

	errs = append(errs, w.WriteBits(0x0f, 4))

	if n, err := w.Write([]byte{0x80, 0x8f}); n != 2 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 2, err)
	}

	if n, err := w.Align(); n != 4 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 4, err)
	}
	if n, err := w.Align(); n != 0 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 0, err)
	}

	errs = append(errs, w.Close())

	for _, v := range errs {
		if v != nil {
			t.Error("Got error:", v)
		}
	}

	if !bytes.Equal(b.Bytes(), expected) {
		t.Errorf("Got: %x, want: %x", b.Bytes(), expected)
	}
}

func TestReaderEOF(t *testing.T) {
	r := NewReader(bytes.NewBuffer([]byte{0x01}))

	if b, err := r.ReadByte(); b != 1 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", b, 1, err)
	}
	if _, err := r.ReadByte(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if _, err := r.ReadBool(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if _, err := r.ReadBits(1); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if n, err := r.Read(make([]byte, 2)); n != 0 || err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
}

func TestChain(t *testing.T) {
	b := &bytes.Buffer{}
	w := NewWriter(b)

	rand.Seed(time.Now().UnixNano())

	expected := make([]uint64, 100000)
	bits := make([]byte, len(expected))

	// Writing (generating)
	for i := range expected {
		expected[i] = uint64(rand.Int63())
		bits[i] = byte(1 + rand.Int31n(60))
		expected[i] &= uint64(1)<<bits[i] - 1
		w.WriteBits(expected[i], bits[i])
	}
	if err := w.Close(); err != nil {
		t.Error("Got error:", err)
	}

	r := NewReader(bytes.NewBuffer(b.Bytes()))

	// Reading (verifying)
	for i, v := range expected {
		if u, err := r.ReadBits(bits[i]); u != v || err != nil {
			t.Errorf("Idx: %d, Got: %x, want: %x, bits: %d, error: %v", i, u, v, bits[i], err)
		}
	}
}
<commit_msg>Increasing code coverage.<commit_after>package bitio

import (
	"bytes"
	"io"
	"math/rand"
	"testing"
	"time"
)

func TestReader(t *testing.T) {
	data := []byte{3, 255, 0xcc, 0x1a, 0xbc, 0xde, 0x80, 0x01, 0x02, 0xf8, 0x08, 0xf0}

	r := NewReader(bytes.NewBuffer(data))

	if b, err := r.ReadByte(); b != 3 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", b, 3, err)
	}
	if i, err := r.ReadBits(8); i != 255 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 255, err)
	}

	if i, err := r.ReadBits(4); i != 0xc || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 0xc, err)
	}

	if i, err := r.ReadBits(8); i != 0xc1 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 0xc1, err)
	}

	if i, err := r.ReadBits(20); i != 0xabcde || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 0xabcde, err)
	}

	if b, err := r.ReadBool(); !b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, false, err)
	}
	if b, err := r.ReadBool(); b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, true, err)
	}

	if n := r.Align(); n != 6 {
		t.Errorf("Got %v, want %v", n, 6)
	}

	s := make([]byte, 2)
	if n, err := r.Read(s); n != 2 || err != nil || !bytes.Equal(s, []byte{0x01, 0x02}) {
		t.Errorf("Got %v, want %v, error: %v", s, []byte{0x01, 0x02}, err)
	}

	if i, err := r.ReadBits(4); i != 0xf || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 0xf, err)
	}

	if n, err := r.Read(s); n != 2 || err != nil || !bytes.Equal(s, []byte{0x80, 0x8f}) {
		t.Errorf("Got %v, want %v, error: %v", s, []byte{0x80, 0x8f}, err)
	}
}

func TestWriter(t *testing.T) {
	b := &bytes.Buffer{}

	w := NewWriter(b)

	expected := []byte{0xc1, 0x7f, 0xac, 0x89, 0x24, 0x78, 0x01, 0x02, 0xf8, 0x08, 0xf0}

	errs := []error{}
	errs = append(errs, w.WriteByte(0xc1))
	errs = append(errs, w.WriteBool(false))
	errs = append(errs, w.WriteBits(0x3f, 6))
	errs = append(errs, w.WriteBool(true))
	errs = append(errs, w.WriteByte(0xac))
	errs = append(errs, w.WriteBits(0x01, 1))
	errs = append(errs, w.WriteBits(0x1248f, 20))

	if n, err := w.Align(); n != 3 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 3, err)
	}

	if n, err := w.Write([]byte{0x01, 0x02}); n != 2 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 2, err)
	}

	errs = append(errs, w.WriteBits(0x0f, 4))

	if n, err := w.Write([]byte{0x80, 0x8f}); n != 2 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 2, err)
	}

	if n, err := w.Align(); n != 4 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 4, err)
	}
	if n, err := w.Align(); n != 0 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", n, 0, err)
	}

	errs = append(errs, w.Close())

	for _, v := range errs {
		if v != nil {
			t.Error("Got error:", v)
		}
	}

	if !bytes.Equal(b.Bytes(), expected) {
		t.Errorf("Got: %x, want: %x", b.Bytes(), expected)
	}
}

func TestReaderEOF(t *testing.T) {
	r := NewReader(bytes.NewBuffer([]byte{0x01}))

	if b, err := r.ReadByte(); b != 1 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", b, 1, err)
	}
	if _, err := r.ReadByte(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if _, err := r.ReadBool(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if _, err := r.ReadBits(1); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if n, err := r.Read(make([]byte, 2)); n != 0 || err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}

	r = NewReader(bytes.NewBuffer([]byte{0x01}))
	if _, err := r.ReadBits(17); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}

	// Byte spreading byte boundary (readUnalignedByte)
	r = NewReader(bytes.NewBuffer([]byte{0xc1, 0x01}))
	if b, err := r.ReadBool(); !b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, false, err)
	}
	if b, err := r.ReadByte(); b != 0x82 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", b, 0x82, err)
	}
	// readUnalignedByte resulting in EOF
	if _, err := r.ReadByte(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
}

func TestChain(t *testing.T) {
	b := &bytes.Buffer{}
	w := NewWriter(b)

	rand.Seed(time.Now().UnixNano())

	expected := make([]uint64, 100000)
	bits := make([]byte, len(expected))

	// Writing (generating)
	for i := range expected {
		expected[i] = uint64(rand.Int63())
		bits[i] = byte(1 + rand.Int31n(60))
		expected[i] &= uint64(1)<<bits[i] - 1
		w.WriteBits(expected[i], bits[i])
	}
	if err := w.Close(); err != nil {
		t.Error("Got error:", err)
	}

	r := NewReader(bytes.NewBuffer(b.Bytes()))

	// Reading (verifying)
	for i, v := range expected {
		if u, err := r.ReadBits(bits[i]); u != v || err != nil {
			t.Errorf("Idx: %d, Got: %x, want: %x, bits: %d, error: %v", i, u, v, bits[i], err)
		}
	}
}
<|endoftext|>


<commit_before>package ipam

import (
	"context"
	"fmt"
	"net"
	"sync"
	"testing"

	"github.com/docker/libnetwork/ipamapi"
	"github.com/stretchr/testify/assert"
	"golang.org/x/sync/semaphore"
)

const (
	all = iota
	even
	odd
)

type releaseMode uint

type testContext struct {
	a      *Allocator
	opts   map[string]string
	ipList []*net.IPNet
	ipMap  map[string]bool
	pid    string
	maxIP  int
}

func newTestContext(t *testing.T, mask int, options map[string]string) *testContext {
	a, err := getAllocator(true)
	if err != nil {
		t.Fatal(err)
	}
	a.addrSpaces["giallo"] = &addrSpace{
		id:      dsConfigKey + "/" + "giallo",
		ds:      a.addrSpaces[localAddressSpace].ds,
		alloc:   a.addrSpaces[localAddressSpace].alloc,
		scope:   a.addrSpaces[localAddressSpace].scope,
		subnets: map[SubnetKey]*PoolData{},
	}

	network := fmt.Sprintf("192.168.100.0/%d", mask)
	// total ips 2^(32-mask) - 2 (network and broadcast)
	totalIps := 1<<uint(32-mask) - 2

	pid, _, _, err := a.RequestPool("giallo", network, "", nil, false)
	if err != nil {
		t.Fatal(err)
	}

	return &testContext{
		a:      a,
		opts:   options,
		ipList: make([]*net.IPNet, 0, totalIps),
		ipMap:  make(map[string]bool),
		pid:    pid,
		maxIP:  totalIps,
	}
}

func TestDebug(t *testing.T) {
	tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
	tctx.a.RequestAddress(tctx.pid, nil, map[string]string{ipamapi.AllocSerialPrefix: "true"})
	tctx.a.RequestAddress(tctx.pid, nil, map[string]string{ipamapi.AllocSerialPrefix: "true"})
}

func TestFullAllocateRelease(t *testing.T) {
	for _, parallelism := range []int64{2, 4, 8} {
		for _, mask := range []int{29, 25, 24, 21} {
			tctx := newTestContext(t, mask, map[string]string{ipamapi.AllocSerialPrefix: "true"})
			allocate(t, tctx, parallelism)
			release(t, tctx, all, parallelism)
		}
	}
}

func TestOddAllocateRelease(t *testing.T) {
	for _, parallelism := range []int64{2, 4, 8} {
		for _, mask := range []int{29, 25, 24, 21} {
			tctx := newTestContext(t, mask, map[string]string{ipamapi.AllocSerialPrefix: "true"})
			allocate(t, tctx, parallelism)
			release(t, tctx, odd, parallelism)
		}
	}
}

func TestFullAllocateSerialReleaseParallel(t *testing.T) {
	for _, parallelism := range []int64{1, 4, 8} {
		tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
		allocate(t, tctx, 1)
		release(t, tctx, all, parallelism)
	}
}

func TestOddAllocateSerialReleaseParallel(t *testing.T) {
	for _, parallelism := range []int64{1, 4, 8} {
		tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
		allocate(t, tctx, 1)
		release(t, tctx, odd, parallelism)
	}
}

func TestEvenAllocateSerialReleaseParallel(t *testing.T) {
	for _, parallelism := range []int64{1, 4, 8} {
		tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
		allocate(t, tctx, 1)
		release(t, tctx, even, parallelism)
	}
}

func allocate(t *testing.T, tctx *testContext, parallel int64) {
	// Allocate the whole space
	parallelExec := semaphore.NewWeighted(parallel)
	routineNum := tctx.maxIP + 10
	ch := make(chan *net.IPNet, routineNum)
	var id int
	var wg sync.WaitGroup
	// routine loop
	for {
		wg.Add(1)
		go func(id int) {
			parallelExec.Acquire(context.Background(), 1)
			ip, _, _ := tctx.a.RequestAddress(tctx.pid, nil, tctx.opts)
			ch <- ip
			parallelExec.Release(1)
			wg.Done()
		}(id)
		id++
		if id == routineNum {
			break
		}
	}

	// give time to all the go routines to finish
	wg.Wait()

	// process results
	for i := 0; i < routineNum; i++ {
		ip := <-ch
		if ip == nil {
			continue
		}
		if there, ok := tctx.ipMap[ip.String()]; ok && there {
			t.Fatalf("Got duplicate IP %s", ip.String())
			break
		}
		tctx.ipList = append(tctx.ipList, ip)
		tctx.ipMap[ip.String()] = true
	}

	assert.Len(t, tctx.ipList, tctx.maxIP)
	if len(tctx.ipList) != tctx.maxIP {
		t.Fatal("missmatch number allocation")
	}
}

func release(t *testing.T, tctx *testContext, mode releaseMode, parallel int64) {
	var startIndex, increment, stopIndex, length int
	switch mode {
	case all:
		startIndex = 0
		increment = 1
		stopIndex = tctx.maxIP - 1
		length = tctx.maxIP
	case odd, even:
		if mode == odd {
			startIndex = 1
		}
		increment = 2
		stopIndex = tctx.maxIP - 1
		length = tctx.maxIP / 2
		if tctx.maxIP%2 > 0 {
			length++
		}
	default:
		t.Fatal("unsupported mode yet")
	}

	ipIndex := make([]int, 0, length)
	// calculate the index to release from the ipList
	for i := startIndex; ; i += increment {
		ipIndex = append(ipIndex, i)
		if i+increment > stopIndex {
			break
		}
	}

	var id int
	parallelExec := semaphore.NewWeighted(parallel)
	ch := make(chan *net.IPNet, len(ipIndex))
	wg := sync.WaitGroup{}
	for index := range ipIndex {
		wg.Add(1)
		go func(id, index int) {
			parallelExec.Acquire(context.Background(), 1)
			// logrus.Errorf("index %v", index)
			// logrus.Errorf("list %v", tctx.ipList)
			err := tctx.a.ReleaseAddress(tctx.pid, tctx.ipList[index].IP)
			if err != nil {
				t.Fatalf("routine %d got %v", id, err)
			}
			ch <- tctx.ipList[index]
			parallelExec.Release(1)
			wg.Done()
		}(id, index)
		id++
	}
	wg.Wait()

	for i := 0; i < len(ipIndex); i++ {
		ip := <-ch

		// check if it is really free
		_, _, err := tctx.a.RequestAddress(tctx.pid, ip.IP, nil)
		assert.NoError(t, err, "ip %v not properly released", ip)
		if err != nil {
			t.Fatalf("ip %v not properly released, error:%v", ip, err)
		}
		err = tctx.a.ReleaseAddress(tctx.pid, ip.IP)
		assert.NoError(t, err)

		if there, ok := tctx.ipMap[ip.String()]; !ok || !there {
			t.Fatalf("ip %v got double deallocated", ip)
		}
		tctx.ipMap[ip.String()] = false
		for j, v := range tctx.ipList {
			if v == ip {
				tctx.ipList = append(tctx.ipList[:j], tctx.ipList[j+1:]...)
				break
			}
		}
	}

	assert.Len(t, tctx.ipList, tctx.maxIP-length)
}
<commit_msg>Add test for parallel request pool<commit_after>package ipam

import (
	"context"
	"fmt"
	"math/rand"
	"net"
	"sort"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/docker/libnetwork/ipamapi"
	"github.com/stretchr/testify/assert"
	"golang.org/x/sync/semaphore"
)

const (
	all = iota
	even
	odd
)

type releaseMode uint

type testContext struct {
	a      *Allocator
	opts   map[string]string
	ipList []*net.IPNet
	ipMap  map[string]bool
	pid    string
	maxIP  int
}

func newTestContext(t *testing.T, mask int, options map[string]string) *testContext {
	a, err := getAllocator(false)
	if err != nil {
		t.Fatal(err)
	}
	a.addrSpaces["giallo"] = &addrSpace{
		id:      dsConfigKey + "/" + "giallo",
		ds:      a.addrSpaces[localAddressSpace].ds,
		alloc:   a.addrSpaces[localAddressSpace].alloc,
		scope:   a.addrSpaces[localAddressSpace].scope,
		subnets: map[SubnetKey]*PoolData{},
	}

	network := fmt.Sprintf("192.168.100.0/%d", mask)
	// total ips 2^(32-mask) - 2 (network and broadcast)
	totalIps := 1<<uint(32-mask) - 2

	pid, _, _, err := a.RequestPool("giallo", network, "", nil, false)
	if err != nil {
		t.Fatal(err)
	}

	return &testContext{
		a:      a,
		opts:   options,
		ipList: make([]*net.IPNet, 0, totalIps),
		ipMap:  make(map[string]bool),
		pid:    pid,
		maxIP:  totalIps,
	}
}

func TestDebug(t *testing.T) {
	tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
	tctx.a.RequestAddress(tctx.pid, nil, map[string]string{ipamapi.AllocSerialPrefix: "true"})
	tctx.a.RequestAddress(tctx.pid, nil, map[string]string{ipamapi.AllocSerialPrefix: "true"})
}

type op struct {
	id   int32
	add  bool
	name string
}

func (o *op) String() string {
	return fmt.Sprintf("%+v", *o)
}

func TestRequestPoolParallel(t *testing.T) {
	a, err := getAllocator(false)
	if err != nil {
		t.Fatal(err)
	}
	var operationIndex int32
	ch := make(chan *op, 240)
	for i := 0; i < 120; i++ {
		go func(t *testing.T, a *Allocator, ch chan *op) {
			name, _, _, err := a.RequestPool("GlobalDefault", "", "", nil, false)
			if err != nil {
				t.Fatalf("request error %v", err)
			}
			idx := atomic.AddInt32(&operationIndex, 1)
			ch <- &op{idx, true, name}
			time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
			idx = atomic.AddInt32(&operationIndex, 1)
			err = a.ReleasePool(name)
			if err != nil {
				t.Fatalf("relase error %v", err)
			}
			ch <- &op{idx, false, name}
		}(t, a, ch)
	}

	// map of events
	m := make(map[string][]*op)
	for i := 0; i < 240; i++ {
		x := <-ch
		ops, ok := m[x.name]
		if !ok {
			ops = make([]*op, 0, 10)
		}
		ops = append(ops, x)
		m[x.name] = ops
	}

	// Post processing to avoid event reordering on the channel
	for pool, ops := range m {
		sort.Slice(ops[:], func(i, j int) bool {
			return ops[i].id < ops[j].id
		})
		expected := true
		for _, op := range ops {
			if op.add != expected {
				t.Fatalf("Operations for %v not valid %v, operations %v", pool, op, ops)
			}
			expected = !expected
		}
	}
}

func TestFullAllocateRelease(t *testing.T) {
	for _, parallelism := range []int64{2, 4, 8} {
		for _, mask := range []int{29, 25, 24, 21} {
			tctx := newTestContext(t, mask, map[string]string{ipamapi.AllocSerialPrefix: "true"})
			allocate(t, tctx, parallelism)
			release(t, tctx, all, parallelism)
		}
	}
}

func TestOddAllocateRelease(t *testing.T) {
	for _, parallelism := range []int64{2, 4, 8} {
		for _, mask := range []int{29, 25, 24, 21} {
			tctx := newTestContext(t, mask, map[string]string{ipamapi.AllocSerialPrefix: "true"})
			allocate(t, tctx, parallelism)
			release(t, tctx, odd, parallelism)
		}
	}
}

func TestFullAllocateSerialReleaseParallel(t *testing.T) {
	for _, parallelism := range []int64{1, 4, 8} {
		tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
		allocate(t, tctx, 1)
		release(t, tctx, all, parallelism)
	}
}

func TestOddAllocateSerialReleaseParallel(t *testing.T) {
	for _, parallelism := range []int64{1, 4, 8} {
		tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
		allocate(t, tctx, 1)
		release(t, tctx, odd, parallelism)
	}
}

func TestEvenAllocateSerialReleaseParallel(t *testing.T) {
	for _, parallelism := range []int64{1, 4, 8} {
		tctx := newTestContext(t, 23, map[string]string{ipamapi.AllocSerialPrefix: "true"})
		allocate(t, tctx, 1)
		release(t, tctx, even, parallelism)
	}
}

func allocate(t *testing.T, tctx *testContext, parallel int64) {
	// Allocate the whole space
	parallelExec := semaphore.NewWeighted(parallel)
	routineNum := tctx.maxIP + 10
	ch := make(chan *net.IPNet, routineNum)
	var id int
	var wg sync.WaitGroup
	// routine loop
	for {
		wg.Add(1)
		go func(id int) {
			parallelExec.Acquire(context.Background(), 1)
			ip, _, _ := tctx.a.RequestAddress(tctx.pid, nil, tctx.opts)
			ch <- ip
			parallelExec.Release(1)
			wg.Done()
		}(id)
		id++
		if id == routineNum {
			break
		}
	}

	// give time to all the go routines to finish
	wg.Wait()

	// process results
	for i := 0; i < routineNum; i++ {
		ip := <-ch
		if ip == nil {
			continue
		}
		if there, ok := tctx.ipMap[ip.String()]; ok && there {
			t.Fatalf("Got duplicate IP %s", ip.String())
			break
		}
		tctx.ipList = append(tctx.ipList, ip)
		tctx.ipMap[ip.String()] = true
	}

	assert.Len(t, tctx.ipList, tctx.maxIP)
	if len(tctx.ipList) != tctx.maxIP {
		t.Fatal("missmatch number allocation")
	}
}

func release(t *testing.T, tctx *testContext, mode releaseMode, parallel int64) {
	var startIndex, increment, stopIndex, length int
	switch mode {
	case all:
		startIndex = 0
		increment = 1
		stopIndex = tctx.maxIP - 1
		length = tctx.maxIP
	case odd, even:
		if mode == odd {
			startIndex = 1
		}
		increment = 2
		stopIndex = tctx.maxIP - 1
		length = tctx.maxIP / 2
		if tctx.maxIP%2 > 0 {
			length++
		}
	default:
		t.Fatal("unsupported mode yet")
	}

	ipIndex := make([]int, 0, length)
	// calculate the index to release from the ipList
	for i := startIndex; ; i += increment {
		ipIndex = append(ipIndex, i)
		if i+increment > stopIndex {
			break
		}
	}

	var id int
	parallelExec := semaphore.NewWeighted(parallel)
	ch := make(chan *net.IPNet, len(ipIndex))
	wg := sync.WaitGroup{}
	for index := range ipIndex {
		wg.Add(1)
		go func(id, index int) {
			parallelExec.Acquire(context.Background(), 1)
			// logrus.Errorf("index %v", index)
			// logrus.Errorf("list %v", tctx.ipList)
			err := tctx.a.ReleaseAddress(tctx.pid, tctx.ipList[index].IP)
			if err != nil {
				t.Fatalf("routine %d got %v", id, err)
			}
			ch <- tctx.ipList[index]
			parallelExec.Release(1)
			wg.Done()
		}(id, index)
		id++
	}
	wg.Wait()

	for i := 0; i < len(ipIndex); i++ {
		ip := <-ch

		// check if it is really free
		_, _, err := tctx.a.RequestAddress(tctx.pid, ip.IP, nil)
		assert.NoError(t, err, "ip %v not properly released", ip)
		if err != nil {
			t.Fatalf("ip %v not properly released, error:%v", ip, err)
		}
		err = tctx.a.ReleaseAddress(tctx.pid, ip.IP)
		assert.NoError(t, err)

		if there, ok := tctx.ipMap[ip.String()]; !ok || !there {
			t.Fatalf("ip %v got double deallocated", ip)
		}
		tctx.ipMap[ip.String()] = false
		for j, v := range tctx.ipList {
			if v == ip {
				tctx.ipList = append(tctx.ipList[:j], tctx.ipList[j+1:]...)
				break
			}
		}
	}

	assert.Len(t, tctx.ipList, tctx.maxIP-length)
}
<|endoftext|>