<commit_before>/*
Copyright © 2014 Brad Ackerman.

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 market

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"

	"github.com/backerman/evego/pkg/dbaccess"
	"github.com/backerman/evego/pkg/eveapi"
	"github.com/backerman/evego/pkg/types"
)

type eveCentral struct {
	db       dbaccess.EveDatabase
	xmlAPI   eveapi.EveAPI
	endpoint *url.URL
	http     http.Client
}

// EveCentral returns an interface to the EVE-Central API.
// It takes as input an EveDatabase object and an HTTP endpoint;
// the latter should be http://api.eve-central.com/api/quicklook
// for the production EVE-Central instance.
func EveCentral(db dbaccess.EveDatabase, xmlAPI eveapi.EveAPI, endpoint string) EveMarket {
	epURL, err := url.Parse(endpoint)
	if err != nil {
		log.Fatalf("Invalid URL %v passed for Eve-Central endpoint: %v", endpoint, err)
	}
	ec := eveCentral{db: db, endpoint: epURL, xmlAPI: xmlAPI}
	return &ec
}

func (e *eveCentral) getURL(u string) ([]byte, error) {
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("User-Agent", "evego (https://github.com/backerman/evego)")
	resp, err := e.http.Do(req)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	return body, err
}

type order struct {
	RegionID          int     `xml:"region"`
	StationID         int     `xml:"station"`
	StationName       string  `xml:"station_name"`
	Security          float64 `xml:"security"`
	Range             int     `xml:"range"`
	Price             float64 `xml:"price"`
	QuantityAvailable int     `xml:"vol_remain"`
	MinimumVolume     int     `xml:"min_volume"`
	ExpirationDate    string  `xml:"expires"`
	ReportedTime      string  `xml:"reported_time"`
}

type quicklook struct {
	SellOrders []order `xml:"quicklook>sell_orders>order"`
	BuyOrders  []order `xml:"quicklook>buy_orders>order"`
}

func (e *eveCentral) processOrders(data *quicklook, item *types.Item, t types.OrderType) []types.Order {
	var toProcess *[]order
	stationCache := make(map[int]*types.Station)
	switch t {
	case types.Buy:
		toProcess = &data.BuyOrders
	case types.Sell:
		toProcess = &data.SellOrders
	}
	results := []types.Order{}
	for _, o := range *toProcess {
		if stationCache[o.StationID] == nil {
			sta, err := e.db.StationForID(o.StationID)
			if err != nil {
				// If it's not in the static databse, it's an outpost.
				// FIXME Need to expire outposts.
				sta, err = e.xmlAPI.OutpostForID(o.StationID)
				if err != nil {
					// Make a dummy station.
					sta = &types.Station{
						Name: fmt.Sprintf("Unknown Station (ID %d)", o.StationID),
						ID:   o.StationID,
					}
				}
			}
			stationCache[o.StationID] = sta
		}
		oTime, _ := time.Parse("2006-01-02", o.ExpirationDate)
		newOrder := types.Order{
			Type:       t,
			Item:       item,
			Quantity:   o.QuantityAvailable,
			Station:    stationCache[o.StationID],
			Price:      o.Price,
			Expiration: oTime,
		}
		if t == types.Buy {
			// Set the fields specific to buy orders.
			newOrder.MinQuantity = o.MinimumVolume
			switch o.Range {
			case 32767:
				newOrder.JumpRange = types.BuyRegion
			case -1:
				newOrder.JumpRange = types.BuyStation
			case 0:
				newOrder.JumpRange = types.BuySystem
			default:
				newOrder.JumpRange = types.BuyNumberJumps
				newOrder.NumJumps = o.Range
			}
		}
		results = append(results, newOrder)
	}
	return results
}

func (e *eveCentral) OrdersForItem(item *types.Item, location string, orderType types.OrderType) (*[]types.Order, error) {
	var (
		system *types.SolarSystem
		region *types.Region
		err    error
	)
	system, err = e.db.SolarSystemForName(location)
	if err != nil {
		// Not a system or unable to look up. Try region.
		region, err = e.db.RegionForName(location)
		if err != nil {
			// Still can't find it. Return an error.
			return nil, err
		}
	}
	query := url.Values{}
	if region != nil {
		query.Set("regionLimit", fmt.Sprintf("%d", region.ID))
	} else {
		query.Set("usesystem", fmt.Sprintf("%d", system.ID))
	}
	query.Set("typeid", fmt.Sprintf("%d", item.ID))
	e.endpoint.RawQuery = query.Encode()
	orderXML, err := e.getURL(e.endpoint.String())
	if err != nil {
		return nil, err
	}
	orders := &quicklook{}

	err = xml.Unmarshal(orderXML, orders)
	if err != nil {
		return nil, err
	}

	// Convert returned XML struct into what we present to rest of library.
	results := []types.Order{}
	switch orderType {
	case types.AllOrders:
		// The order here matters, if only because it's the order that the
		// orders are presented by EVE Central and therefore the order in which
		// the test cases expect results.
		results = append(results, e.processOrders(orders, item, types.Sell)...)
		results = append(results, e.processOrders(orders, item, types.Buy)...)
	default:
		results = e.processOrders(orders, item, orderType)
	}
	return &results, nil
}

func (e *eveCentral) Close() error {
	return nil
}
<commit_msg>Remove spurious comment.<commit_after>/*
Copyright © 2014 Brad Ackerman.

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 market

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"

	"github.com/backerman/evego/pkg/dbaccess"
	"github.com/backerman/evego/pkg/eveapi"
	"github.com/backerman/evego/pkg/types"
)

type eveCentral struct {
	db       dbaccess.EveDatabase
	xmlAPI   eveapi.EveAPI
	endpoint *url.URL
	http     http.Client
}

// EveCentral returns an interface to the EVE-Central API.
// It takes as input an EveDatabase object and an HTTP endpoint;
// the latter should be http://api.eve-central.com/api/quicklook
// for the production EVE-Central instance.
func EveCentral(db dbaccess.EveDatabase, xmlAPI eveapi.EveAPI, endpoint string) EveMarket {
	epURL, err := url.Parse(endpoint)
	if err != nil {
		log.Fatalf("Invalid URL %v passed for Eve-Central endpoint: %v", endpoint, err)
	}
	ec := eveCentral{db: db, endpoint: epURL, xmlAPI: xmlAPI}
	return &ec
}

func (e *eveCentral) getURL(u string) ([]byte, error) {
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("User-Agent", "evego (https://github.com/backerman/evego)")
	resp, err := e.http.Do(req)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	return body, err
}

type order struct {
	RegionID          int     `xml:"region"`
	StationID         int     `xml:"station"`
	StationName       string  `xml:"station_name"`
	Security          float64 `xml:"security"`
	Range             int     `xml:"range"`
	Price             float64 `xml:"price"`
	QuantityAvailable int     `xml:"vol_remain"`
	MinimumVolume     int     `xml:"min_volume"`
	ExpirationDate    string  `xml:"expires"`
	ReportedTime      string  `xml:"reported_time"`
}

type quicklook struct {
	SellOrders []order `xml:"quicklook>sell_orders>order"`
	BuyOrders  []order `xml:"quicklook>buy_orders>order"`
}

func (e *eveCentral) processOrders(data *quicklook, item *types.Item, t types.OrderType) []types.Order {
	var toProcess *[]order
	stationCache := make(map[int]*types.Station)
	switch t {
	case types.Buy:
		toProcess = &data.BuyOrders
	case types.Sell:
		toProcess = &data.SellOrders
	}
	results := []types.Order{}
	for _, o := range *toProcess {
		if stationCache[o.StationID] == nil {
			sta, err := e.db.StationForID(o.StationID)
			if err != nil {
				// If it's not in the static databse, it's an outpost.
				sta, err = e.xmlAPI.OutpostForID(o.StationID)
				if err != nil {
					// Make a dummy station.
					sta = &types.Station{
						Name: fmt.Sprintf("Unknown Station (ID %d)", o.StationID),
						ID:   o.StationID,
					}
				}
			}
			stationCache[o.StationID] = sta
		}
		oTime, _ := time.Parse("2006-01-02", o.ExpirationDate)
		newOrder := types.Order{
			Type:       t,
			Item:       item,
			Quantity:   o.QuantityAvailable,
			Station:    stationCache[o.StationID],
			Price:      o.Price,
			Expiration: oTime,
		}
		if t == types.Buy {
			// Set the fields specific to buy orders.
			newOrder.MinQuantity = o.MinimumVolume
			switch o.Range {
			case 32767:
				newOrder.JumpRange = types.BuyRegion
			case -1:
				newOrder.JumpRange = types.BuyStation
			case 0:
				newOrder.JumpRange = types.BuySystem
			default:
				newOrder.JumpRange = types.BuyNumberJumps
				newOrder.NumJumps = o.Range
			}
		}
		results = append(results, newOrder)
	}
	return results
}

func (e *eveCentral) OrdersForItem(item *types.Item, location string, orderType types.OrderType) (*[]types.Order, error) {
	var (
		system *types.SolarSystem
		region *types.Region
		err    error
	)
	system, err = e.db.SolarSystemForName(location)
	if err != nil {
		// Not a system or unable to look up. Try region.
		region, err = e.db.RegionForName(location)
		if err != nil {
			// Still can't find it. Return an error.
			return nil, err
		}
	}
	query := url.Values{}
	if region != nil {
		query.Set("regionLimit", fmt.Sprintf("%d", region.ID))
	} else {
		query.Set("usesystem", fmt.Sprintf("%d", system.ID))
	}
	query.Set("typeid", fmt.Sprintf("%d", item.ID))
	e.endpoint.RawQuery = query.Encode()
	orderXML, err := e.getURL(e.endpoint.String())
	if err != nil {
		return nil, err
	}
	orders := &quicklook{}

	err = xml.Unmarshal(orderXML, orders)
	if err != nil {
		return nil, err
	}

	// Convert returned XML struct into what we present to rest of library.
	results := []types.Order{}
	switch orderType {
	case types.AllOrders:
		// The order here matters, if only because it's the order that the
		// orders are presented by EVE Central and therefore the order in which
		// the test cases expect results.
		results = append(results, e.processOrders(orders, item, types.Sell)...)
		results = append(results, e.processOrders(orders, item, types.Buy)...)
	default:
		results = e.processOrders(orders, item, orderType)
	}
	return &results, nil
}

func (e *eveCentral) Close() error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bitfx2/exchange"
	"bitfx2/okcoin"
	// "github.com/davecgh/go-spew/spew"
	"math"
	"testing"
)

func init() {
	cfg.Sec.MaxArb = .02
	cfg.Sec.MinArb = -.01
	cfg.Sec.MinOrder = 25
	cfg.Sec.MaxOrder = 50
}

type neededArb struct {
	buyExgPos, sellExgPos, arb float64
}

func TestCalculateNeededArb(t *testing.T) {
	neededArbTests := []neededArb{
		{500, -500, .02},
		{-500, 500, -.01},
		{500, 500, .005},
		{-100, -100, .005},
		{0, 0, .005},
		{-250, 250, -.0025},
		{250, -250, .0125},
		{100, -100, .008},
		{0, -200, .008},
		{-200, 0, .002},
		{-100, 100, .002},
	}

	for _, neededArb := range neededArbTests {
		arb := calcNeededArb(neededArb.buyExgPos, neededArb.sellExgPos, 500, 500)
		if math.Abs(arb-neededArb.arb) > .000001 {
			t.Errorf("For %.4f / %.4f expect %.4f, got %.4f\n", neededArb.buyExgPos, neededArb.sellExgPos, neededArb.arb, arb)
		}
	}
}

func TestFilterBook(t *testing.T) {
	testBook := exchange.Book{
		Exg: okcoin.New("", "", "", "usd", 1, 0.002, 500),
		Bids: exchange.BidItems{
			0: {Price: 1.90, Amount: 10},
			1: {Price: 1.80, Amount: 10},
			2: {Price: 1.70, Amount: 100},
		},
		Asks: exchange.AskItems{
			0: {Price: 2.10, Amount: 10},
			1: {Price: 2.20, Amount: 20},
			2: {Price: 2.30, Amount: 10},
		},
	}

	market := filterBook(testBook)

	if math.Abs(market.bid.orderPrice-1.70) > .000001 {
		t.Errorf("Wrong bid order price")
	}
	if math.Abs(market.bid.amount-50) > .000001 {
		t.Errorf("Wrong bid amount")
	}
	adjPrice := ((1.90*10 + 1.80*10 + 1.70*30) / 50) * (1 - .002)
	if math.Abs(market.bid.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong bid adjusted price")
	}
	if math.Abs(market.ask.orderPrice-2.20) > .000001 {
		t.Errorf("Wrong ask order price")
	}
	if math.Abs(market.ask.amount-30) > .000001 {
		t.Errorf("Wrong ask amount")
	}
	adjPrice = ((2.10*10 + 2.20*20) / 30) * (1 + .002)
	if math.Abs(market.ask.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong ask adjusted price")
	}

	testBook = exchange.Book{
		Exg: okcoin.New("", "", "", "usd", 2, 0.002, 500),
		Bids: exchange.BidItems{
			0: {Price: 1.90, Amount: 30},
			1: {Price: 1.80, Amount: 10},
			2: {Price: 1.70, Amount: 100},
		},
		Asks: exchange.AskItems{
			0: {Price: 2.10, Amount: 100},
			1: {Price: 2.20, Amount: 20},
			2: {Price: 2.30, Amount: 10},
		},
	}

	market = filterBook(testBook)

	if math.Abs(market.bid.orderPrice-1.90) > .000001 {
		t.Errorf("Wrong bid order price")
	}
	if math.Abs(market.bid.amount-30) > .000001 {
		t.Errorf("Wrong bid amount")
	}
	adjPrice = 1.90 * (1 - .002)
	if math.Abs(market.bid.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong bid adjusted price")
	}
	if math.Abs(market.ask.orderPrice-2.10) > .000001 {
		t.Errorf("Wrong ask order price")
	}
	if math.Abs(market.ask.amount-50) > .000001 {
		t.Errorf("Wrong ask amount")
	}
	adjPrice = 2.10 * (1 + .002)
	if math.Abs(market.ask.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong ask adjusted price")
	}
}

func TestFindBestBid(t *testing.T) {
	markets := make(map[exchange.Exchange]filteredBook)
	exg1 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg2 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg3 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	markets[exg1] = filteredBook{bid: market{adjPrice: 2.00, amount: 500}}
	markets[exg2] = filteredBook{bid: market{adjPrice: 1.99}}
	markets[exg3] = filteredBook{bid: market{adjPrice: 1.98}}
	if math.Abs(findBestBid(markets).adjPrice-2.00) > .000001 {
		t.Error("Returned wrong best bid")
	}
	exg1.SetPosition(-490)
	if math.Abs(findBestBid(markets).adjPrice-1.99) > .000001 {
		t.Error("Returned wrong best bid after position update")
	}
	exg1.SetPosition(-250)
	if math.Abs(findBestBid(markets).amount-250) > .000001 {
		t.Error("Returned wrong best bid amount after position update")
	}
}

func TestFindBestAsk(t *testing.T) {
	markets := make(map[exchange.Exchange]filteredBook)
	exg1 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg2 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg3 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	markets[exg1] = filteredBook{ask: market{adjPrice: 1.98, amount: 500}}
	markets[exg2] = filteredBook{ask: market{adjPrice: 1.99}}
	markets[exg3] = filteredBook{ask: market{adjPrice: 2.00}}
	if math.Abs(findBestAsk(markets).adjPrice-1.98) > .000001 {
		t.Error("Returned wrong best ask")
	}
	exg1.SetPosition(490)
	if math.Abs(findBestAsk(markets).adjPrice-1.99) > .000001 {
		t.Error("Returned wrong best ask after position update")
	}
	exg1.SetPosition(250)
	if math.Abs(findBestAsk(markets).amount-250) > .000001 {
		t.Error("Returned wrong best ask after position update")
	}
}

func TestFindBestArb(t *testing.T) {
	// No opportunity
	markets := make(map[exchange.Exchange]filteredBook)
	exg1 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg2 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg3 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	markets[exg1] = filteredBook{
		bid: market{adjPrice: 1.98, amount: 50, exg: exg1},
		ask: market{adjPrice: 2.00, amount: 50, exg: exg1},
	}
	markets[exg2] = filteredBook{
		bid: market{adjPrice: 1.99, amount: 50, exg: exg2},
		ask: market{adjPrice: 2.01, amount: 50, exg: exg2},
	}
	markets[exg3] = filteredBook{
		bid: market{adjPrice: 2.00, amount: 50, exg: exg3},
		ask: market{adjPrice: 2.02, amount: 50, exg: exg3},
	}
	if _, _, exists := findBestArb(markets); exists {
		t.Errorf("Should be no arb opportunity")
	}
	// Change positions to create an exit opportunity
	exg1.SetPosition(-500)
	exg3.SetPosition(500)
	bestBid, bestAsk, exists := findBestArb(markets)
	if !exists || bestBid.exg != exg3 || bestAsk.exg != exg1 {
		t.Errorf("Should be an exit opportunity after position update")
	}
	exg1.SetPosition(0)
	exg3.SetPosition(0)

	// Create an arb opportunity
	markets[exg1] = filteredBook{
		bid: market{adjPrice: 2.03, amount: 50, exg: exg1},
		ask: market{adjPrice: 2.04, amount: 50, exg: exg1},
	}
	markets[exg2] = filteredBook{
		bid: market{adjPrice: 2.04, amount: 50, exg: exg2},
		ask: market{adjPrice: 2.05, amount: 50, exg: exg2},
	}
	markets[exg3] = filteredBook{
		bid: market{adjPrice: 1.99, amount: 50, exg: exg3},
		ask: market{adjPrice: 2.00, amount: 50, exg: exg3},
	}
	bestBid, bestAsk, exists = findBestArb(markets)
	if !exists || bestBid.exg != exg2 || bestAsk.exg != exg3 {
		t.Errorf("Should be an arb opportunity")
	}

	// Set exg3 postion to only allow for 30 more
	exg3.SetPosition(470)
	_, bestAsk, _ = findBestArb(markets)
	if math.Abs(bestAsk.amount-30) > .000001 {
		t.Errorf("Should be a decrease in best ask amount")
	}

	// Change exg3 postion
	exg2.SetPosition(-500)
	bestBid, _, _ = findBestArb(markets)
	if bestBid.exg != exg1 {
		t.Errorf("Best bid exchange should have changed")
	}
}
<commit_msg>incorporate FX into existing tests<commit_after>package main

import (
	"bitfx2/bitfinex"
	"bitfx2/exchange"
	"bitfx2/okcoin"
	// "github.com/davecgh/go-spew/spew"
	"math"
	"testing"
)

func init() {
	cfg.Sec.MaxArb = .02
	cfg.Sec.MinArb = -.01
	cfg.Sec.FXPremium = .01
	cfg.Sec.MinOrder = 25
	cfg.Sec.MaxOrder = 50
}

type neededArb struct {
	buyExgPos, sellExgPos, arb float64
}

func TestCalculateNeededArb(t *testing.T) {
	// Test without FX
	neededArbTests := []neededArb{
		{500, -500, .02},
		{-500, 500, -.01},
		{500, 500, .005},
		{-100, -100, .005},
		{0, 0, .005},
		{-250, 250, -.0025},
		{250, -250, .0125},
		{100, -100, .008},
		{0, -200, .008},
		{-200, 0, .002},
		{-100, 100, .002},
	}
	buyExg := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	sellExg := bitfinex.New("", "", "", "usd", 2, 0.001, 500)

	for _, neededArb := range neededArbTests {
		buyExg.SetPosition(neededArb.buyExgPos)
		sellExg.SetPosition(neededArb.sellExgPos)
		arb := calcNeededArb(buyExg, sellExg)
		if math.Abs(arb-neededArb.arb) > .000001 {
			t.Errorf("For %.4f / %.4f expect %.4f, got %.4f\n", buyExg.Position(), sellExg.Position(), neededArb.arb, arb)
		}
	}

	// Test with FX
	neededArbTests = []neededArb{
		{500, -500, .03},
		{-500, 500, -.01},
		{500, 500, .01},
		{-100, -100, .01},
		{0, 0, .01},
		{-250, 250, 0},
		{250, -250, .02},
		{100, -100, .014},
		{0, -200, .014},
		{-200, 0, .006},
		{-100, 100, .006},
	}
	buyExg = okcoin.New("", "", "", "cny", 1, 0.002, 500)
	sellExg = bitfinex.New("", "", "", "usd", 2, 0.001, 500)

	for _, neededArb := range neededArbTests {
		buyExg.SetPosition(neededArb.buyExgPos)
		sellExg.SetPosition(neededArb.sellExgPos)
		arb := calcNeededArb(buyExg, sellExg)
		if math.Abs(arb-neededArb.arb) > .000001 {
			t.Errorf("For %.4f / %.4f expect %.4f, got %.4f\n", buyExg.Position(), sellExg.Position(), neededArb.arb, arb)
		}
	}

}

func TestFilterBook(t *testing.T) {
	testBook := exchange.Book{
		Exg: okcoin.New("", "", "", "usd", 1, 0.002, 500),
		Bids: exchange.BidItems{
			0: {Price: 1.90, Amount: 10},
			1: {Price: 1.80, Amount: 10},
			2: {Price: 1.70, Amount: 100},
		},
		Asks: exchange.AskItems{
			0: {Price: 2.10, Amount: 10},
			1: {Price: 2.20, Amount: 20},
			2: {Price: 2.30, Amount: 10},
		},
	}
	market := filterBook(testBook, 1)
	if math.Abs(market.bid.orderPrice-1.70) > .000001 {
		t.Errorf("Wrong bid order price")
	}
	if math.Abs(market.bid.amount-50) > .000001 {
		t.Errorf("Wrong bid amount")
	}
	adjPrice := ((1.90*10 + 1.80*10 + 1.70*30) / 50) * (1 - .002)
	if math.Abs(market.bid.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong bid adjusted price")
	}
	if math.Abs(market.ask.orderPrice-2.20) > .000001 {
		t.Errorf("Wrong ask order price")
	}
	if math.Abs(market.ask.amount-30) > .000001 {
		t.Errorf("Wrong ask amount")
	}
	adjPrice = ((2.10*10 + 2.20*20) / 30) * (1 + .002)
	if math.Abs(market.ask.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong ask adjusted price")
	}
	// Same test but with FX adjustment
	fxPrice := 2.0
	market = filterBook(testBook, fxPrice)
	if math.Abs(market.bid.orderPrice-1.70) > .000001 {
		t.Errorf("Wrong bid order price")
	}
	if math.Abs(market.bid.amount-50) > .000001 {
		t.Errorf("Wrong bid amount")
	}
	adjPrice = ((1.90*10 + 1.80*10 + 1.70*30) / 50) * (1 - .002) / fxPrice
	if math.Abs(market.bid.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong bid adjusted price")
	}
	if math.Abs(market.ask.orderPrice-2.20) > .000001 {
		t.Errorf("Wrong ask order price")
	}
	if math.Abs(market.ask.amount-30) > .000001 {
		t.Errorf("Wrong ask amount")
	}
	adjPrice = ((2.10*10 + 2.20*20) / 30) * (1 + .002) / fxPrice
	if math.Abs(market.ask.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong ask adjusted price")
	}

	testBook = exchange.Book{
		Exg: okcoin.New("", "", "", "usd", 2, 0.002, 500),
		Bids: exchange.BidItems{
			0: {Price: 1.90, Amount: 30},
			1: {Price: 1.80, Amount: 10},
			2: {Price: 1.70, Amount: 100},
		},
		Asks: exchange.AskItems{
			0: {Price: 2.10, Amount: 100},
			1: {Price: 2.20, Amount: 20},
			2: {Price: 2.30, Amount: 10},
		},
	}
	market = filterBook(testBook, 1)
	if math.Abs(market.bid.orderPrice-1.90) > .000001 {
		t.Errorf("Wrong bid order price")
	}
	if math.Abs(market.bid.amount-30) > .000001 {
		t.Errorf("Wrong bid amount")
	}
	adjPrice = 1.90 * (1 - .002)
	if math.Abs(market.bid.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong bid adjusted price")
	}
	if math.Abs(market.ask.orderPrice-2.10) > .000001 {
		t.Errorf("Wrong ask order price")
	}
	if math.Abs(market.ask.amount-50) > .000001 {
		t.Errorf("Wrong ask amount")
	}
	adjPrice = 2.10 * (1 + .002)
	if math.Abs(market.ask.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong ask adjusted price")
	}
	// Same test as above, but wiht FX adjustment
	fxPrice = 3.0
	market = filterBook(testBook, fxPrice)
	if math.Abs(market.bid.orderPrice-1.90) > .000001 {
		t.Errorf("Wrong bid order price")
	}
	if math.Abs(market.bid.amount-30) > .000001 {
		t.Errorf("Wrong bid amount")
	}
	adjPrice = 1.90 * (1 - .002) / fxPrice
	if math.Abs(market.bid.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong bid adjusted price")
	}
	if math.Abs(market.ask.orderPrice-2.10) > .000001 {
		t.Errorf("Wrong ask order price")
	}
	if math.Abs(market.ask.amount-50) > .000001 {
		t.Errorf("Wrong ask amount")
	}
	adjPrice = 2.10 * (1 + .002) / fxPrice
	if math.Abs(market.ask.adjPrice-adjPrice) > .000001 {
		t.Errorf("Wrong ask adjusted price")
	}
}

func TestFindBestBid(t *testing.T) {
	markets := make(map[exchange.Exchange]filteredBook)
	exg1 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg2 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg3 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	markets[exg1] = filteredBook{bid: market{adjPrice: 2.00, amount: 500}}
	markets[exg2] = filteredBook{bid: market{adjPrice: 1.99}}
	markets[exg3] = filteredBook{bid: market{adjPrice: 1.98}}
	if math.Abs(findBestBid(markets).adjPrice-2.00) > .000001 {
		t.Error("Returned wrong best bid")
	}
	exg1.SetPosition(-490)
	if math.Abs(findBestBid(markets).adjPrice-1.99) > .000001 {
		t.Error("Returned wrong best bid after position update")
	}
	exg1.SetPosition(-250)
	if math.Abs(findBestBid(markets).amount-250) > .000001 {
		t.Error("Returned wrong best bid amount after position update")
	}
}

func TestFindBestAsk(t *testing.T) {
	markets := make(map[exchange.Exchange]filteredBook)
	exg1 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg2 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg3 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	markets[exg1] = filteredBook{ask: market{adjPrice: 1.98, amount: 500}}
	markets[exg2] = filteredBook{ask: market{adjPrice: 1.99}}
	markets[exg3] = filteredBook{ask: market{adjPrice: 2.00}}
	if math.Abs(findBestAsk(markets).adjPrice-1.98) > .000001 {
		t.Error("Returned wrong best ask")
	}
	exg1.SetPosition(490)
	if math.Abs(findBestAsk(markets).adjPrice-1.99) > .000001 {
		t.Error("Returned wrong best ask after position update")
	}
	exg1.SetPosition(250)
	if math.Abs(findBestAsk(markets).amount-250) > .000001 {
		t.Error("Returned wrong best ask after position update")
	}
}

func TestFindBestArb(t *testing.T) {
	// No opportunity
	markets := make(map[exchange.Exchange]filteredBook)
	exg1 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg2 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	exg3 := okcoin.New("", "", "", "usd", 1, 0.002, 500)
	markets[exg1] = filteredBook{
		bid: market{adjPrice: 1.98, amount: 50, exg: exg1},
		ask: market{adjPrice: 2.00, amount: 50, exg: exg1},
	}
	markets[exg2] = filteredBook{
		bid: market{adjPrice: 1.99, amount: 50, exg: exg2},
		ask: market{adjPrice: 2.01, amount: 50, exg: exg2},
	}
	markets[exg3] = filteredBook{
		bid: market{adjPrice: 2.00, amount: 50, exg: exg3},
		ask: market{adjPrice: 2.02, amount: 50, exg: exg3},
	}
	if _, _, exists := findBestArb(markets); exists {
		t.Errorf("Should be no arb opportunity")
	}
	// Change positions to create an exit opportunity
	exg1.SetPosition(-500)
	exg3.SetPosition(500)
	bestBid, bestAsk, exists := findBestArb(markets)
	if !exists || bestBid.exg != exg3 || bestAsk.exg != exg1 {
		t.Errorf("Should be an exit opportunity after position update")
	}
	exg1.SetPosition(0)
	exg3.SetPosition(0)

	// Create an arb opportunity
	markets[exg1] = filteredBook{
		bid: market{adjPrice: 2.03, amount: 50, exg: exg1},
		ask: market{adjPrice: 2.04, amount: 50, exg: exg1},
	}
	markets[exg2] = filteredBook{
		bid: market{adjPrice: 2.04, amount: 50, exg: exg2},
		ask: market{adjPrice: 2.05, amount: 50, exg: exg2},
	}
	markets[exg3] = filteredBook{
		bid: market{adjPrice: 1.99, amount: 50, exg: exg3},
		ask: market{adjPrice: 2.00, amount: 50, exg: exg3},
	}
	bestBid, bestAsk, exists = findBestArb(markets)
	if !exists || bestBid.exg != exg2 || bestAsk.exg != exg3 {
		t.Errorf("Should be an arb opportunity")
	}

	// Set exg3 postion to only allow for 30 more
	exg3.SetPosition(470)
	_, bestAsk, _ = findBestArb(markets)
	if math.Abs(bestAsk.amount-30) > .000001 {
		t.Errorf("Should be a decrease in best ask amount")
	}

	// Change exg3 postion
	exg2.SetPosition(-500)
	bestBid, _, _ = findBestArb(markets)
	if bestBid.exg != exg1 {
		t.Errorf("Best bid exchange should have changed")
	}
}
<|endoftext|>


<commit_before>package cli_test

import (
	"fmt"
	"os"
	"os/exec"

	"bytes"
	"io/ioutil"
	"math/rand"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
	"text/template"
	"time"

	"github.com/appcelerator/amp/api/server"
	"gopkg.in/yaml.v2"
)

type TestSpec struct {
	Name     string
	Commands []CommandSpec
}

type CommandSpec struct {
	Cmd               string   `yaml:"cmd"`
	Args              []string `yaml:"args"`
	Options           []string `yaml:"options"`
	Expectation       string   `yaml:"expectation"`
	Retry             int      `yaml:"retry"`
	Timeout           int64    `yaml:"timeout"`
	Delay             int64    `yaml:"delay"`
}

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

var (
	testDir   = "./test_samples"
	lookupDir = "./lookup"
	regexMap  map[string]string
)

func TestMain(m *testing.M) {
	server.StartTestServer()
	os.Exit(m.Run())
}

func TestCmds(t *testing.T) {
	err := loadRegexLookup()
	if err != nil {
		t.Errorf("Unable to load lookup specs, reason: %v", err)
		return
	}
	tests, err := loadTestSpecs()
	if err != nil {
		t.Errorf("unable to load test specs, reason: %v", err)
		return
	}
	for _, test := range tests {
		t.Log("-----------------------------------------------------------------------------------------")
		t.Logf("Running spec: %s", test.Name)
		if err := runTestSpec(t, test); err != nil {
			t.Error(err)
			return
		}
	}
}

func loadTestSpecs() ([]*TestSpec, error) {
	files, err := ioutil.ReadDir(testDir)
	if err != nil {
		return nil, err
	}
	tests := []*TestSpec{}
	for _, file := range files {
		test, err := loadTestSpec(path.Join(testDir, file.Name()))
		if err != nil {
			return nil, err
		}
		if test != nil {
			tests = append(tests, test)
		}
	}
	return tests, nil
}

func loadTestSpec(fileName string) (*TestSpec, error) {
	if filepath.Ext(fileName) != ".yml" {
		return nil, nil
	}
	content, err := ioutil.ReadFile(fileName)
	if err != nil {
		return nil, fmt.Errorf("unable to load test spec: %s. Error: %v", fileName, err)
	}
	testSpec := &TestSpec{
		Name: fileName,
	}

	var commandMap []CommandSpec
	if err := yaml.Unmarshal(content, &commandMap); err != nil {
		return nil, fmt.Errorf("unable to parse test spec: %s. Error: %v", fileName, err)
	}

	for _, command := range commandMap {
		testSpec.Commands = append(testSpec.Commands, command)
	}
	return testSpec, nil
}

func runTestSpec(t *testing.T, test *TestSpec) (err error) {
	var i int
	var cache = map[string]string{}

	for _, cmdSpec := range test.Commands {
		var tmplString []string
		startTime := time.Now().UnixNano() / 1000000

		for i = -1; i < cmdSpec.Retry; i++ {
			cmdString := generateCmdString(&cmdSpec)
			tmplOutput, tmplErr := performTemplating(strings.Join(cmdString, " "), cache)
			if tmplErr != nil {
				err = fmt.Errorf("Executing templating failed: %s", tmplErr)
				t.Log(err)
			}
			tmplString := strings.Fields(tmplOutput)

			t.Logf("Running: %s", strings.Join(tmplString, " "))
			cmdOutput, cmdErr := exec.Command(tmplString[0], tmplString[1:]...).CombinedOutput()
			expectedOutput := regexp.MustCompile(cmdSpec.Expectation)
			if !expectedOutput.MatchString(string(cmdOutput)) {
				err = fmt.Errorf("miss matched expected output: %s : Error: %v", cmdOutput, cmdErr)
				t.Log(err)
			}

			endTime := time.Now().UnixNano() / 1000000
			if cmdSpec.Timeout != 0 && endTime-startTime >= cmdSpec.Timeout {
				return fmt.Errorf("Command execution has exceeded timeout : %s", tmplString)
			}
			if err == nil {
				break
			}
			time.Sleep(time.Duration(cmdSpec.Delay) * time.Millisecond)
		}
		if i > 0 && i == cmdSpec.Retry {
			t.Log("This command :", tmplString, "has re-run", i, "times.")
		}
	}
	return
}

func generateCmdString(cmdSpec *CommandSpec) (cmdString []string) {
	cmdSplit := strings.Fields(cmdSpec.Cmd)
	optionsSplit := []string{}
	for _, val := range cmdSpec.Options {
		optionsSplit = append(optionsSplit, strings.Fields(val)...)
	}
	cmdString = append(cmdSplit, cmdSpec.Args...)
	cmdString = append(cmdString, optionsSplit...)
	if regexMap[cmdSpec.Expectation] != "" {
		cmdSpec.Expectation = regexMap[cmdSpec.Expectation]
	}
	return
}

func loadRegexLookup() error {
	files, err := ioutil.ReadDir(lookupDir)
	if err != nil {
		return err
	}
	for _, file := range files {
		err := parseLookup(path.Join(lookupDir, file.Name()))
		if err != nil {
			return err
		}
	}
	return nil
}

func parseLookup(file string) error {
	if filepath.Ext(file) != ".yml" {
		return nil
	}
	pairs, err := ioutil.ReadFile(file)
	if err != nil {
		return fmt.Errorf("Unable to load regex lookup: %s. Error: %v", file, err)
	}
	if err := yaml.Unmarshal(pairs, &regexMap); err != nil {
		return fmt.Errorf("Unable to parse regex lookup: %s. Error: %v", file, err)
	}
	return nil
}

func performTemplating(s string, cache map[string]string) (output string, err error) {
	fmt.Println(s)
	var t *template.Template
	t, err = template.New("Command").Parse(s)
	if err != nil {
		return
	}
	f := func(in string) string {
		if val, ok := cache[in]; ok {
			return val
		}
		out := in + "-" + randString(10)
		cache[in] = out
		return out
	}
	var doc bytes.Buffer
	var fm = template.FuncMap{
		"uniq": func(in string) string { return f(in) },
	}
	err = t.Execute(&doc, fm)
	if err != nil {
		return
	}
	output = doc.String()
	fmt.Println(output)
	return
}

func randString(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}
<commit_msg>fix Go formatting (#429)<commit_after>package cli_test

import (
	"fmt"
	"os"
	"os/exec"

	"bytes"
	"io/ioutil"
	"math/rand"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
	"text/template"
	"time"

	"github.com/appcelerator/amp/api/server"
	"gopkg.in/yaml.v2"
)

type TestSpec struct {
	Name     string
	Commands []CommandSpec
}

type CommandSpec struct {
	Cmd         string   `yaml:"cmd"`
	Args        []string `yaml:"args"`
	Options     []string `yaml:"options"`
	Expectation string   `yaml:"expectation"`
	Retry       int      `yaml:"retry"`
	Timeout     int64    `yaml:"timeout"`
	Delay       int64    `yaml:"delay"`
}

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

var (
	testDir   = "./test_samples"
	lookupDir = "./lookup"
	regexMap  map[string]string
)

func TestMain(m *testing.M) {
	server.StartTestServer()
	os.Exit(m.Run())
}

func TestCmds(t *testing.T) {
	err := loadRegexLookup()
	if err != nil {
		t.Errorf("Unable to load lookup specs, reason: %v", err)
		return
	}
	tests, err := loadTestSpecs()
	if err != nil {
		t.Errorf("unable to load test specs, reason: %v", err)
		return
	}
	for _, test := range tests {
		t.Log("-----------------------------------------------------------------------------------------")
		t.Logf("Running spec: %s", test.Name)
		if err := runTestSpec(t, test); err != nil {
			t.Error(err)
			return
		}
	}
}

func loadTestSpecs() ([]*TestSpec, error) {
	files, err := ioutil.ReadDir(testDir)
	if err != nil {
		return nil, err
	}
	tests := []*TestSpec{}
	for _, file := range files {
		test, err := loadTestSpec(path.Join(testDir, file.Name()))
		if err != nil {
			return nil, err
		}
		if test != nil {
			tests = append(tests, test)
		}
	}
	return tests, nil
}

func loadTestSpec(fileName string) (*TestSpec, error) {
	if filepath.Ext(fileName) != ".yml" {
		return nil, nil
	}
	content, err := ioutil.ReadFile(fileName)
	if err != nil {
		return nil, fmt.Errorf("unable to load test spec: %s. Error: %v", fileName, err)
	}
	testSpec := &TestSpec{
		Name: fileName,
	}

	var commandMap []CommandSpec
	if err := yaml.Unmarshal(content, &commandMap); err != nil {
		return nil, fmt.Errorf("unable to parse test spec: %s. Error: %v", fileName, err)
	}

	for _, command := range commandMap {
		testSpec.Commands = append(testSpec.Commands, command)
	}
	return testSpec, nil
}

func runTestSpec(t *testing.T, test *TestSpec) (err error) {
	var i int
	var cache = map[string]string{}

	for _, cmdSpec := range test.Commands {
		var tmplString []string
		startTime := time.Now().UnixNano() / 1000000

		for i = -1; i < cmdSpec.Retry; i++ {
			cmdString := generateCmdString(&cmdSpec)
			tmplOutput, tmplErr := performTemplating(strings.Join(cmdString, " "), cache)
			if tmplErr != nil {
				err = fmt.Errorf("Executing templating failed: %s", tmplErr)
				t.Log(err)
			}
			tmplString := strings.Fields(tmplOutput)

			t.Logf("Running: %s", strings.Join(tmplString, " "))
			cmdOutput, cmdErr := exec.Command(tmplString[0], tmplString[1:]...).CombinedOutput()
			expectedOutput := regexp.MustCompile(cmdSpec.Expectation)
			if !expectedOutput.MatchString(string(cmdOutput)) {
				err = fmt.Errorf("miss matched expected output: %s : Error: %v", cmdOutput, cmdErr)
				t.Log(err)
			}

			endTime := time.Now().UnixNano() / 1000000
			if cmdSpec.Timeout != 0 && endTime-startTime >= cmdSpec.Timeout {
				return fmt.Errorf("Command execution has exceeded timeout : %s", tmplString)
			}
			if err == nil {
				break
			}
			time.Sleep(time.Duration(cmdSpec.Delay) * time.Millisecond)
		}
		if i > 0 && i == cmdSpec.Retry {
			t.Log("This command :", tmplString, "has re-run", i, "times.")
		}
	}
	return
}

func generateCmdString(cmdSpec *CommandSpec) (cmdString []string) {
	cmdSplit := strings.Fields(cmdSpec.Cmd)
	optionsSplit := []string{}
	for _, val := range cmdSpec.Options {
		optionsSplit = append(optionsSplit, strings.Fields(val)...)
	}
	cmdString = append(cmdSplit, cmdSpec.Args...)
	cmdString = append(cmdString, optionsSplit...)
	if regexMap[cmdSpec.Expectation] != "" {
		cmdSpec.Expectation = regexMap[cmdSpec.Expectation]
	}
	return
}

func loadRegexLookup() error {
	files, err := ioutil.ReadDir(lookupDir)
	if err != nil {
		return err
	}
	for _, file := range files {
		err := parseLookup(path.Join(lookupDir, file.Name()))
		if err != nil {
			return err
		}
	}
	return nil
}

func parseLookup(file string) error {
	if filepath.Ext(file) != ".yml" {
		return nil
	}
	pairs, err := ioutil.ReadFile(file)
	if err != nil {
		return fmt.Errorf("Unable to load regex lookup: %s. Error: %v", file, err)
	}
	if err := yaml.Unmarshal(pairs, &regexMap); err != nil {
		return fmt.Errorf("Unable to parse regex lookup: %s. Error: %v", file, err)
	}
	return nil
}

func performTemplating(s string, cache map[string]string) (output string, err error) {
	fmt.Println(s)
	var t *template.Template
	t, err = template.New("Command").Parse(s)
	if err != nil {
		return
	}
	f := func(in string) string {
		if val, ok := cache[in]; ok {
			return val
		}
		out := in + "-" + randString(10)
		cache[in] = out
		return out
	}
	var doc bytes.Buffer
	var fm = template.FuncMap{
		"uniq": func(in string) string { return f(in) },
	}
	err = t.Execute(&doc, fm)
	if err != nil {
		return
	}
	output = doc.String()
	fmt.Println(output)
	return
}

func randString(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}
<|endoftext|>


<commit_before>package cli_test

import (
	"fmt"
	"os"
	"os/exec"

	"bytes"
	"io/ioutil"
	"math/rand"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
	"text/template"
	"time"

	"github.com/appcelerator/amp/api/server"
	"gopkg.in/yaml.v2"
)

type TestSpec struct {
	Name     string
	Commands []CommandSpec
}

type CommandSpec struct {
	Cmd         string   `yaml:"cmd"`
	Args        []string `yaml:"args"`
	Options     []string `yaml:"options"`
	Expectation string   `yaml:"expectation"`
	Retry       int      `yaml:"retry"`
	Timeout     int64    `yaml:"timeout"`
	Delay       int64    `yaml:"delay"`
}

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

var (
	testDir   = "./test_samples"
	lookupDir = "./lookup"
	regexMap  map[string]string
)

func TestMain(m *testing.M) {
	server.StartTestServer()
	os.Exit(m.Run())
}

func TestCmds(t *testing.T) {
	err := loadRegexLookup()
	if err != nil {
		t.Errorf("Unable to load lookup specs, reason: %v", err)
		return
	}
	tests, err := loadTestSpecs()
	if err != nil {
		t.Errorf("unable to load test specs, reason: %v", err)
		return
	}
	for _, test := range tests {
		t.Log("-----------------------------------------------------------------------------------------")
		t.Logf("Running spec: %s", test.Name)
		if err := runTestSpec(t, test); err != nil {
			t.Error(err)
			return
		}
	}
}

func loadTestSpecs() ([]*TestSpec, error) {
	files, err := ioutil.ReadDir(testDir)
	if err != nil {
		return nil, err
	}
	tests := []*TestSpec{}
	for _, file := range files {
		test, err := loadTestSpec(path.Join(testDir, file.Name()))
		if err != nil {
			return nil, err
		}
		if test != nil {
			tests = append(tests, test)
		}
	}
	return tests, nil
}

func loadTestSpec(fileName string) (*TestSpec, error) {
	if filepath.Ext(fileName) != ".yml" {
		return nil, nil
	}
	content, err := ioutil.ReadFile(fileName)
	if err != nil {
		return nil, fmt.Errorf("unable to load test spec: %s. Error: %v", fileName, err)
	}
	testSpec := &TestSpec{
		Name: fileName,
	}

	var commandMap []CommandSpec
	if err := yaml.Unmarshal(content, &commandMap); err != nil {
		return nil, fmt.Errorf("unable to parse test spec: %s. Error: %v", fileName, err)
	}

	for _, command := range commandMap {
		testSpec.Commands = append(testSpec.Commands, command)
	}
	return testSpec, nil
}

func runTestSpec(t *testing.T, test *TestSpec) (err error) {
	var i int
	var cache = map[string]string{}

	for _, cmdSpec := range test.Commands {
		var tmplString []string
		startTime := time.Now().UnixNano() / 1000000

		for i = -1; i < cmdSpec.Retry; i++ {
			cmdString := generateCmdString(&cmdSpec)
			tmplOutput, tmplErr := performTemplating(strings.Join(cmdString, " "), cache)
			if tmplErr != nil {
				err = fmt.Errorf("Executing templating failed: %s", tmplErr)
				t.Log(err)
			}
			tmplString := strings.Fields(tmplOutput)

			t.Logf("Running: %s", strings.Join(tmplString, " "))
			cmdOutput, cmdErr := exec.Command(tmplString[0], tmplString[1:]...).CombinedOutput()
			expectedOutput := regexp.MustCompile(cmdSpec.Expectation)
			if !expectedOutput.MatchString(string(cmdOutput)) {
				err = fmt.Errorf("miss matched expected output: %s : Error: %v", cmdOutput, cmdErr)
				t.Log(err)
			}

			endTime := time.Now().UnixNano() / 1000000
			if cmdSpec.Timeout != 0 && endTime-startTime >= cmdSpec.Timeout {
				return fmt.Errorf("Command execution has exceeded timeout : %s", tmplString)
			}
			if err == nil {
				break
			}
			time.Sleep(time.Duration(cmdSpec.Delay) * time.Millisecond)
		}
		if i > 0 && i == cmdSpec.Retry {
			t.Log("This command :", tmplString, "has re-run", i, "times.")
		}
	}
	return
}

func generateCmdString(cmdSpec *CommandSpec) (cmdString []string) {
	cmdSplit := strings.Fields(cmdSpec.Cmd)
	optionsSplit := []string{}
	for _, val := range cmdSpec.Options {
		optionsSplit = append(optionsSplit, strings.Fields(val)...)
	}
	cmdString = append(cmdSplit, cmdSpec.Args...)
	cmdString = append(cmdString, optionsSplit...)
	if regexMap[cmdSpec.Expectation] != "" {
		cmdSpec.Expectation = regexMap[cmdSpec.Expectation]
	}
	return
}

func loadRegexLookup() error {
	files, err := ioutil.ReadDir(lookupDir)
	if err != nil {
		return err
	}
	for _, file := range files {
		err := parseLookup(path.Join(lookupDir, file.Name()))
		if err != nil {
			return err
		}
	}
	return nil
}

func parseLookup(file string) error {
	if filepath.Ext(file) != ".yml" {
		return nil
	}
	pairs, err := ioutil.ReadFile(file)
	if err != nil {
		return fmt.Errorf("Unable to load regex lookup: %s. Error: %v", file, err)
	}
	if err := yaml.Unmarshal(pairs, &regexMap); err != nil {
		return fmt.Errorf("Unable to parse regex lookup: %s. Error: %v", file, err)
	}
	return nil
}

func performTemplating(s string, cache map[string]string) (output string, err error) {
	fmt.Println(s)
	var t *template.Template
	t, err = template.New("Command").Parse(s)
	if err != nil {
		return
	}
	f := func(in string) string {
		if val, ok := cache[in]; ok {
			return val
		}
		out := in + "-" + randString(10)
		cache[in] = out
		return out
	}
	var doc bytes.Buffer
	var fm = template.FuncMap{
		"uniq": func(in string) string { return f(in) },
	}
	err = t.Execute(&doc, fm)
	if err != nil {
		return
	}
	output = doc.String()
	fmt.Println(output)
	return
}

func randString(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}
<commit_msg>Minor fix to cli_test.go (#440)<commit_after>package cli_test

import (
	"fmt"
	"os"
	"os/exec"

	"bytes"
	"io/ioutil"
	"math/rand"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
	"text/template"
	"time"

	"github.com/appcelerator/amp/api/server"
	"gopkg.in/yaml.v2"
)

type TestSpec struct {
	Name     string
	Commands []CommandSpec
}

type CommandSpec struct {
	Cmd         string   `yaml:"cmd"`
	Args        []string `yaml:"args"`
	Options     []string `yaml:"options"`
	Expectation string   `yaml:"expectation"`
	Retry       int      `yaml:"retry"`
	Timeout     int64    `yaml:"timeout"`
	Delay       int64    `yaml:"delay"`
}

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

var (
	testDir   = "./test_samples"
	lookupDir = "./lookup"
	regexMap  map[string]string
)

func TestMain(m *testing.M) {
	server.StartTestServer()
	os.Exit(m.Run())
}

func TestCmds(t *testing.T) {
	err := loadRegexLookup()
	if err != nil {
		t.Errorf("Unable to load lookup specs, reason: %v", err)
		return
	}
	tests, err := loadTestSpecs()
	if err != nil {
		t.Errorf("unable to load test specs, reason: %v", err)
		return
	}
	for _, test := range tests {
		t.Log("-----------------------------------------------------------------------------------------")
		t.Logf("Running spec: %s", test.Name)
		if err := runTestSpec(t, test); err != nil {
			t.Error(err)
			return
		}
	}
}

func loadTestSpecs() ([]*TestSpec, error) {
	files, err := ioutil.ReadDir(testDir)
	if err != nil {
		return nil, err
	}
	tests := []*TestSpec{}
	for _, file := range files {
		test, err := loadTestSpec(path.Join(testDir, file.Name()))
		if err != nil {
			return nil, err
		}
		if test != nil {
			tests = append(tests, test)
		}
	}
	return tests, nil
}

func loadTestSpec(fileName string) (*TestSpec, error) {
	if filepath.Ext(fileName) != ".yml" {
		return nil, nil
	}
	content, err := ioutil.ReadFile(fileName)
	if err != nil {
		return nil, fmt.Errorf("unable to load test spec: %s. Error: %v", fileName, err)
	}
	testSpec := &TestSpec{
		Name: fileName,
	}

	var commandMap []CommandSpec
	if err := yaml.Unmarshal(content, &commandMap); err != nil {
		return nil, fmt.Errorf("unable to parse test spec: %s. Error: %v", fileName, err)
	}

	for _, command := range commandMap {
		testSpec.Commands = append(testSpec.Commands, command)
	}
	return testSpec, nil
}

func runTestSpec(t *testing.T, test *TestSpec) (err error) {
	var i int
	var cache = map[string]string{}

	for _, cmdSpec := range test.Commands {
		var tmplString []string
		startTime := time.Now().UnixNano() / 1000000

		for i = -1; i < cmdSpec.Retry; i++ {
			cmdString := generateCmdString(&cmdSpec)
			tmplOutput, tmplErr := performTemplating(strings.Join(cmdString, " "), cache)
			if tmplErr != nil {
				err = fmt.Errorf("Executing templating failed: %s", tmplErr)
				t.Log(err)
			}
			tmplString = strings.Fields(tmplOutput)

			t.Logf("Running: %s", strings.Join(tmplString, " "))
			cmdOutput, cmdErr := exec.Command(tmplString[0], tmplString[1:]...).CombinedOutput()
			expectedOutput := regexp.MustCompile(cmdSpec.Expectation)
			if !expectedOutput.MatchString(string(cmdOutput)) {
				err = fmt.Errorf("miss matched expected output: %s : Error: %v", cmdOutput, cmdErr)
				t.Log(err)
			}

			endTime := time.Now().UnixNano() / 1000000
			if cmdSpec.Timeout != 0 && endTime-startTime >= cmdSpec.Timeout {
				return fmt.Errorf("Command execution has exceeded timeout : %s", tmplString)
			}
			if err == nil {
				break
			}
			time.Sleep(time.Duration(cmdSpec.Delay) * time.Millisecond)
		}
		if i > 0 && i == cmdSpec.Retry {
			t.Log("This command :", tmplString, "has re-run", i, "times.")
		}
	}
	return
}

func generateCmdString(cmdSpec *CommandSpec) (cmdString []string) {
	cmdSplit := strings.Fields(cmdSpec.Cmd)
	optionsSplit := []string{}
	for _, val := range cmdSpec.Options {
		optionsSplit = append(optionsSplit, strings.Fields(val)...)
	}
	cmdString = append(cmdSplit, cmdSpec.Args...)
	cmdString = append(cmdString, optionsSplit...)
	if regexMap[cmdSpec.Expectation] != "" {
		cmdSpec.Expectation = regexMap[cmdSpec.Expectation]
	}
	return
}

func loadRegexLookup() error {
	files, err := ioutil.ReadDir(lookupDir)
	if err != nil {
		return err
	}
	for _, file := range files {
		err := parseLookup(path.Join(lookupDir, file.Name()))
		if err != nil {
			return err
		}
	}
	return nil
}

func parseLookup(file string) error {
	if filepath.Ext(file) != ".yml" {
		return nil
	}
	pairs, err := ioutil.ReadFile(file)
	if err != nil {
		return fmt.Errorf("Unable to load regex lookup: %s. Error: %v", file, err)
	}
	if err := yaml.Unmarshal(pairs, &regexMap); err != nil {
		return fmt.Errorf("Unable to parse regex lookup: %s. Error: %v", file, err)
	}
	return nil
}

func performTemplating(s string, cache map[string]string) (output string, err error) {
	fmt.Println(s)
	var t *template.Template
	t, err = template.New("Command").Parse(s)
	if err != nil {
		return
	}
	f := func(in string) string {
		if val, ok := cache[in]; ok {
			return val
		}
		out := in + "-" + randString(10)
		cache[in] = out
		return out
	}
	var doc bytes.Buffer
	var fm = template.FuncMap{
		"uniq": func(in string) string { return f(in) },
	}
	err = t.Execute(&doc, fm)
	if err != nil {
		return
	}
	output = doc.String()
	fmt.Println(output)
	return
}

func randString(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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.

// Binary to run against a server to validate protocol conformance.
package main

import (
	"context"
	"crypto/tls"
	"fmt"

	"flag"
	"github.com/GoogleCloudPlatform/stet/client"
	"github.com/GoogleCloudPlatform/stet/constants"
	sspb "github.com/GoogleCloudPlatform/stet/proto/secure_session_go_proto"
	"github.com/GoogleCloudPlatform/stet/server"
	"github.com/GoogleCloudPlatform/stet/transportshim"
	"github.com/alecthomas/colour"
)

var (
	keyURI = flag.String("key-uri", fmt.Sprintf("http://localhost:%d/v0/%v", constants.HTTPPort, server.KeyPath1), "A valid key URI stored in the server")
)

const (
	recordHeaderHandshake      = 0x16
	handshakeHeaderServerHello = 0x02
)

type ekmClient struct {
	client client.ConfidentialEKMClient
	shim   transportshim.ShimInterface
}

// Initializes a new EKM client for the given version of TLS against the
// given key URL, also kicking off the internal TLS handshake.
func newEKMClient(keyURL string, tlsVersion int) ekmClient {
	c := ekmClient{}
	c.client = client.NewConfidentialEKMClient(keyURL)

	c.shim = transportshim.NewTransportShim()

	cfg := &tls.Config{
		CipherSuites:       constants.AllowableCipherSuites,
		MinVersion:         uint16(tlsVersion),
		MaxVersion:         uint16(tlsVersion),
		InsecureSkipVerify: true,
	}

	tlss := tls.Client(c.shim, cfg)

	go func() {
		if err := tlss.Handshake(); err != nil {
			return
		}
	}()

	return c
}

type beginSessionTest struct {
	testName         string
	expectErr        bool
	mutateTLSRecords func(r []byte) []byte
}

func runBeginSessionTestCase(mutateTLSRecords func(r []byte) []byte) error {
	ctx := context.Background()

	c := newEKMClient(*keyURI, tls.VersionTLS13)

	req := &sspb.BeginSessionRequest{
		TlsRecords: c.shim.DrainSendBuf(),
	}

	// Mutate the request TLS records.
	req.TlsRecords = mutateTLSRecords(req.TlsRecords)

	resp, err := c.client.BeginSession(ctx, req)
	if err != nil {
		return err
	}

	records := resp.GetTlsRecords()

	if records[0] != recordHeaderHandshake {
		return fmt.Errorf("Handshake record not received")
	}

	if records[5] != handshakeHeaderServerHello {
		return fmt.Errorf("Response is not Server Hello")
	}

	return nil

}

func main() {
	// Define and run BeginSession tests.
	fmt.Println("Running BeginSession tests...")

	testCases := []beginSessionTest{
		{
			testName:         "Valid request with proper TLS Client Hello",
			expectErr:        false,
			mutateTLSRecords: func(r []byte) []byte { return r },
		},
		{
			testName:  "Malformed Client Hello in request",
			expectErr: true,
			mutateTLSRecords: func(r []byte) []byte {
				r[5] = 0xFF // Client Hello byte should be 0x01
				return r
			},
		},
	}

	for _, testCase := range testCases {
		err := runBeginSessionTestCase(testCase.mutateTLSRecords)
		testPassed := testCase.expectErr == (err != nil)
		if testPassed {
			colour.Printf("^2 - %v^R\n", testCase.testName)
		} else {
			colour.Printf("^1 - %v^R\n", testCase.testName)
		}
	}
}
<commit_msg>Add Handshake tests to conformance test suite<commit_after>// Copyright 2021 Google LLC
//
// 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.

// Binary to run against a server to validate protocol conformance.
package main

import (
	"context"
	"crypto/tls"
	"fmt"

	"flag"
	"github.com/GoogleCloudPlatform/stet/client"
	"github.com/GoogleCloudPlatform/stet/constants"
	sspb "github.com/GoogleCloudPlatform/stet/proto/secure_session_go_proto"
	"github.com/GoogleCloudPlatform/stet/server"
	"github.com/GoogleCloudPlatform/stet/transportshim"
	"github.com/alecthomas/colour"
)

var (
	keyURI = flag.String("key-uri", fmt.Sprintf("http://localhost:%d/v0/%v", constants.HTTPPort, server.KeyPath1), "A valid key URI stored in the server")
)

const (
	recordHeaderHandshake      = 0x16
	handshakeHeaderServerHello = 0x02
)

type ekmClient struct {
	client client.ConfidentialEKMClient
	shim   transportshim.ShimInterface
	tls    *tls.Conn
}

// Initializes a new EKM client for the given version of TLS against the
// given key URL, also kicking off the internal TLS handshake.
func newEKMClient(keyURL string, tlsVersion int) ekmClient {
	c := ekmClient{}
	c.client = client.NewConfidentialEKMClient(keyURL)

	c.shim = transportshim.NewTransportShim()

	cfg := &tls.Config{
		CipherSuites:       constants.AllowableCipherSuites,
		MinVersion:         tls.VersionTLS12,
		MaxVersion:         tls.VersionTLS13,
		InsecureSkipVerify: true,
	}

	c.tls = tls.Client(c.shim, cfg)

	go func() {
		if err := c.tls.Handshake(); err != nil {
			return
		}
	}()

	return c
}

// Returns an empty byte array.
func emptyFn([]byte) []byte { return []byte{} }

// Given a byte array `b`, returns `b`.
func identityFn(b []byte) []byte { return b }

type beginSessionTest struct {
	testName         string
	expectErr        bool
	mutateTLSRecords func(r []byte) []byte
}

func runBeginSessionTestCase(mutateTLSRecords func(r []byte) []byte) error {
	ctx := context.Background()

	c := newEKMClient(*keyURI, tls.VersionTLS13)

	req := &sspb.BeginSessionRequest{
		TlsRecords: c.shim.DrainSendBuf(),
	}

	// Mutate the request TLS records.
	req.TlsRecords = mutateTLSRecords(req.TlsRecords)

	resp, err := c.client.BeginSession(ctx, req)
	if err != nil {
		return err
	}

	records := resp.GetTlsRecords()

	if records[0] != recordHeaderHandshake {
		return fmt.Errorf("Handshake record not received")
	}

	if records[5] != handshakeHeaderServerHello {
		return fmt.Errorf("Response is not Server Hello")
	}

	return nil
}

type handshakeTest struct {
	testName         string
	expectErr        bool
	mutateTLSRecords func(r []byte) []byte
	mutateSessionKey func(s []byte) []byte
}

func runHandshakeTestCase(mutateTLSRecords, mutateSessionKey func(r []byte) []byte) error {
	ctx := context.Background()

	c := newEKMClient(*keyURI, tls.VersionTLS13)

	req := &sspb.BeginSessionRequest{
		TlsRecords: c.shim.DrainSendBuf(),
	}

	resp, err := c.client.BeginSession(ctx, req)
	if err != nil {
		return err
	}

	sessionContext := mutateSessionKey(resp.GetSessionContext())
	c.shim.QueueReceiveBuf(resp.GetTlsRecords())

	req2 := &sspb.HandshakeRequest{
		SessionContext: sessionContext,
		TlsRecords:     mutateTLSRecords(c.shim.DrainSendBuf()),
	}

	_, err = c.client.Handshake(ctx, req2)
	if err != nil {
		return err
	}

	// Under TLS 1.3, the TLS implementation has nothing to return here.
	// However, attempting to call `c.tls.ConnectionState()` when the
	// server communicates with TLS 1.2 causes the client to hang
	// infinitely, so as a proxy, perform checks on the response records
	// only if they are non-nil.
	if len(resp.GetTlsRecords()) > 0 {
		records := resp.GetTlsRecords()

		// The handshake data itself is encypted, so just verify that the
		// header for this segment of data is a handshake record.
		if records[0] != recordHeaderHandshake {
			return fmt.Errorf("Handshake record not received")
		}
	}

	return nil
}

func main() {
	// Define and run BeginSession tests.
	fmt.Println("Running BeginSession tests...")

	testCases := []beginSessionTest{
		{
			testName:         "Valid request with proper TLS Client Hello",
			expectErr:        false,
			mutateTLSRecords: identityFn,
		},
		{
			testName:  "Malformed Client Hello in request",
			expectErr: true,
			mutateTLSRecords: func(r []byte) []byte {
				r[5] = 0xFF // Client Hello byte should be 0x01
				return r
			},
		},
		{
			testName:         "No TLS records in request",
			expectErr:        true,
			mutateTLSRecords: emptyFn,
		},
	}

	for _, testCase := range testCases {
		err := runBeginSessionTestCase(testCase.mutateTLSRecords)
		testPassed := testCase.expectErr == (err != nil)
		if testPassed {
			colour.Printf(" - ^2%v^R\n", testCase.testName)
		} else {
			colour.Printf(" - ^1%v^R (%v)\n", testCase.testName, err.Error())
		}
	}

	// Define and run Handshake tests.
	fmt.Println("Running Handshake tests...")

	testCases2 := []handshakeTest{
		{
			testName:         "Valid request with proper TLS Client Handshake",
			expectErr:        false,
			mutateTLSRecords: identityFn,
			mutateSessionKey: identityFn,
		},
		{
			testName:         "No TLS records in request",
			expectErr:        true,
			mutateTLSRecords: emptyFn,
			mutateSessionKey: identityFn,
		},
		{
			testName:         "Invalid session key",
			expectErr:        true,
			mutateTLSRecords: identityFn,
			mutateSessionKey: emptyFn,
		},
	}

	for _, testCase := range testCases2 {
		err := runHandshakeTestCase(testCase.mutateTLSRecords, testCase.mutateSessionKey)
		testPassed := testCase.expectErr == (err != nil)
		if testPassed {
			colour.Printf(" - ^2%v^R\n", testCase.testName)
		} else {
			colour.Printf(" - ^1%v^R (%v)\n", testCase.testName, err.Error())
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strings"
	"sync"
	"text/template"

	"github.com/haya14busa/errorformat"
	"github.com/haya14busa/errorformat/fmts"
	"github.com/haya14busa/go-checkstyle/checkstyle"
)

const usageMessage = "" +
	`Usage: errorformat [flags] [errorformat ...]

errorformat reads compiler/linter/static analyzer result from STDIN, formats
them by given 'errorformat' (90% compatible with Vim's errorformat. :h
errorformat), and outputs formated result to STDOUT.

Example:
	$ echo '/path/to/file:14:28: error message\nfile2:3:4: msg' | errorformat "%f:%l:%c: %m"
	/path/to/file|14 col 28| error message
	file2|3 col 4| msg

	$ golint ./... | errorformat -name=golint

The -f flag specifies an alternate format for the entry, using the
syntax of package template.  The default output is equivalent to -f
'{{.String}}'. The struct being passed to the template is:

	type Entry struct {
		// name of a file
		Filename string
		// line number
		Lnum int
		// column number (first column is 1)
		Col int
		// true: "col" is visual column
		// false: "col" is byte index
		Vcol bool
		// error number
		Nr int
		// search pattern used to locate the error
		Pattern string
		// description of the error
		Text string
		// type of the error, 'E', '1', etc.
		Type rune
		// true: recognized error message
		Valid bool

		// Original error lines (often one line. more than one line for multi-line
		// errorformat. :h errorformat-multi-line)
		Lines []string
	}
`

func usage() {
	fmt.Fprintln(os.Stderr, usageMessage)
	fmt.Fprintln(os.Stderr, "Flags:")
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	var (
		entryFmt = flag.String("f", "{{.String}}", "format template for -output-format=template")
		outFmt   = flag.String("output-format", "template", "output format (template|checkstyle)")
		name     = flag.String("name", "", "defined errorformat name")
		list     = flag.Bool("list", false, "list defined errorformats")
	)
	flag.Usage = usage
	flag.Parse()
	errorformats := flag.Args()
	if err := run(os.Stdin, os.Stdout, errorformats, *outFmt, *entryFmt, *name, *list); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(r io.Reader, w io.Writer, efms []string, outFmt, entryFmt, name string, list bool) error {
	if list {
		fs := fmts.DefinedFmts()
		out := make([]string, 0, len(fs))
		for _, f := range fs {
			out = append(out, fmt.Sprintf("%s\t\t%s - %s", f.Name, f.Description, f.URL))
		}
		sort.Strings(out)
		fmt.Fprintln(w, strings.Join(out, "\n"))
		return nil
	}

	if name != "" {
		f, ok := fmts.DefinedFmts()[name]
		if !ok {
			return fmt.Errorf("%q is not defined", name)
		}
		efms = f.Errorformat
	}

	var writer Writer

	switch outFmt {
	case "template":
		fm := template.FuncMap{
			"join": strings.Join,
		}
		tmpl, err := template.New("main").Funcs(fm).Parse(entryFmt)
		if err != nil {
			return err
		}
		writer = &TemplateWriter{Template: tmpl, Writer: newTrackingWriter(w)}
	case "checkstyle":
		writer = &CheckStyleWriter{w: w}
	default:
		return fmt.Errorf("unknown output format: -output-fmt=%v", outFmt)
	}
	defer func() {
		if err := writer.Flash(); err != nil {
			log.Println(err)
		}
	}()

	efm, err := errorformat.NewErrorformat(efms)
	if err != nil {
		return err
	}
	s := efm.NewScanner(r)
	for s.Scan() {
		if err := writer.Write(s.Entry()); err != nil {
			return err
		}
	}
	return nil
}

type Writer interface {
	Write(*errorformat.Entry) error
	Flash() error
}

type TemplateWriter struct {
	Template *template.Template
	Writer   *TrackingWriter
}

func (t *TemplateWriter) Write(e *errorformat.Entry) error {
	if err := t.Template.Execute(t.Writer, e); err != nil {
		return err
	}
	if t.Writer.NeedNL() {
		t.Writer.WriteNL()
	}
	return nil
}

func (*TemplateWriter) Flash() error {
	return nil
}

// TrackingWriter tracks the last byte written on every write so
// we can avoid printing a newline if one was already written or
// if there is no output at all.
type TrackingWriter struct {
	w    io.Writer
	last byte
}

func newTrackingWriter(w io.Writer) *TrackingWriter {
	return &TrackingWriter{
		w:    w,
		last: '\n',
	}
}

func (t *TrackingWriter) Write(p []byte) (n int, err error) {
	n, err = t.w.Write(p)
	if n > 0 {
		t.last = p[n-1]
	}
	return
}

var nl = []byte{'\n'}

// WriteNL writes NL.
func (t *TrackingWriter) WriteNL() (int, error) {
	return t.w.Write(nl)
}

// NeedNL returns true if the last byte written is not NL.
func (t *TrackingWriter) NeedNL() bool {
	return t.last != '\n'
}

type CheckStyleWriter struct {
	mu    sync.Mutex
	files map[string]*checkstyle.File
	w     io.Writer
}

func (c *CheckStyleWriter) Write(e *errorformat.Entry) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.files == nil {
		c.files = make(map[string]*checkstyle.File)
	}
	if _, ok := c.files[e.Filename]; !ok {
		c.files[e.Filename] = &checkstyle.File{Name: e.Filename}
	}
	checkerr := &checkstyle.Error{
		Column:  e.Col,
		Line:    e.Lnum,
		Message: e.Text,
	}
	c.files[e.Filename].Errors = append(c.files[e.Filename].Errors, checkerr)
	return nil
}

func (c *CheckStyleWriter) Flash() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	r := &checkstyle.Result{Version: "1.0"}
	for _, f := range c.files {
		r.Files = append(r.Files, f)
	}
	fmt.Fprint(c.w, xml.Header)
	e := xml.NewEncoder(c.w)
	e.Indent("", "  ")
	defer c.w.Write(nl)
	return e.Encode(r)
}
<commit_msg>s/-output-format/-w/ as writer format<commit_after>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strings"
	"sync"
	"text/template"

	"github.com/haya14busa/errorformat"
	"github.com/haya14busa/errorformat/fmts"
	"github.com/haya14busa/go-checkstyle/checkstyle"
)

const usageMessage = "" +
	`Usage: errorformat [flags] [errorformat ...]

errorformat reads compiler/linter/static analyzer result from STDIN, formats
them by given 'errorformat' (90% compatible with Vim's errorformat. :h
errorformat), and outputs formated result to STDOUT.

Example:
	$ echo '/path/to/file:14:28: error message\nfile2:3:4: msg' | errorformat "%f:%l:%c: %m"
	/path/to/file|14 col 28| error message
	file2|3 col 4| msg

	$ golint ./... | errorformat -name=golint

The -f flag specifies an alternate format for the entry, using the
syntax of package template.  The default output is equivalent to -f
'{{.String}}'. The struct being passed to the template is:

	type Entry struct {
		// name of a file
		Filename string
		// line number
		Lnum int
		// column number (first column is 1)
		Col int
		// true: "col" is visual column
		// false: "col" is byte index
		Vcol bool
		// error number
		Nr int
		// search pattern used to locate the error
		Pattern string
		// description of the error
		Text string
		// type of the error, 'E', '1', etc.
		Type rune
		// true: recognized error message
		Valid bool

		// Original error lines (often one line. more than one line for multi-line
		// errorformat. :h errorformat-multi-line)
		Lines []string
	}
`

func usage() {
	fmt.Fprintln(os.Stderr, usageMessage)
	fmt.Fprintln(os.Stderr, "Flags:")
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	var (
		entryFmt  = flag.String("f", "{{.String}}", "format template for -w=template")
		writerFmt = flag.String("w", "template", "writer format (template|checkstyle)")
		name      = flag.String("name", "", "defined errorformat name")
		list      = flag.Bool("list", false, "list defined errorformats")
	)
	flag.Usage = usage
	flag.Parse()
	errorformats := flag.Args()
	if err := run(os.Stdin, os.Stdout, errorformats, *writerFmt, *entryFmt, *name, *list); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(r io.Reader, w io.Writer, efms []string, writerFmt, entryFmt, name string, list bool) error {
	if list {
		fs := fmts.DefinedFmts()
		out := make([]string, 0, len(fs))
		for _, f := range fs {
			out = append(out, fmt.Sprintf("%s\t\t%s - %s", f.Name, f.Description, f.URL))
		}
		sort.Strings(out)
		fmt.Fprintln(w, strings.Join(out, "\n"))
		return nil
	}

	if name != "" {
		f, ok := fmts.DefinedFmts()[name]
		if !ok {
			return fmt.Errorf("%q is not defined", name)
		}
		efms = f.Errorformat
	}

	var writer Writer

	switch writerFmt {
	case "template":
		fm := template.FuncMap{
			"join": strings.Join,
		}
		tmpl, err := template.New("main").Funcs(fm).Parse(entryFmt)
		if err != nil {
			return err
		}
		writer = &TemplateWriter{Template: tmpl, Writer: newTrackingWriter(w)}
	case "checkstyle":
		writer = &CheckStyleWriter{w: w}
	default:
		return fmt.Errorf("unknown writer: -w=%v", writerFmt)
	}
	defer func() {
		if err := writer.Flash(); err != nil {
			log.Println(err)
		}
	}()

	efm, err := errorformat.NewErrorformat(efms)
	if err != nil {
		return err
	}
	s := efm.NewScanner(r)
	for s.Scan() {
		if err := writer.Write(s.Entry()); err != nil {
			return err
		}
	}
	return nil
}

type Writer interface {
	Write(*errorformat.Entry) error
	Flash() error
}

type TemplateWriter struct {
	Template *template.Template
	Writer   *TrackingWriter
}

func (t *TemplateWriter) Write(e *errorformat.Entry) error {
	if err := t.Template.Execute(t.Writer, e); err != nil {
		return err
	}
	if t.Writer.NeedNL() {
		t.Writer.WriteNL()
	}
	return nil
}

func (*TemplateWriter) Flash() error {
	return nil
}

// TrackingWriter tracks the last byte written on every write so
// we can avoid printing a newline if one was already written or
// if there is no output at all.
type TrackingWriter struct {
	w    io.Writer
	last byte
}

func newTrackingWriter(w io.Writer) *TrackingWriter {
	return &TrackingWriter{
		w:    w,
		last: '\n',
	}
}

func (t *TrackingWriter) Write(p []byte) (n int, err error) {
	n, err = t.w.Write(p)
	if n > 0 {
		t.last = p[n-1]
	}
	return
}

var nl = []byte{'\n'}

// WriteNL writes NL.
func (t *TrackingWriter) WriteNL() (int, error) {
	return t.w.Write(nl)
}

// NeedNL returns true if the last byte written is not NL.
func (t *TrackingWriter) NeedNL() bool {
	return t.last != '\n'
}

type CheckStyleWriter struct {
	mu    sync.Mutex
	files map[string]*checkstyle.File
	w     io.Writer
}

func (c *CheckStyleWriter) Write(e *errorformat.Entry) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.files == nil {
		c.files = make(map[string]*checkstyle.File)
	}
	if _, ok := c.files[e.Filename]; !ok {
		c.files[e.Filename] = &checkstyle.File{Name: e.Filename}
	}
	checkerr := &checkstyle.Error{
		Column:  e.Col,
		Line:    e.Lnum,
		Message: e.Text,
	}
	c.files[e.Filename].Errors = append(c.files[e.Filename].Errors, checkerr)
	return nil
}

func (c *CheckStyleWriter) Flash() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	r := &checkstyle.Result{Version: "1.0"}
	for _, f := range c.files {
		r.Files = append(r.Files, f)
	}
	fmt.Fprint(c.w, xml.Header)
	e := xml.NewEncoder(c.w)
	e.Indent("", "  ")
	defer c.w.Write(nl)
	return e.Encode(r)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"

	"github.com/spf13/cobra"

	"github.com/weaveworks/flux/git"
	"github.com/weaveworks/flux/update"
)

type syncOpts struct {
	*rootOpts
}

func newSync(parent *rootOpts) *syncOpts {
	return &syncOpts{rootOpts: parent}
}

func (opts *syncOpts) Command() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "sync",
		Short: "synchronize the cluster with the git repository, now",
		RunE:  opts.RunE,
	}
	return cmd
}

func (opts *syncOpts) RunE(cmd *cobra.Command, args []string) error {
	if len(args) > 0 {
		return errorWantedNoArgs
	}

	ctx := context.Background()

	gitConfig, err := opts.API.GitRepoConfig(ctx, false)
	if err != nil {
		return err
	}

	switch gitConfig.Status {
	case git.RepoNoConfig:
		return fmt.Errorf("no git repository is configured")
	case git.RepoReady:
		break
	default:
		return fmt.Errorf("git repository %s is not ready to sync (status: %s)", gitConfig.Remote.URL, string(gitConfig.Status))
	}

	fmt.Fprintf(cmd.OutOrStderr(), "Synchronizing with %s\n", gitConfig.Remote.URL)

	updateSpec := update.Spec{
		Type: update.Sync,
		Spec: update.ManualSync{},
	}
	jobID, err := opts.API.UpdateManifests(ctx, updateSpec)
	if err != nil {
		return err
	}
	fmt.Fprintf(cmd.OutOrStderr(), "Job ID %s\n", string(jobID))
	result, err := awaitJob(ctx, opts.API, jobID)
	if err != nil {
		return err
	}
	fmt.Fprintf(cmd.OutOrStderr(), "HEAD of %s is %s\n", gitConfig.Remote.Branch, result.Revision)
	err = awaitSync(ctx, opts.API, result.Revision)
	if err != nil {
		return err
	}
	fmt.Fprintf(cmd.OutOrStderr(), "Applied %s\n", result.Revision)
	fmt.Fprintln(cmd.OutOrStderr(), "Done.")
	return nil
}
<commit_msg>Rejig fluxctl sync output<commit_after>package main

import (
	"context"
	"fmt"

	"github.com/spf13/cobra"

	"github.com/weaveworks/flux/git"
	"github.com/weaveworks/flux/update"
)

type syncOpts struct {
	*rootOpts
}

func newSync(parent *rootOpts) *syncOpts {
	return &syncOpts{rootOpts: parent}
}

func (opts *syncOpts) Command() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "sync",
		Short: "synchronize the cluster with the git repository, now",
		RunE:  opts.RunE,
	}
	return cmd
}

func (opts *syncOpts) RunE(cmd *cobra.Command, args []string) error {
	if len(args) > 0 {
		return errorWantedNoArgs
	}

	ctx := context.Background()

	gitConfig, err := opts.API.GitRepoConfig(ctx, false)
	if err != nil {
		return err
	}

	switch gitConfig.Status {
	case git.RepoNoConfig:
		return fmt.Errorf("no git repository is configured")
	case git.RepoReady:
		break
	default:
		return fmt.Errorf("git repository %s is not ready to sync (status: %s)", gitConfig.Remote.URL, string(gitConfig.Status))
	}

	fmt.Fprintf(cmd.OutOrStderr(), "Synchronizing with %s\n", gitConfig.Remote.URL)

	updateSpec := update.Spec{
		Type: update.Sync,
		Spec: update.ManualSync{},
	}
	jobID, err := opts.API.UpdateManifests(ctx, updateSpec)
	if err != nil {
		return err
	}
	result, err := awaitJob(ctx, opts.API, jobID)
	if err != nil {
		fmt.Fprintf(cmd.OutOrStderr(), "Failed to complete sync job (ID %q)\n", jobID)
		return err
	}

	rev := result.Revision[:7]
	fmt.Fprintf(cmd.OutOrStderr(), "HEAD of %s is %s\n", gitConfig.Remote.Branch, rev)
	fmt.Fprintf(cmd.OutOrStderr(), "Waiting for %s to be applied ...\n", rev)
	err = awaitSync(ctx, opts.API, rev)
	if err != nil {
		return err
	}
	fmt.Fprintln(cmd.OutOrStderr(), "Done.")
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/CloudCom/goose/lib/goose"
)

var createCmd = &Command{
	Name:    "create",
	Usage:   "<migration_name>",
	Summary: "Create the scaffolding for a new migration",
	Help:    `create extended help here...`,
	Run:     createRun,
}

var migrationType string

func init() {
	createCmd.Flag.StringVar(&migrationType, "type", "sql", "type of migration to create [sql,go]")
}

func createRun(cmd *Command, args ...string) {
	if len(args) != 1 {
		cmd.Flag.Usage()
		os.Exit(1)
	}

	conf, err := dbConfFromFlags()
	if err != nil {
		log.Fatal(err)
	}

	if err = os.MkdirAll(conf.MigrationsDir, 0777); err != nil {
		log.Fatal(err)
	}

	n, err := goose.CreateMigration(args[0], migrationType, conf.MigrationsDir, time.Now())
	if err != nil {
		log.Fatal(err)
	}

	a, e := filepath.Abs(n)
	if e != nil {
		log.Fatal(e)
	}

	fmt.Println("goose: created", a)
}
<commit_msg>migrations directory should not be created 0777<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/CloudCom/goose/lib/goose"
)

var createCmd = &Command{
	Name:    "create",
	Usage:   "<migration_name>",
	Summary: "Create the scaffolding for a new migration",
	Help:    `create extended help here...`,
	Run:     createRun,
}

var migrationType string

func init() {
	createCmd.Flag.StringVar(&migrationType, "type", "sql", "type of migration to create [sql,go]")
}

func createRun(cmd *Command, args ...string) {
	if len(args) != 1 {
		cmd.Flag.Usage()
		os.Exit(1)
	}

	conf, err := dbConfFromFlags()
	if err != nil {
		log.Fatal(err)
	}

	if err = os.MkdirAll(conf.MigrationsDir, 0750); err != nil {
		log.Fatal(err)
	}

	n, err := goose.CreateMigration(args[0], migrationType, conf.MigrationsDir, time.Now())
	if err != nil {
		log.Fatal(err)
	}

	a, e := filepath.Abs(n)
	if e != nil {
		log.Fatal(e)
	}

	fmt.Println("goose: created", a)
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"io/ioutil"
	"path/filepath"
	"strings"

	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/errors"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju/osenv"
	"launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	coretesting "launchpad.net/juju-core/testing"
)

type DeploySuite struct {
	testing.RepoSuite
}

var _ = gc.Suite(&DeploySuite{})

func runDeploy(c *gc.C, args ...string) error {
	_, err := coretesting.RunCommand(c, &DeployCommand{}, args)
	return err
}

var initErrorTests = []struct {
	args []string
	err  string
}{
	{
		args: nil,
		err:  `no charm specified`,
	}, {
		args: []string{"charm-name", "service-name", "hotdog"},
		err:  `unrecognized args: \["hotdog"\]`,
	}, {
		args: []string{"craz~ness"},
		err:  `invalid charm name "craz~ness"`,
	}, {
		args: []string{"craziness", "burble-1"},
		err:  `invalid service name "burble-1"`,
	}, {
		args: []string{"craziness", "burble1", "-n", "0"},
		err:  `--num-units must be a positive integer`,
	}, {
		args: []string{"craziness", "burble1", "--to", "bigglesplop"},
		err:  `invalid --to parameter "bigglesplop"`,
	}, {
		args: []string{"craziness", "burble1", "-n", "2", "--to", "123"},
		err:  `cannot use --num-units > 1 with --to`,
	}, {
		args: []string{"craziness", "burble1", "--constraints", "gibber=plop"},
		err:  `invalid value "gibber=plop" for flag --constraints: unknown constraint "gibber"`,
	},
}

func (s *DeploySuite) TestInitErrors(c *gc.C) {
	for i, t := range initErrorTests {
		c.Logf("test %d", i)
		err := coretesting.InitCommand(&DeployCommand{}, t.args)
		c.Assert(err, gc.ErrorMatches, t.err)
	}
}

func (s *DeploySuite) TestNoCharm(c *gc.C) {
	err := runDeploy(c, "local:unknown-123")
	c.Assert(err, gc.ErrorMatches, `charm not found in ".*": local:precise/unknown-123`)
}

func (s *DeploySuite) TestCharmDir(c *gc.C) {
	coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	s.AssertService(c, "dummy", curl, 1, 0)
}

func (s *DeploySuite) TestUpgradeReportsDeprecated(c *gc.C) {
	coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy")
	ctx, err := coretesting.RunCommand(c, &DeployCommand{}, []string{"local:dummy", "-u"})
	c.Assert(err, gc.IsNil)

	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
	output := strings.Split(coretesting.Stderr(ctx), "\n")
	c.Check(output[0], gc.Matches, `Added charm ".*" to the environment.`)
	c.Check(output[1], gc.Equals, "--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.")
}

func (s *DeploySuite) TestUpgradeCharmDir(c *gc.C) {
	// Add the charm, so the url will exist and a new revision will be
	// picked in ServiceDeploy.
	dummyCharm := s.AddTestingCharm(c, "dummy")

	dirPath := coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:quantal/dummy")
	c.Assert(err, gc.IsNil)
	upgradedRev := dummyCharm.Revision() + 1
	curl := dummyCharm.URL().WithRevision(upgradedRev)
	s.AssertService(c, "dummy", curl, 1, 0)
	// Check the charm dir was left untouched.
	ch, err := charm.ReadDir(dirPath)
	c.Assert(err, gc.IsNil)
	c.Assert(ch.Revision(), gc.Equals, 1)
}

func (s *DeploySuite) TestCharmBundle(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy", "some-service-name")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	s.AssertService(c, "some-service-name", curl, 1, 0)
}

func (s *DeploySuite) TestSubordinateCharm(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "logging")
	err := runDeploy(c, "local:logging")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/logging-1")
	s.AssertService(c, "logging", curl, 0, 0)
}

func (s *DeploySuite) TestConfig(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	path := setupConfigFile(c, c.MkDir())
	err := runDeploy(c, "local:dummy", "dummy-service", "--config", path)
	c.Assert(err, gc.IsNil)
	service, err := s.State.Service("dummy-service")
	c.Assert(err, gc.IsNil)
	settings, err := service.ConfigSettings()
	c.Assert(err, gc.IsNil)
	c.Assert(settings, gc.DeepEquals, charm.Settings{
		"skill-level": int64(9000),
		"username":    "admin001",
	})
}

func (s *DeploySuite) TestRelativeConfigPath(c *gc.C) {
	defer coretesting.MakeEmptyFakeHome(c).Restore()
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	path := filepath.Join(osenv.Home(), "config.yaml")
	err := ioutil.WriteFile(path, nil, 0644)
	c.Assert(err, gc.IsNil)
	err = runDeploy(c, "local:dummy", "dummy-service", "--config", "~/config.yaml")
	c.Assert(err, gc.IsNil)
}

func (s *DeploySuite) TestConfigError(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	path := setupConfigFile(c, c.MkDir())
	err := runDeploy(c, "local:dummy", "other-service", "--config", path)
	c.Assert(err, gc.ErrorMatches, `no settings found for "other-service"`)
	_, err = s.State.Service("other-service")
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
}

func (s *DeploySuite) TestConstraints(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy", "--constraints", "mem=2G cpu-cores=2")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	service, _ := s.AssertService(c, "dummy", curl, 1, 0)
	cons, err := service.Constraints()
	c.Assert(err, gc.IsNil)
	c.Assert(cons, gc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2"))
}

func (s *DeploySuite) TestNetworks(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy", "--networks", ", net1, net2 , ", "--exclude-networks", "net3,net4")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	service, _ := s.AssertService(c, "dummy", curl, 1, 0)
	includeNetworks, excludeNetworks, err := service.Networks()
	c.Assert(err, gc.IsNil)
	c.Assert(includeNetworks, gc.DeepEquals, []string{"net1", "net2"})
	c.Assert(excludeNetworks, gc.DeepEquals, []string{"net3", "net4"})
}

func (s *DeploySuite) TestSubordinateConstraints(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "logging")
	err := runDeploy(c, "local:logging", "--constraints", "mem=1G")
	c.Assert(err, gc.ErrorMatches, "cannot use --constraints with subordinate service")
}

func (s *DeploySuite) TestNumUnits(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy", "-n", "13")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	s.AssertService(c, "dummy", curl, 13, 0)
}

func (s *DeploySuite) TestNumUnitsSubordinate(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "logging")
	err := runDeploy(c, "--num-units", "3", "local:logging")
	c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service")
	_, err = s.State.Service("dummy")
	c.Assert(err, gc.ErrorMatches, `service "dummy" not found`)
}

func (s *DeploySuite) assertForceMachine(c *gc.C, machineId string) {
	svc, err := s.State.Service("portlandia")
	c.Assert(err, gc.IsNil)
	units, err := svc.AllUnits()
	c.Assert(err, gc.IsNil)
	c.Assert(units, gc.HasLen, 1)
	mid, err := units[0].AssignedMachineId()
	c.Assert(err, gc.IsNil)
	c.Assert(mid, gc.Equals, machineId)
}

func (s *DeploySuite) TestForceMachine(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	machine, err := s.State.AddMachine("precise", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	err = runDeploy(c, "--to", machine.Id(), "local:dummy", "portlandia")
	c.Assert(err, gc.IsNil)
	s.assertForceMachine(c, machine.Id())
}

func (s *DeploySuite) TestForceMachineExistingContainer(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	template := state.MachineTemplate{
		Series: "precise",
		Jobs:   []state.MachineJob{state.JobHostUnits},
	}
	container, err := s.State.AddMachineInsideNewMachine(template, template, instance.LXC)
	c.Assert(err, gc.IsNil)
	err = runDeploy(c, "--to", container.Id(), "local:dummy", "portlandia")
	c.Assert(err, gc.IsNil)
	s.assertForceMachine(c, container.Id())
	machines, err := s.State.AllMachines()
	c.Assert(err, gc.IsNil)
	c.Assert(machines, gc.HasLen, 2)
}

func (s *DeploySuite) TestForceMachineNewContainer(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	machine, err := s.State.AddMachine("precise", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	err = runDeploy(c, "--to", "lxc:"+machine.Id(), "local:dummy", "portlandia")
	c.Assert(err, gc.IsNil)
	s.assertForceMachine(c, machine.Id()+"/lxc/0")
	machines, err := s.State.AllMachines()
	c.Assert(err, gc.IsNil)
	c.Assert(machines, gc.HasLen, 2)
}

func (s *DeploySuite) TestForceMachineNotFound(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "--to", "42", "local:dummy", "portlandia")
	c.Assert(err, gc.ErrorMatches, `cannot assign unit "portlandia/0" to machine: machine 42 not found`)
	_, err = s.State.Service("dummy")
	c.Assert(err, gc.ErrorMatches, `service "dummy" not found`)
}

func (s *DeploySuite) TestForceMachineSubordinate(c *gc.C) {
	machine, err := s.State.AddMachine("precise", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	coretesting.Charms.BundlePath(s.SeriesPath, "logging")
	err = runDeploy(c, "--to", machine.Id(), "local:logging")
	c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service")
	_, err = s.State.Service("dummy")
	c.Assert(err, gc.ErrorMatches, `service "dummy" not found`)
}
<commit_msg>Fix test<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"strings"

	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/errors"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju/osenv"
	"launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	coretesting "launchpad.net/juju-core/testing"
)

type DeploySuite struct {
	testing.RepoSuite
}

var _ = gc.Suite(&DeploySuite{})

func runDeploy(c *gc.C, args ...string) error {
	_, err := coretesting.RunCommand(c, &DeployCommand{}, args)
	return err
}

var initErrorTests = []struct {
	args []string
	err  string
}{
	{
		args: nil,
		err:  `no charm specified`,
	}, {
		args: []string{"charm-name", "service-name", "hotdog"},
		err:  `unrecognized args: \["hotdog"\]`,
	}, {
		args: []string{"craz~ness"},
		err:  `invalid charm name "craz~ness"`,
	}, {
		args: []string{"craziness", "burble-1"},
		err:  `invalid service name "burble-1"`,
	}, {
		args: []string{"craziness", "burble1", "-n", "0"},
		err:  `--num-units must be a positive integer`,
	}, {
		args: []string{"craziness", "burble1", "--to", "bigglesplop"},
		err:  `invalid --to parameter "bigglesplop"`,
	}, {
		args: []string{"craziness", "burble1", "-n", "2", "--to", "123"},
		err:  `cannot use --num-units > 1 with --to`,
	}, {
		args: []string{"craziness", "burble1", "--constraints", "gibber=plop"},
		err:  `invalid value "gibber=plop" for flag --constraints: unknown constraint "gibber"`,
	},
}

func (s *DeploySuite) TestInitErrors(c *gc.C) {
	for i, t := range initErrorTests {
		c.Logf("test %d", i)
		err := coretesting.InitCommand(&DeployCommand{}, t.args)
		c.Assert(err, gc.ErrorMatches, t.err)
	}
}

func (s *DeploySuite) TestNoCharm(c *gc.C) {
	err := runDeploy(c, "local:unknown-123")
	c.Assert(err, gc.ErrorMatches, `charm not found in ".*": local:precise/unknown-123`)
}

func (s *DeploySuite) TestCharmDir(c *gc.C) {
	coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	s.AssertService(c, "dummy", curl, 1, 0)
}

func (s *DeploySuite) TestUpgradeReportsDeprecated(c *gc.C) {
	coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy")
	ctx, err := coretesting.RunCommand(c, &DeployCommand{}, []string{"local:dummy", "-u"})
	c.Assert(err, gc.IsNil)

	c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
	output := strings.Split(coretesting.Stderr(ctx), "\n")
	c.Check(output[0], gc.Matches, `Added charm ".*" to the environment.`)
	c.Check(output[1], gc.Equals, "--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.")
}

func (s *DeploySuite) TestUpgradeCharmDir(c *gc.C) {
	// Add the charm, so the url will exist and a new revision will be
	// picked in ServiceDeploy.
	dummyCharm := s.AddTestingCharm(c, "dummy")

	dirPath := coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:quantal/dummy")
	c.Assert(err, gc.IsNil)
	upgradedRev := dummyCharm.Revision() + 1
	curl := dummyCharm.URL().WithRevision(upgradedRev)
	s.AssertService(c, "dummy", curl, 1, 0)
	// Check the charm dir was left untouched.
	ch, err := charm.ReadDir(dirPath)
	c.Assert(err, gc.IsNil)
	c.Assert(ch.Revision(), gc.Equals, 1)
}

func (s *DeploySuite) TestCharmBundle(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy", "some-service-name")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	s.AssertService(c, "some-service-name", curl, 1, 0)
}

func (s *DeploySuite) TestSubordinateCharm(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "logging")
	err := runDeploy(c, "local:logging")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/logging-1")
	s.AssertService(c, "logging", curl, 0, 0)
}

func (s *DeploySuite) TestConfig(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	path := setupConfigFile(c, c.MkDir())
	err := runDeploy(c, "local:dummy", "dummy-service", "--config", path)
	c.Assert(err, gc.IsNil)
	service, err := s.State.Service("dummy-service")
	c.Assert(err, gc.IsNil)
	settings, err := service.ConfigSettings()
	c.Assert(err, gc.IsNil)
	c.Assert(settings, gc.DeepEquals, charm.Settings{
		"skill-level": int64(9000),
		"username":    "admin001",
	})
}

func (s *DeploySuite) TestRelativeConfigPath(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	// Putting a config file in home is okay as $HOME is set to a tempdir
	setupConfigFile(c, osenv.Home())
	err := runDeploy(c, "local:dummy", "dummy-service", "--config", "~/testconfig.yaml")
	c.Assert(err, gc.IsNil)
}

func (s *DeploySuite) TestConfigError(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	path := setupConfigFile(c, c.MkDir())
	err := runDeploy(c, "local:dummy", "other-service", "--config", path)
	c.Assert(err, gc.ErrorMatches, `no settings found for "other-service"`)
	_, err = s.State.Service("other-service")
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
}

func (s *DeploySuite) TestConstraints(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy", "--constraints", "mem=2G cpu-cores=2")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	service, _ := s.AssertService(c, "dummy", curl, 1, 0)
	cons, err := service.Constraints()
	c.Assert(err, gc.IsNil)
	c.Assert(cons, gc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2"))
}

func (s *DeploySuite) TestNetworks(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy", "--networks", ", net1, net2 , ", "--exclude-networks", "net3,net4")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	service, _ := s.AssertService(c, "dummy", curl, 1, 0)
	includeNetworks, excludeNetworks, err := service.Networks()
	c.Assert(err, gc.IsNil)
	c.Assert(includeNetworks, gc.DeepEquals, []string{"net1", "net2"})
	c.Assert(excludeNetworks, gc.DeepEquals, []string{"net3", "net4"})
}

func (s *DeploySuite) TestSubordinateConstraints(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "logging")
	err := runDeploy(c, "local:logging", "--constraints", "mem=1G")
	c.Assert(err, gc.ErrorMatches, "cannot use --constraints with subordinate service")
}

func (s *DeploySuite) TestNumUnits(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "local:dummy", "-n", "13")
	c.Assert(err, gc.IsNil)
	curl := charm.MustParseURL("local:precise/dummy-1")
	s.AssertService(c, "dummy", curl, 13, 0)
}

func (s *DeploySuite) TestNumUnitsSubordinate(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "logging")
	err := runDeploy(c, "--num-units", "3", "local:logging")
	c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service")
	_, err = s.State.Service("dummy")
	c.Assert(err, gc.ErrorMatches, `service "dummy" not found`)
}

func (s *DeploySuite) assertForceMachine(c *gc.C, machineId string) {
	svc, err := s.State.Service("portlandia")
	c.Assert(err, gc.IsNil)
	units, err := svc.AllUnits()
	c.Assert(err, gc.IsNil)
	c.Assert(units, gc.HasLen, 1)
	mid, err := units[0].AssignedMachineId()
	c.Assert(err, gc.IsNil)
	c.Assert(mid, gc.Equals, machineId)
}

func (s *DeploySuite) TestForceMachine(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	machine, err := s.State.AddMachine("precise", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	err = runDeploy(c, "--to", machine.Id(), "local:dummy", "portlandia")
	c.Assert(err, gc.IsNil)
	s.assertForceMachine(c, machine.Id())
}

func (s *DeploySuite) TestForceMachineExistingContainer(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	template := state.MachineTemplate{
		Series: "precise",
		Jobs:   []state.MachineJob{state.JobHostUnits},
	}
	container, err := s.State.AddMachineInsideNewMachine(template, template, instance.LXC)
	c.Assert(err, gc.IsNil)
	err = runDeploy(c, "--to", container.Id(), "local:dummy", "portlandia")
	c.Assert(err, gc.IsNil)
	s.assertForceMachine(c, container.Id())
	machines, err := s.State.AllMachines()
	c.Assert(err, gc.IsNil)
	c.Assert(machines, gc.HasLen, 2)
}

func (s *DeploySuite) TestForceMachineNewContainer(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	machine, err := s.State.AddMachine("precise", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	err = runDeploy(c, "--to", "lxc:"+machine.Id(), "local:dummy", "portlandia")
	c.Assert(err, gc.IsNil)
	s.assertForceMachine(c, machine.Id()+"/lxc/0")
	machines, err := s.State.AllMachines()
	c.Assert(err, gc.IsNil)
	c.Assert(machines, gc.HasLen, 2)
}

func (s *DeploySuite) TestForceMachineNotFound(c *gc.C) {
	coretesting.Charms.BundlePath(s.SeriesPath, "dummy")
	err := runDeploy(c, "--to", "42", "local:dummy", "portlandia")
	c.Assert(err, gc.ErrorMatches, `cannot assign unit "portlandia/0" to machine: machine 42 not found`)
	_, err = s.State.Service("dummy")
	c.Assert(err, gc.ErrorMatches, `service "dummy" not found`)
}

func (s *DeploySuite) TestForceMachineSubordinate(c *gc.C) {
	machine, err := s.State.AddMachine("precise", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	coretesting.Charms.BundlePath(s.SeriesPath, "logging")
	err = runDeploy(c, "--to", machine.Id(), "local:logging")
	c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service")
	_, err = s.State.Service("dummy")
	c.Assert(err, gc.ErrorMatches, `service "dummy" not found`)
}
<|endoftext|>