<commit_before>// 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 main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)

const TIMEOUT = time.Duration(time.Second * 10)
const URL = "http://finance.yahoo.com/webservice/v1/symbols/%s/quote?format=json"
const UP = "↑"   // rune 8593
const DOWN = "↓" // rune 8595

var re = regexp.MustCompile(`^\d.+\.\d{2}`) // this is to have only 2 decimal places
var signalChan = make(chan os.Signal, 1)    // channel to catch ctrl-c

var (
	symbolFlag   = flag.String("s", "", "Symbols for ticker, comma seperate (no spaces)")
	intervalFlag = flag.Int("i", 1, "Interval for stock data to be updated in seconds")
)

type Stock struct {
	List List `json:"list"`
}

type List struct {
	Meta      Meta        `json:"meta"`
	Resources []Resources `json:"resources"`
}

type Meta struct {
	Type  string `json:"type"`
	Start uint   `json:"start"`
	Count uint   `json:"count"`
}

type Resources struct {
	Resource Resource `json:"resource"`
}

type Resource struct {
	Classname string `json:"classname"`
	Fields    Fields `json:"fields"`
}

type Fields struct {
	Name    string `json:"name"`
	Price   string `json:"price"`
	Symbol  string `json:"symbol"`
	TS      string `json:"ts"`
	Type    string `json:"type"`
	UTCTime string `json:"utctime"`
	Volume  string `json:"volume"`
}

type stockwatcher struct {
	quotes   map[string]map[string]float64
	interval time.Duration
	m        *sync.Mutex
}

// clearScreen runs a shell clear command
func clearScreen() {
	c := exec.Command("clear")
	c.Stdout = os.Stdout
	c.Run()
}

func NewStockWatcher(i time.Duration) *stockwatcher {
	return &stockwatcher{
		quotes:   make(map[string]map[string]float64),
		interval: i,
		m:        &sync.Mutex{},
	}
}

func (t *stockwatcher) add(symbol string) {
	t.m.Lock()
	defer t.m.Unlock()
	if _, ok := t.quotes[symbol]; !ok {
		t.quotes[symbol] = map[string]float64{}
	}

}

func (t *stockwatcher) updateStock(symbol string, price float64) {
	t.m.Lock()
	defer t.m.Unlock()
	t.quotes[symbol] = map[string]float64{
		"previous": t.quotes[symbol]["current"],
		"current":  price,
	}
}

// query will retrieve data for a given symbol
// TODO(briandowns) Instead of dying on failure, set the symbol data to nil values
func query(symbol string) (*Stock, error) {
	data := &Stock{}
	client := http.Client{
		Timeout: TIMEOUT,
	}

	resp, err := client.Get(fmt.Sprintf(URL, symbol))
	if err != nil {
		return nil, errors.New("unable to retrive symbol data")
	}
	defer resp.Body.Close()

	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {

		log.Fatalln(err)
		os.Exit(1)
	}
	return data, nil
}

func convertPrice(p string) float64 {
	price, err := strconv.ParseFloat(p, 64)
	if err != nil {
		log.Fatalln(err)
		os.Exit(1)
	}
	return price
}

func (t *stockwatcher) runner() {
	var wg sync.WaitGroup
	for k, _ := range t.quotes {
		wg.Add(1)
		go func(k string) {
			defer wg.Done()
			stock, err := query(k)
			if err != nil {
				log.Fatalln(err)
				os.Exit(1)
			}
			t.updateStock(stock.List.Resources[0].Resource.Fields.Symbol,
				convertPrice(re.FindString(stock.List.Resources[0].Resource.Fields.Price)),
			)
		}(k)
	}
	wg.Wait()
}

func (t *stockwatcher) printData() {
	var keys []string
	for k := range t.quotes {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	pos := 1
	for _, k := range keys {
		if t.quotes[k]["previous"] == 0.00 || t.quotes[k]["previous"] == t.quotes[k]["current"] {
			printTb(1, pos, fmt.Sprintf("%-6s %-7v %11s %-4s\n", k, t.quotes[k]["current"], "%", "-"), termbox.ColorWhite, termbox.ColorDefault)
			//printTb(1, pos, fmt.Sprintf("%6s %7v %7s %4s\n", k, t.quotes[k]["current"], "-", "-"), termbox.ColorWhite, termbox.ColorDefault)
			pos++
			//fmt.Printf("%6s %7v %%%5s %4s\n", k, v["current"], "-", "-")
		} else if t.quotes[k]["current"] > t.quotes[k]["previous"] {
			printTb(1, pos, fmt.Sprintf("%-6s %-7v +%-.6f %% %-4s\n", k, t.quotes[k]["current"], t.quotes[k]["current"]/t.quotes[k]["previous"], UP), termbox.ColorGreen, termbox.ColorDefault)
			//printTb(1, pos, fmt.Sprintf("%6s %7v %7v %4s\n", k, t.quotes[k]["current"], t.quotes[k]["previous"], UP), termbox.ColorGreen, termbox.ColorDefault)
			//fmt.Printf("%6s %7v %%%5v %4s\n", k, v["current"], 100*(v["previous"]/v["current"]), green(UP))
			pos++
		} else {
			printTb(1, pos, fmt.Sprintf("%-6s %-7v -%-.6f %% %-4s\n", k, t.quotes[k]["current"], t.quotes[k]["current"]/t.quotes[k]["previous"], DOWN), termbox.ColorRed, termbox.ColorDefault)
			//fmt.Printf("%6s %7v %%%5v %4s\n", k, v["current"], 100*(v["previous"]/v["current"]), red(DOWN))
			pos++
		}
	}
}

func printTb(x, y int, msg string, fg, bg termbox.Attribute) {
	for _, c := range []rune(msg) {
		//termbox.SetCell(x, y, c, termbox.ColorWhite, termbox.ColorDefault)
		termbox.SetCell(x, y, c, fg, bg)
		x += runewidth.RuneWidth(c)
	}
	termbox.Flush()
}

func main() {
	flag.Parse()

	signal.Notify(signalChan, os.Interrupt)
	go func() {
		for range signalChan {
			os.Exit(1)
		}
	}()

	if flag.NFlag() != 2 || *symbolFlag == "" {
		flag.Usage()
		os.Exit(1)
	}

	t := NewStockWatcher(time.Duration(*intervalFlag) * time.Second)

	switch {
	case strings.Contains(*symbolFlag, ","):
		for _, a := range strings.Split(*symbolFlag, ",") {
			t.add(a)
		}
	default:
		t.add(*symbolFlag)
	}

	err := termbox.Init()
	if err != nil {
		log.Fatal(err)
	}
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	event := make(chan termbox.Event)
	go func() {
		for {
			// Post events to channel
			event <- termbox.PollEvent()
		}
	}()

loop:
	for {
		t.runner()
		t.printData()

		// Poll key event or timeout (maybe)
		select {
		case <-event:
			break loop
		case <-time.After(t.interval):
			continue loop
		}
	}
	close(event)
	time.Sleep(1 * time.Second)
	termbox.Close()
	os.Exit(0)
}
<commit_msg>add comments and refactor a function to be named more appropriately<commit_after>// 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.

// A CLI application to monitor one or more stocks.
package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)

const TIMEOUT = time.Duration(time.Second * 10)
const URL = "http://finance.yahoo.com/webservice/v1/symbols/%s/quote?format=json"
const UP = "↑"   // rune 8593
const DOWN = "↓" // rune 8595

var re = regexp.MustCompile(`^\d.+\.\d{2}`) // this is to have only 2 decimal places
var signalChan = make(chan os.Signal, 1)    // channel to catch ctrl-c

// Flag variables to hold CLI arguments
var (
	symbolFlag   = flag.String("s", "", "Symbols for ticker, comma seperate (no spaces)")
	intervalFlag = flag.Int("i", 1, "Interval for stock data to be updated in seconds")
)

type Stock struct {
	List List `json:"list"`
}

type List struct {
	Meta      Meta        `json:"meta"`
	Resources []Resources `json:"resources"`
}

type Meta struct {
	Type  string `json:"type"`
	Start uint   `json:"start"`
	Count uint   `json:"count"`
}

type Resources struct {
	Resource Resource `json:"resource"`
}

type Resource struct {
	Classname string `json:"classname"`
	Fields    Fields `json:"fields"`
}

// Fields holds all of the retrieved data from the API call
type Fields struct {
	Name    string `json:"name"`    // name of company
	Price   string `json:"price"`   // current price
	Symbol  string `json:"symbol"`  // stock symbol
	TS      string `json:"ts"`      //
	Type    string `json:"type"`    // type of stock (equity, etc...)
	UTCTime string `json:"utctime"` // time in UTC
	Volume  string `json:"volume"`  // shares traded
}

// stockwatcher holds the relevant data for the running instance
type stockwatcher struct {
	quotes   map[string]map[string]float64
	interval time.Duration
	m        *sync.Mutex
}

// clearScreen runs a shell clear command
func clearScreen() {
	c := exec.Command("clear")
	c.Stdout = os.Stdout
	c.Run()
}

// NewStockWatcher returns a new instance of stockwatcher with the given parameters
func NewStockWatcher(i time.Duration) *stockwatcher {
	return &stockwatcher{
		quotes:   make(map[string]map[string]float64),
		interval: i,
		m:        &sync.Mutex{},
	}
}

// add takes the given symbol and populates a key in the quotes map
func (t *stockwatcher) add(symbol string) {
	t.m.Lock()
	defer t.m.Unlock()
	if _, ok := t.quotes[symbol]; !ok {
		t.quotes[symbol] = map[string]float64{}
	}

}

// updateStock populates stockwatcher struct with stock prices
func (t *stockwatcher) updateStock(symbol string, price float64) {
	t.m.Lock()
	defer t.m.Unlock()
	t.quotes[symbol] = map[string]float64{
		"previous": t.quotes[symbol]["current"],
		"current":  price,
	}
}

// query will retrieve data for a given symbol
// TODO(briandowns) Instead of dying on failure, set the symbol data to nil values
func query(symbol string) (*Stock, error) {
	data := &Stock{}
	client := http.Client{
		Timeout: TIMEOUT,
	}

	resp, err := client.Get(fmt.Sprintf(URL, symbol))
	if err != nil {
		return nil, errors.New("unable to retrive symbol data")
	}
	defer resp.Body.Close()

	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {

		log.Fatalln(err)
		os.Exit(1)
	}
	return data, nil
}

// convertPrice converts the given string to a float64 value
func convertPrice(p string) float64 {
	price, err := strconv.ParseFloat(p, 64)
	if err != nil {
		log.Fatalln(err)
		os.Exit(1)
	}
	return price
}

// runner goes through and gets the data for each symbol
func (t *stockwatcher) runner() {
	var wg sync.WaitGroup
	for k, _ := range t.quotes {
		wg.Add(1)
		go func(k string) {
			defer wg.Done()
			stock, err := query(k)
			if err != nil {
				log.Fatalln(err)
				os.Exit(1)
			}
			t.updateStock(stock.List.Resources[0].Resource.Fields.Symbol,
				convertPrice(re.FindString(stock.List.Resources[0].Resource.Fields.Price)),
			)
		}(k)
	}
	wg.Wait()
}

// formatData formats the given data for printing
func (t *stockwatcher) formatData() {
	var keys []string
	for k := range t.quotes {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	pos := 1
	for _, k := range keys {
		if t.quotes[k]["previous"] == 0.00 || t.quotes[k]["previous"] == t.quotes[k]["current"] {
			printTb(1, pos, fmt.Sprintf("%-6s %-7v %11s %-4s\n", k, t.quotes[k]["current"], "%", "-"), termbox.ColorWhite, termbox.ColorDefault)
			//printTb(1, pos, fmt.Sprintf("%6s %7v %7s %4s\n", k, t.quotes[k]["current"], "-", "-"), termbox.ColorWhite, termbox.ColorDefault)
			pos++
			//fmt.Printf("%6s %7v %%%5s %4s\n", k, v["current"], "-", "-")
		} else if t.quotes[k]["current"] > t.quotes[k]["previous"] {
			printTb(1, pos, fmt.Sprintf("%-6s %-7v +%-.6f %% %-4s\n", k, t.quotes[k]["current"], t.quotes[k]["current"]/t.quotes[k]["previous"], UP), termbox.ColorGreen, termbox.ColorDefault)
			//printTb(1, pos, fmt.Sprintf("%6s %7v %7v %4s\n", k, t.quotes[k]["current"], t.quotes[k]["previous"], UP), termbox.ColorGreen, termbox.ColorDefault)
			//fmt.Printf("%6s %7v %%%5v %4s\n", k, v["current"], 100*(v["previous"]/v["current"]), green(UP))
			pos++
		} else {
			printTb(1, pos, fmt.Sprintf("%-6s %-7v -%-.6f %% %-4s\n", k, t.quotes[k]["current"], t.quotes[k]["current"]/t.quotes[k]["previous"], DOWN), termbox.ColorRed, termbox.ColorDefault)
			//fmt.Printf("%6s %7v %%%5v %4s\n", k, v["current"], 100*(v["previous"]/v["current"]), red(DOWN))
			pos++
		}
	}
}

// printTb prints the given data out to the screen
func printTb(x, y int, msg string, fg, bg termbox.Attribute) {
	for _, c := range []rune(msg) {
		//termbox.SetCell(x, y, c, termbox.ColorWhite, termbox.ColorDefault)
		termbox.SetCell(x, y, c, fg, bg)
		x += runewidth.RuneWidth(c)
	}
	termbox.Flush()
}

func main() {
	flag.Parse()

	// catch ctrl+c's
	signal.Notify(signalChan, os.Interrupt)
	go func() {
		for range signalChan {
			os.Exit(1)
		}
	}()

	// make sure we got what was expected from the CLI
	if flag.NFlag() != 2 || *symbolFlag == "" {
		flag.Usage()
		os.Exit(1)
	}

	t := NewStockWatcher(time.Duration(*intervalFlag) * time.Second)

	// check if more than one symbol has been given
	switch {
	case strings.Contains(*symbolFlag, ","):
		for _, a := range strings.Split(*symbolFlag, ",") {
			t.add(a)
		}
	default:
		t.add(*symbolFlag)
	}

	// initialize termbox
	err := termbox.Init()
	if err != nil {
		log.Fatal(err)
	}
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	event := make(chan termbox.Event)
	go func() {
		for {
			// Post events to channel
			event <- termbox.PollEvent()
		}
	}()

loop:
	for {
		t.runner()
		t.formatData()

		// Poll key event or timeout (maybe)
		select {
		case <-event:
			break loop
		case <-time.After(t.interval):
			continue loop
		}
	}
	close(event)
	time.Sleep(1 * time.Second)
	termbox.Close()
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"os"
	"time"
)

func main() {
	port := 3001
	host := fmt.Sprintf(":%d", port)
	fmt.Println("dailing: ", host)

	conn, err := net.Dial("tcp", host)
	if err != nil {
		fmt.Println("error connecting to host ", host)
		os.Exit(1)
	}

	defer conn.Close()
	count := 0

    buf := make([]byte, 512)

	for {
		count++
        if count % 10 == 0 {
            time.Sleep(5 * time.Second)
        }

		text := "ulid\n\r"
		_, err := fmt.Fprintf(conn, text)
		if err != nil {
			fmt.Println("lost connection...")
			return
		}

		fmt.Printf("sent: %s", text)

        n, err := conn.Read(buf)
        if err != nil {
			fmt.Println("lost connection...")
			return
        }

        fmt.Printf("recd: %s", buf[:n]);
	}
}
<commit_msg>added all commands to tcp example client<commit_after>package main

import (
	"fmt"
	"net"
	"os"
    "strings"
	"time"
)

func main() {
	port := 3001
	host := fmt.Sprintf(":%d", port)
	fmt.Println("dailing: ", host)

	conn, err := net.Dial("tcp", host)
	if err != nil {
		fmt.Println("error connecting to host ", host)
		os.Exit(1)
	}

    cmds := strings.Split("uuid ulid guid tsid txid bytes", " ")
	defer conn.Close()
	count := 0

    buf := make([]byte, 512)

	for {
        cmd := cmds[(count % len(cmds))]
		count++
        if count % 10 == 0 {
            time.Sleep(5 * time.Second)
        }

		text := fmt.Sprintf("%s\n\r", cmd)
		_, err := fmt.Fprintf(conn, text)
		if err != nil {
			fmt.Println("lost connection...")
			return
		}

		fmt.Printf("sent: %s", text)

        n, err := conn.Read(buf)
        if err != nil {
			fmt.Println("lost connection...")
			return
        }

        fmt.Printf("recd: %s", buf[:n]);
	}
}
<|endoftext|>


<commit_before>package xmux

import (
	"net/http"

	"github.com/rs/xhandler"
)

// Group makes it simple to configure a group of routes with the
// same prefix. Use mux.NewGroup("/prefix") to create a group.
type Group struct {
	m *Mux
	p string
}

func newRouteGroup(mux *Mux, path string) *Group {
	if path[0] != '/' {
		panic("path must begin with '/' in path '" + path + "'")
	}

	//Strip traling / (if present) as all added sub paths must start with a /
	if path[len(path)-1] == '/' {
		path = path[:len(path)-1]
	}
	return &Group{m: mux, p: path}
}

// NewGroup creates a new sub routes group with the provided path prefix.
// All routes added to the returned group will have the path prepended.
func (g *Group) NewGroup(path string) *Group {
	return newRouteGroup(g.m, g.subPath(path))
}

// GET is a shortcut for g.Handle("GET", path, handler)
func (g *Group) GET(path string, handler xhandler.HandlerC) {
	g.HandleC("GET", path, handler)
}

// HEAD is a shortcut for g.Handle("HEAD", path, handler)
func (g *Group) HEAD(path string, handler xhandler.HandlerC) {
	g.HandleC("HEAD", path, handler)
}

// OPTIONS is a shortcut for g.Handle("OPTIONS", path, handler)
func (g *Group) OPTIONS(path string, handler xhandler.HandlerC) {
	g.HandleC("OPTIONS", path, handler)
}

// POST is a shortcut for g.Handle("POST", path, handler)
func (g *Group) POST(path string, handler xhandler.HandlerC) {
	g.HandleC("POST", path, handler)
}

// PUT is a shortcut for g.Handle("PUT", path, handler)
func (g *Group) PUT(path string, handler xhandler.HandlerC) {
	g.HandleC("PUT", path, handler)
}

// PATCH is a shortcut for g.Handle("PATCH", path, handler)
func (g *Group) PATCH(path string, handler xhandler.HandlerC) {
	g.HandleC("PATCH", path, handler)
}

// DELETE is a shortcut for g.Handle("DELETE", path, handler)
func (g *Group) DELETE(path string, handler xhandler.HandlerC) {
	g.HandleC("DELETE", path, handler)
}

// HandleC registers a net/context aware request handler with the given
// path and method.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
func (g *Group) HandleC(method, path string, handler xhandler.HandlerC) {
	g.m.HandleC(method, g.subPath(path), handler)
}

// Handle regiester a standard http.Handler request handler with the given
// path and method. With this adapter, your handler won't have access to the
// context and thus won't work with URL parameters.
func (g *Group) Handle(method, path string, handler http.Handler) {
	g.m.Handle(method, g.subPath(path), handler)
}

// HandleFunc regiester a standard http.HandlerFunc request handler with the given
// path and method. With this adapter, your handler won't have access to the
// context and thus won't work with URL parameters.
func (g *Group) HandleFunc(method, path string, handler http.HandlerFunc) {
	g.m.HandleFunc(method, g.subPath(path), handler)
}

func (g *Group) subPath(path string) string {
	if path[0] != '/' {
		panic("path must start with a '/'")
	}
	return g.p + path
}
<commit_msg>Fix a typo<commit_after>package xmux

import (
	"net/http"

	"github.com/rs/xhandler"
)

// Group makes it simple to configure a group of routes with the
// same prefix. Use mux.NewGroup("/prefix") to create a group.
type Group struct {
	m *Mux
	p string
}

func newRouteGroup(mux *Mux, path string) *Group {
	if path[0] != '/' {
		panic("path must begin with '/' in path '" + path + "'")
	}

	//Strip traling / (if present) as all added sub paths must start with a /
	if path[len(path)-1] == '/' {
		path = path[:len(path)-1]
	}
	return &Group{m: mux, p: path}
}

// NewGroup creates a new sub routes group with the provided path prefix.
// All routes added to the returned group will have the path prepended.
func (g *Group) NewGroup(path string) *Group {
	return newRouteGroup(g.m, g.subPath(path))
}

// GET is a shortcut for g.Handle("GET", path, handler)
func (g *Group) GET(path string, handler xhandler.HandlerC) {
	g.HandleC("GET", path, handler)
}

// HEAD is a shortcut for g.Handle("HEAD", path, handler)
func (g *Group) HEAD(path string, handler xhandler.HandlerC) {
	g.HandleC("HEAD", path, handler)
}

// OPTIONS is a shortcut for g.Handle("OPTIONS", path, handler)
func (g *Group) OPTIONS(path string, handler xhandler.HandlerC) {
	g.HandleC("OPTIONS", path, handler)
}

// POST is a shortcut for g.Handle("POST", path, handler)
func (g *Group) POST(path string, handler xhandler.HandlerC) {
	g.HandleC("POST", path, handler)
}

// PUT is a shortcut for g.Handle("PUT", path, handler)
func (g *Group) PUT(path string, handler xhandler.HandlerC) {
	g.HandleC("PUT", path, handler)
}

// PATCH is a shortcut for g.Handle("PATCH", path, handler)
func (g *Group) PATCH(path string, handler xhandler.HandlerC) {
	g.HandleC("PATCH", path, handler)
}

// DELETE is a shortcut for g.Handle("DELETE", path, handler)
func (g *Group) DELETE(path string, handler xhandler.HandlerC) {
	g.HandleC("DELETE", path, handler)
}

// HandleC registers a net/context aware request handler with the given
// path and method.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
func (g *Group) HandleC(method, path string, handler xhandler.HandlerC) {
	g.m.HandleC(method, g.subPath(path), handler)
}

// Handle regiester a standard http.Handler request handler with the given
// path and method. With this adapter, your handler won't have access to the
// context and thus won't work with URL parameters.
func (g *Group) Handle(method, path string, handler http.Handler) {
	g.m.Handle(method, g.subPath(path), handler)
}

// HandleFunc registers a standard http.HandlerFunc request handler with the given
// path and method. With this adapter, your handler won't have access to the
// context and thus won't work with URL parameters.
func (g *Group) HandleFunc(method, path string, handler http.HandlerFunc) {
	g.m.HandleFunc(method, g.subPath(path), handler)
}

func (g *Group) subPath(path string) string {
	if path[0] != '/' {
		panic("path must start with a '/'")
	}
	return g.p + path
}
<|endoftext|>


<commit_before>package workspace

import (
	"github.com/qiniu/go-sdk/v7/auth"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"path/filepath"

	"github.com/qiniu/qshell/v2/iqshell/common/account"
	"github.com/qiniu/qshell/v2/iqshell/common/config"
	"github.com/qiniu/qshell/v2/iqshell/common/log"
	"github.com/qiniu/qshell/v2/iqshell/common/utils"
)

type LoadInfo struct {
	UserConfigPath   string
	CmdConfig        *config.Config
	WorkspacePath    string
	JobPathBuilder   func(cmdPath string) string
	globalConfigPath string
}

// Load 加载工作环境
func Load(info LoadInfo) (err *data.CodeError) {
	err = info.initInfo()
	if err != nil {
		return
	}

	// 检查工作目录
	if len(info.WorkspacePath) == 0 {
		err = data.NewEmptyError().AppendDesc("can't get home dir")
		return
	}
	workspaceDir = info.WorkspacePath
	log.Debug("workspace:" + workspaceDir)

	err = utils.CreateDirIfNotExist(workspaceDir)
	if err != nil {
		return
	}

	// 加载账户
	accountDBPath := filepath.Join(workspaceDir, usersDBName)
	accountPath := filepath.Join(workspaceDir, currentUserFileName)
	oldAccountPath := filepath.Join(workspaceDir, oldUserFileName)
	err = account.Load(account.LoadInfo{
		AccountPath:    accountPath,
		OldAccountPath: oldAccountPath,
		AccountDBPath:  accountDBPath,
	})
	if err != nil {
		log.ErrorF("load account error:%v", err)
		return
	}

	// 检查用户路径
	acc, err := account.GetAccount()
	if err == nil {
		currentAccount = &acc
		accountName := acc.Name
		if len(accountName) == 0 {
			accountName = currentAccount.AccessKey
		}
		log.DebugF("current user name:%s", accountName)

		userDir = filepath.Join(workspaceDir, usersDirName, accountName)

		// 配置 config 的 Credentials
		cfg.Credentials = &auth.Credentials{
			AccessKey: acc.AccessKey,
			SecretKey: []byte(acc.SecretKey),
		}
	} else {
		userDir = filepath.Join(workspaceDir, usersDirName, defaultUserDirName)
	}
	log.DebugF("user dir:%s", userDir)

	// 配置 Job path
	jobDir = filepath.Join(userDir, info.CmdConfig.CmdId)
	if info.JobPathBuilder != nil {
		jobDir = info.JobPathBuilder(jobDir)
	}
	err = utils.CreateDirIfNotExist(jobDir)
	if err != nil {
		return data.NewEmptyError().AppendDescF("create job dir error:%v", err)
	}
	log.DebugF("job dir:%s", userDir)

	// 检查用户配置，用户配置可能被指定，如果未指定则使用用户目录下配置
	if len(info.UserConfigPath) == 0 && len(userDir) > 0 {
		info.UserConfigPath = filepath.Join(userDir, configFileName)
	}

	// 设置配置文件路径
	err = config.Load(config.LoadInfo{
		UserConfigPath:   info.UserConfigPath,
		GlobalConfigPath: info.globalConfigPath,
	})
	if err != nil {
		log.ErrorF("load config error:%v", err)
		return
	}

	// 加载配置
	resultCfg := &config.Config{}
	resultCfg.Merge(info.CmdConfig)
	resultCfg.Merge(config.GetUser())
	resultCfg.Merge(config.GetGlobal())
	resultCfg.Merge(defaultConfig())
	cfg = resultCfg

	log.DebugF("cmd    config:\n%v", info.CmdConfig)
	log.DebugF("user   config(%s):\n%v", info.UserConfigPath, config.GetUser())
	log.DebugF("global config(%s):\n%v", info.globalConfigPath, config.GetGlobal())
	log.DebugF("final  config:\n%v", cfg)

	err = checkConfig(cfg)
	if err != nil {
		return
	}

	// 在工作区加载之后监听
	observerCmdInterrupt()

	return
}

func (w *LoadInfo) initInfo() *data.CodeError {
	home, err := utils.GetHomePath()
	if err != nil {
		return data.NewEmptyError().AppendDescF("get home path error:%v", err)
	}
	if len(w.WorkspacePath) == 0 {
		w.WorkspacePath = filepath.Join(home, workspaceName)
	}
	// 全局配置文件路径，兼容老版本，位置在用户目录下
	w.globalConfigPath = filepath.Join(home, configFileName)
	return nil
}
<commit_msg>change  log info: job dir<commit_after>package workspace

import (
	"github.com/qiniu/go-sdk/v7/auth"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"path/filepath"

	"github.com/qiniu/qshell/v2/iqshell/common/account"
	"github.com/qiniu/qshell/v2/iqshell/common/config"
	"github.com/qiniu/qshell/v2/iqshell/common/log"
	"github.com/qiniu/qshell/v2/iqshell/common/utils"
)

type LoadInfo struct {
	UserConfigPath   string
	CmdConfig        *config.Config
	WorkspacePath    string
	JobPathBuilder   func(cmdPath string) string
	globalConfigPath string
}

// Load 加载工作环境
func Load(info LoadInfo) (err *data.CodeError) {
	err = info.initInfo()
	if err != nil {
		return
	}

	// 检查工作目录
	if len(info.WorkspacePath) == 0 {
		err = data.NewEmptyError().AppendDesc("can't get home dir")
		return
	}
	workspaceDir = info.WorkspacePath
	log.Debug("workspace:" + workspaceDir)

	err = utils.CreateDirIfNotExist(workspaceDir)
	if err != nil {
		return
	}

	// 加载账户
	accountDBPath := filepath.Join(workspaceDir, usersDBName)
	accountPath := filepath.Join(workspaceDir, currentUserFileName)
	oldAccountPath := filepath.Join(workspaceDir, oldUserFileName)
	err = account.Load(account.LoadInfo{
		AccountPath:    accountPath,
		OldAccountPath: oldAccountPath,
		AccountDBPath:  accountDBPath,
	})
	if err != nil {
		log.ErrorF("load account error:%v", err)
		return
	}

	// 检查用户路径
	acc, err := account.GetAccount()
	if err == nil {
		currentAccount = &acc
		accountName := acc.Name
		if len(accountName) == 0 {
			accountName = currentAccount.AccessKey
		}
		log.DebugF("current user name:%s", accountName)

		userDir = filepath.Join(workspaceDir, usersDirName, accountName)

		// 配置 config 的 Credentials
		cfg.Credentials = &auth.Credentials{
			AccessKey: acc.AccessKey,
			SecretKey: []byte(acc.SecretKey),
		}
	} else {
		userDir = filepath.Join(workspaceDir, usersDirName, defaultUserDirName)
	}
	log.DebugF("user dir:%s", userDir)

	// 配置 Job path
	jobDir = filepath.Join(userDir, info.CmdConfig.CmdId)
	if info.JobPathBuilder != nil {
		jobDir = info.JobPathBuilder(jobDir)
	}
	err = utils.CreateDirIfNotExist(jobDir)
	if err != nil {
		return data.NewEmptyError().AppendDescF("create job dir error:%v", err)
	}
	log.DebugF("job dir:%s", jobDir)

	// 检查用户配置，用户配置可能被指定，如果未指定则使用用户目录下配置
	if len(info.UserConfigPath) == 0 && len(userDir) > 0 {
		info.UserConfigPath = filepath.Join(userDir, configFileName)
	}

	// 设置配置文件路径
	err = config.Load(config.LoadInfo{
		UserConfigPath:   info.UserConfigPath,
		GlobalConfigPath: info.globalConfigPath,
	})
	if err != nil {
		log.ErrorF("load config error:%v", err)
		return
	}

	// 加载配置
	resultCfg := &config.Config{}
	resultCfg.Merge(info.CmdConfig)
	resultCfg.Merge(config.GetUser())
	resultCfg.Merge(config.GetGlobal())
	resultCfg.Merge(defaultConfig())
	cfg = resultCfg

	log.DebugF("cmd    config:\n%v", info.CmdConfig)
	log.DebugF("user   config(%s):\n%v", info.UserConfigPath, config.GetUser())
	log.DebugF("global config(%s):\n%v", info.globalConfigPath, config.GetGlobal())
	log.DebugF("final  config:\n%v", cfg)

	err = checkConfig(cfg)
	if err != nil {
		return
	}

	// 在工作区加载之后监听
	observerCmdInterrupt()

	return
}

func (w *LoadInfo) initInfo() *data.CodeError {
	home, err := utils.GetHomePath()
	if err != nil {
		return data.NewEmptyError().AppendDescF("get home path error:%v", err)
	}
	if len(w.WorkspacePath) == 0 {
		w.WorkspacePath = filepath.Join(home, workspaceName)
	}
	// 全局配置文件路径，兼容老版本，位置在用户目录下
	w.globalConfigPath = filepath.Join(home, configFileName)
	return nil
}
<|endoftext|>


<commit_before>package store

import (
	"fmt"

	"github.com/kusubooru/shimmie"
)

const (
	configKeyTitle          = "title"
	configKeyDescription    = "site_description"
	configKeyKeywords       = "site_keywords"
	configKeyAnalyticsID    = "google_analytics_id"
	configKeyAnalyticsIDOld = "ga_profile_id"
)

func (db *datastore) GetCommon() (*shimmie.Common, error) {
	keys := []string{
		configKeyTitle,
		configKeyDescription,
		configKeyKeywords,
		configKeyAnalyticsID,
		configKeyAnalyticsIDOld,
	}
	c, err := db.GetConfig(keys...)
	if err != nil {
		return nil, err
	}
	conf := shimmie.Common{
		Title:       c[configKeyTitle],
		Description: c[configKeyDescription],
		Keywords:    c[configKeyKeywords],
		AnalyticsID: c[configKeyAnalyticsID],
	}
	if conf.AnalyticsID == "" {
		conf.AnalyticsID = c[configKeyAnalyticsIDOld]
	}
	return &conf, nil
}

func (db *datastore) GetConfig(keys ...string) (map[string]string, error) {
	query := fmt.Sprintf(configGetQuery)
	if len(keys) != 0 {
		query = fmt.Sprintf("%vWHERE\n", query)
		for _, k := range keys {
			query = fmt.Sprintf("%v  name = '%v' OR\n", query, k)
		}
	}
	query = query[:len(query)-4] // chomp last ' OR\n'

	rows, err := db.Query(query)
	if err != nil {
		return nil, err
	}
	defer func() {
		if cerr := rows.Close(); cerr != nil && err == nil {
			err = cerr
		}
	}()

	var (
		config = struct {
			Name  string
			Value string
		}{}
		m = map[string]string{}
	)
	for rows.Next() {
		err := rows.Scan(&config.Name, &config.Value)
		if err != nil {
			return nil, err
		}
		m[config.Name] = config.Value
	}
	return m, nil
}

const configGetQuery = `
SELECT *
FROM config
`
<commit_msg>Change from Sprintf to Sprint<commit_after>package store

import (
	"fmt"

	"github.com/kusubooru/shimmie"
)

const (
	configKeyTitle          = "title"
	configKeyDescription    = "site_description"
	configKeyKeywords       = "site_keywords"
	configKeyAnalyticsID    = "google_analytics_id"
	configKeyAnalyticsIDOld = "ga_profile_id"
)

func (db *datastore) GetCommon() (*shimmie.Common, error) {
	keys := []string{
		configKeyTitle,
		configKeyDescription,
		configKeyKeywords,
		configKeyAnalyticsID,
		configKeyAnalyticsIDOld,
	}
	c, err := db.GetConfig(keys...)
	if err != nil {
		return nil, err
	}
	conf := shimmie.Common{
		Title:       c[configKeyTitle],
		Description: c[configKeyDescription],
		Keywords:    c[configKeyKeywords],
		AnalyticsID: c[configKeyAnalyticsID],
	}
	if conf.AnalyticsID == "" {
		conf.AnalyticsID = c[configKeyAnalyticsIDOld]
	}
	return &conf, nil
}

func (db *datastore) GetConfig(keys ...string) (map[string]string, error) {
	query := fmt.Sprint(configGetQuery)
	if len(keys) != 0 {
		query = fmt.Sprintf("%vWHERE\n", query)
		for _, k := range keys {
			query = fmt.Sprintf("%v  name = '%v' OR\n", query, k)
		}
	}
	query = query[:len(query)-4] // chomp last ' OR\n'

	rows, err := db.Query(query)
	if err != nil {
		return nil, err
	}
	defer func() {
		if cerr := rows.Close(); cerr != nil && err == nil {
			err = cerr
		}
	}()

	var (
		config = struct {
			Name  string
			Value string
		}{}
		m = map[string]string{}
	)
	for rows.Next() {
		err := rows.Scan(&config.Name, &config.Value)
		if err != nil {
			return nil, err
		}
		m[config.Name] = config.Value
	}
	return m, nil
}

const configGetQuery = `
SELECT *
FROM config
`
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

var httpTransport *http.Transport

func init() {
	httpTransport = new(http.Transport)
	httpTransport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
}

// Returns $GOPATH/src/k8s.io/...
func k8s(parts ...string) string {
	p := []string{os.Getenv("GOPATH"), "src", "k8s.io"}
	for _, a := range parts {
		p = append(p, a)
	}
	return filepath.Join(p...)
}

// append(errs, err) if err != nil
func appendError(errs []error, err error) []error {
	if err != nil {
		return append(errs, err)
	}
	return errs
}

// Returns $HOME/part/part/part
func home(parts ...string) string {
	p := []string{os.Getenv("HOME")}
	for _, a := range parts {
		p = append(p, a)
	}
	return filepath.Join(p...)
}

// export PATH=path:$PATH
func insertPath(path string) error {
	return os.Setenv("PATH", fmt.Sprintf("%v:%v", path, os.Getenv("PATH")))
}

// Essentially curl url | writer
func httpRead(url string, writer io.Writer) error {
	log.Printf("curl %s", url)
	c := &http.Client{Transport: httpTransport}
	r, err := c.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if r.StatusCode >= 400 {
		return fmt.Errorf("%v returned %d", url, r.StatusCode)
	}
	_, err = io.Copy(writer, r.Body)
	if err != nil {
		return err
	}
	return nil
}

// return f(), adding junit xml testcase result for name
func xmlWrap(name string, f func() error) error {
	alreadyInterrupted := interrupted
	start := time.Now()
	err := f()
	duration := time.Since(start)
	c := testCase{
		Name:      name,
		ClassName: "e2e.go",
		Time:      duration.Seconds(),
	}
	if err == nil && !alreadyInterrupted && interrupted {
		err = fmt.Errorf("kubetest interrupted during step %s", name)
	}
	if err != nil {
		if !alreadyInterrupted {
			c.Failure = err.Error()
		} else {
			c.Skipped = err.Error()
		}
		suite.Failures++
	}

	suite.Cases = append(suite.Cases, c)
	suite.Tests++
	return err
}

// return cmd.Wait() and/or timing out.
func finishRunning(cmd *exec.Cmd) error {
	stepName := strings.Join(cmd.Args, " ")
	if terminated {
		return fmt.Errorf("skipped %s (kubetest is terminated)", stepName)
	}
	if cmd.Stdout == nil && verbose {
		cmd.Stdout = os.Stdout
	}
	if cmd.Stderr == nil && verbose {
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("error starting %v: %v", stepName, err)
	}

	finished := make(chan error)

	sigChannel := make(chan os.Signal, 1)
	signal.Notify(sigChannel, os.Interrupt)

	go func() {
		finished <- cmd.Wait()
	}()

	for {
		select {
		case <-sigChannel:
			log.Printf("Killing %v(%v) after receiving signal", stepName, -cmd.Process.Pid)
			if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil {
				log.Printf("Failed to kill %v: %v", stepName, err)
			}
		case <-terminate.C:
			terminated = true
			terminate.Reset(time.Duration(0)) // Kill subsequent processes immediately.
			if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil {
				log.Printf("Failed to kill %v: %v", stepName, err)
			}
			if err := cmd.Process.Kill(); err != nil {
				log.Printf("Failed to terminate %s (terminated 15m after interrupt): %v", stepName, err)
			}
		case <-interrupt.C:
			interrupted = true
			log.Printf("Interrupt after %s timeout during %s. Will terminate in another 15m", timeout, stepName)
			terminate.Reset(15 * time.Minute)
			if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGINT); err != nil {
				log.Printf("Failed to interrupt %s. Will terminate immediately: %v", stepName, err)
				syscall.Kill(-cmd.Process.Pid, syscall.SIGTERM)
				cmd.Process.Kill()
			}
		case err := <-finished:
			if err != nil {
				var suffix string
				if terminated {
					suffix = " (terminated)"
				} else if interrupted {
					suffix = " (interrupted)"
				}
				return fmt.Errorf("error during %s%s: %v", stepName, suffix, err)
			}
			return err
		}
	}
}

// return exec.Command(cmd, args...) while calling .StdinPipe().WriteString(input)
func inputCommand(input, cmd string, args ...string) (*exec.Cmd, error) {
	c := exec.Command(cmd, args...)
	w, e := c.StdinPipe()
	if e != nil {
		return nil, e
	}
	go func() {
		if _, e = io.WriteString(w, input); e != nil {
			log.Printf("Failed to write all %d chars to %s: %v", len(input), cmd, e)
		}
		if e = w.Close(); e != nil {
			log.Printf("Failed to close stdin for %s: %v", cmd, e)
		}
	}()
	return c, nil
}

// return cmd.Output(), potentially timing out in the process.
func output(cmd *exec.Cmd) ([]byte, error) {
	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	err := finishRunning(cmd)
	return stdout.Bytes(), err

}

// gs://foo and bar becomes gs://foo/bar
func joinURL(urlPath, path string) (string, error) {
	u, err := url.Parse(urlPath)
	if err != nil {
		return "", err
	}
	u.Path = filepath.Join(u.Path, path)
	return u.String(), nil
}

// Chdir() to dir and return a function to cd back to Getwd()
func pushd(dir string) (func() error, error) {
	old, err := os.Getwd()
	if err != nil {
		return nil, fmt.Errorf("failed to os.Getwd(): %v", err)
	}
	if err = os.Chdir(dir); err != nil {
		return nil, err
	}
	return func() error {
		return os.Chdir(old)
	}, nil
}

// Push env=value and return a function that resets env
func pushEnv(env, value string) (func() error, error) {
	prev, present := os.LookupEnv(env)
	if err := os.Setenv(env, value); err != nil {
		return nil, fmt.Errorf("could not set %s: %v", env, err)
	}
	return func() error {
		if present {
			return os.Setenv(env, prev)
		}
		return os.Unsetenv(env)
	}, nil
}

// Option that was an ENV that is now a --flag
type migratedOption struct {
	env      string  // env associated with --flag
	option   *string // Value of --flag
	name     string  // --flag name
	skipPush bool    // Push option to env if false
}

// Read value from ENV if --flag unset, optionally pushing to ENV
func migrateOptions(m []migratedOption) error {
	for _, s := range m {
		if *s.option == "" {
			// Jobs may not be using --foo instead of FOO just yet, so ease the transition
			// TODO(fejta): require --foo instead of FOO
			v := os.Getenv(s.env) // expected Getenv
			if v != "" {
				// Tell people to use --foo=blah instead of FOO=blah
				log.Printf("Please use kubetest %s=%s (instead of deprecated %s=%s)", s.name, v, s.env, v)
				*s.option = v
			}
		}
		if s.skipPush {
			continue
		}
		// Script called by kubetest may expect these values to be set, so set them
		// TODO(fejta): refactor the scripts below kubetest to use explicit config
		if *s.option == "" {
			continue
		}
		if err := os.Setenv(s.env, *s.option); err != nil {
			return fmt.Errorf("could not set %s=%s: %v", s.env, *s.option, err)
		}
	}
	return nil
}

func appendField(fields []string, flag, prefix string) []string {
	fields, cur, _ := extractField(fields, flag)
	if len(cur) == 0 {
		cur = prefix
	} else {
		cur = cur + "-" + prefix
	}
	return append(fields, flag+"="+cur)
}

func setFieldDefault(fields []string, flag, val string) []string {
	fields, cur, present := extractField(fields, flag)
	if !present {
		cur = val
	}
	return append(fields, flag+"="+cur)
}

// extractField("--a=this --b=that --c=other", "--b") returns [--a=this, --c=other"], "that"
func extractField(fields []string, target string) ([]string, string, bool) {
	f := []string{}
	prefix := target + "="
	consumeNext := false
	done := false
	r := ""
	for _, field := range fields {
		switch {
		case done:
			f = append(f, field)
		case consumeNext:
			r = field
			done = true
		case field == target:
			consumeNext = true
		case strings.HasPrefix(field, prefix):
			r = strings.SplitN(field, "=", 2)[1]
			done = true
		default:
			f = append(f, field)
		}
	}
	return f, r, done
}

// execError returns a string format of err including stderr if the
// err is an ExitError, useful for errors from e.g. exec.Cmd.Output().
func execError(err error) string {
	if ee, ok := err.(*exec.ExitError); ok {
		return fmt.Sprintf("%v (output: %q)", err, string(ee.Stderr))
	}
	return err.Error()
}
<commit_msg>Add finishRunningParallel function<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
	"time"
)

var httpTransport *http.Transport

func init() {
	httpTransport = new(http.Transport)
	httpTransport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
}

// Returns $GOPATH/src/k8s.io/...
func k8s(parts ...string) string {
	p := []string{os.Getenv("GOPATH"), "src", "k8s.io"}
	for _, a := range parts {
		p = append(p, a)
	}
	return filepath.Join(p...)
}

// append(errs, err) if err != nil
func appendError(errs []error, err error) []error {
	if err != nil {
		return append(errs, err)
	}
	return errs
}

// Returns $HOME/part/part/part
func home(parts ...string) string {
	p := []string{os.Getenv("HOME")}
	for _, a := range parts {
		p = append(p, a)
	}
	return filepath.Join(p...)
}

// export PATH=path:$PATH
func insertPath(path string) error {
	return os.Setenv("PATH", fmt.Sprintf("%v:%v", path, os.Getenv("PATH")))
}

// Essentially curl url | writer
func httpRead(url string, writer io.Writer) error {
	log.Printf("curl %s", url)
	c := &http.Client{Transport: httpTransport}
	r, err := c.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if r.StatusCode >= 400 {
		return fmt.Errorf("%v returned %d", url, r.StatusCode)
	}
	_, err = io.Copy(writer, r.Body)
	if err != nil {
		return err
	}
	return nil
}

// return f(), adding junit xml testcase result for name
func xmlWrap(name string, f func() error) error {
	alreadyInterrupted := interrupted
	start := time.Now()
	err := f()
	duration := time.Since(start)
	c := testCase{
		Name:      name,
		ClassName: "e2e.go",
		Time:      duration.Seconds(),
	}
	if err == nil && !alreadyInterrupted && interrupted {
		err = fmt.Errorf("kubetest interrupted during step %s", name)
	}
	if err != nil {
		if !alreadyInterrupted {
			c.Failure = err.Error()
		} else {
			c.Skipped = err.Error()
		}
		suite.Failures++
	}

	suite.Cases = append(suite.Cases, c)
	suite.Tests++
	return err
}

// return cmd.Wait() and/or timing out.
func finishRunning(cmd *exec.Cmd) error {
	stepName := strings.Join(cmd.Args, " ")
	if terminated {
		return fmt.Errorf("skipped %s (kubetest is terminated)", stepName)
	}
	if cmd.Stdout == nil && verbose {
		cmd.Stdout = os.Stdout
	}
	if cmd.Stderr == nil && verbose {
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("error starting %v: %v", stepName, err)
	}

	finished := make(chan error)

	sigChannel := make(chan os.Signal, 1)
	signal.Notify(sigChannel, os.Interrupt)

	go func() {
		finished <- cmd.Wait()
	}()

	for {
		select {
		case <-sigChannel:
			log.Printf("Killing %v(%v) after receiving signal", stepName, -cmd.Process.Pid)
			if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil {
				log.Printf("Failed to kill %v: %v", stepName, err)
			}
		case <-terminate.C:
			terminated = true
			terminate.Reset(time.Duration(0)) // Kill subsequent processes immediately.
			if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil {
				log.Printf("Failed to kill %v: %v", stepName, err)
			}
			if err := cmd.Process.Kill(); err != nil {
				log.Printf("Failed to terminate %s (terminated 15m after interrupt): %v", stepName, err)
			}
		case <-interrupt.C:
			interrupted = true
			log.Printf("Interrupt after %s timeout during %s. Will terminate in another 15m", timeout, stepName)
			terminate.Reset(15 * time.Minute)
			if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGINT); err != nil {
				log.Printf("Failed to interrupt %s. Will terminate immediately: %v", stepName, err)
				syscall.Kill(-cmd.Process.Pid, syscall.SIGTERM)
				cmd.Process.Kill()
			}
		case err := <-finished:
			if err != nil {
				var suffix string
				if terminated {
					suffix = " (terminated)"
				} else if interrupted {
					suffix = " (interrupted)"
				}
				return fmt.Errorf("error during %s%s: %v", stepName, suffix, err)
			}
			return err
		}
	}
}

type cmdExecResult struct {
	stepName string
	output   string
	execTime time.Duration
	err      error
}

// execute a given command and send output and error via channel
func executeParallelCommand(cmd *exec.Cmd, wg *sync.WaitGroup, resChan chan cmdExecResult) {
	stepName := strings.Join(cmd.Args, " ")
	stdout := bytes.Buffer{}
	cmd.Stdout = &stdout
	cmd.Stderr = &stdout

	start := time.Now()
	log.Printf("Running: %v in parallel", stepName)
	defer wg.Done()

	if terminated {
		resChan <- cmdExecResult{stepName: stepName, output: stdout.String(), execTime: time.Since(start), err: fmt.Errorf("skipped %s (kubetest is terminated)", stepName)}
		return
	}

	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	if err := cmd.Start(); err != nil {
		resChan <- cmdExecResult{stepName: stepName, output: stdout.String(), execTime: time.Since(start), err: fmt.Errorf("error starting %v: %v", stepName, err)}
		return
	}

	finished := make(chan error)
	go func() {
		finished <- cmd.Wait()
	}()

	for {
		select {
		case err := <-finished:
			if err != nil {
				var suffix string
				if terminated {
					suffix = " (terminated)"
				} else if interrupted {
					suffix = " (interrupted)"
				}
				err = fmt.Errorf("error during %s%s: %v", stepName, suffix, err)
			}
			resChan <- cmdExecResult{stepName: stepName, output: stdout.String(), execTime: time.Since(start), err: err}
			return
		}
	}
}

// execute multiple commands in parallel
func finishRunningParallel(cmds ...*exec.Cmd) error {
	var wg sync.WaitGroup
	resultChan := make(chan cmdExecResult, len(cmds))

	for _, cmd := range cmds {
		wg.Add(1)
		go executeParallelCommand(cmd, &wg, resultChan)
	}

	go func() {
		wg.Wait()
		close(resultChan)
	}()

	cmdFailed := false
	for {
		select {
		case <-terminate.C:
			terminated = true
			terminate.Reset(time.Duration(0)) // Kill subsequent processes immediately.
			for _, cmd := range cmds {
				syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
				if err := cmd.Process.Kill(); err != nil {
					log.Printf("Failed to terminate %s (terminated 15m after interrupt): %v", strings.Join(cmd.Args, " "), err)
				}
			}
		case <-interrupt.C:
			interrupted = true
			terminate.Reset(15 * time.Minute)
			for _, cmd := range cmds {
				log.Printf("Interrupt after %s timeout during %s. Will terminate in another 15m", timeout, strings.Join(cmd.Args, " "))
				if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGINT); err != nil {
					log.Printf("Failed to interrupt %s. Will terminate immediately: %v", strings.Join(cmd.Args, " "), err)
					syscall.Kill(-cmd.Process.Pid, syscall.SIGTERM)
					cmd.Process.Kill()
				}
			}
		case result, ok := <-resultChan:
			if !ok {
				if cmdFailed {
					return fmt.Errorf("one or more commands failed")
				}
				return nil
			}
			log.Print(result.output)
			if result.err != nil {
				cmdFailed = true
			}
			log.Printf("Step '%s' finished in %s", result.stepName, result.execTime)
		}
	}
}

// return exec.Command(cmd, args...) while calling .StdinPipe().WriteString(input)
func inputCommand(input, cmd string, args ...string) (*exec.Cmd, error) {
	c := exec.Command(cmd, args...)
	w, e := c.StdinPipe()
	if e != nil {
		return nil, e
	}
	go func() {
		if _, e = io.WriteString(w, input); e != nil {
			log.Printf("Failed to write all %d chars to %s: %v", len(input), cmd, e)
		}
		if e = w.Close(); e != nil {
			log.Printf("Failed to close stdin for %s: %v", cmd, e)
		}
	}()
	return c, nil
}

// return cmd.Output(), potentially timing out in the process.
func output(cmd *exec.Cmd) ([]byte, error) {
	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	err := finishRunning(cmd)
	return stdout.Bytes(), err

}

// gs://foo and bar becomes gs://foo/bar
func joinURL(urlPath, path string) (string, error) {
	u, err := url.Parse(urlPath)
	if err != nil {
		return "", err
	}
	u.Path = filepath.Join(u.Path, path)
	return u.String(), nil
}

// Chdir() to dir and return a function to cd back to Getwd()
func pushd(dir string) (func() error, error) {
	old, err := os.Getwd()
	if err != nil {
		return nil, fmt.Errorf("failed to os.Getwd(): %v", err)
	}
	if err = os.Chdir(dir); err != nil {
		return nil, err
	}
	return func() error {
		return os.Chdir(old)
	}, nil
}

// Push env=value and return a function that resets env
func pushEnv(env, value string) (func() error, error) {
	prev, present := os.LookupEnv(env)
	if err := os.Setenv(env, value); err != nil {
		return nil, fmt.Errorf("could not set %s: %v", env, err)
	}
	return func() error {
		if present {
			return os.Setenv(env, prev)
		}
		return os.Unsetenv(env)
	}, nil
}

// Option that was an ENV that is now a --flag
type migratedOption struct {
	env      string  // env associated with --flag
	option   *string // Value of --flag
	name     string  // --flag name
	skipPush bool    // Push option to env if false
}

// Read value from ENV if --flag unset, optionally pushing to ENV
func migrateOptions(m []migratedOption) error {
	for _, s := range m {
		if *s.option == "" {
			// Jobs may not be using --foo instead of FOO just yet, so ease the transition
			// TODO(fejta): require --foo instead of FOO
			v := os.Getenv(s.env) // expected Getenv
			if v != "" {
				// Tell people to use --foo=blah instead of FOO=blah
				log.Printf("Please use kubetest %s=%s (instead of deprecated %s=%s)", s.name, v, s.env, v)
				*s.option = v
			}
		}
		if s.skipPush {
			continue
		}
		// Script called by kubetest may expect these values to be set, so set them
		// TODO(fejta): refactor the scripts below kubetest to use explicit config
		if *s.option == "" {
			continue
		}
		if err := os.Setenv(s.env, *s.option); err != nil {
			return fmt.Errorf("could not set %s=%s: %v", s.env, *s.option, err)
		}
	}
	return nil
}

func appendField(fields []string, flag, prefix string) []string {
	fields, cur, _ := extractField(fields, flag)
	if len(cur) == 0 {
		cur = prefix
	} else {
		cur = cur + "-" + prefix
	}
	return append(fields, flag+"="+cur)
}

func setFieldDefault(fields []string, flag, val string) []string {
	fields, cur, present := extractField(fields, flag)
	if !present {
		cur = val
	}
	return append(fields, flag+"="+cur)
}

// extractField("--a=this --b=that --c=other", "--b") returns [--a=this, --c=other"], "that"
func extractField(fields []string, target string) ([]string, string, bool) {
	f := []string{}
	prefix := target + "="
	consumeNext := false
	done := false
	r := ""
	for _, field := range fields {
		switch {
		case done:
			f = append(f, field)
		case consumeNext:
			r = field
			done = true
		case field == target:
			consumeNext = true
		case strings.HasPrefix(field, prefix):
			r = strings.SplitN(field, "=", 2)[1]
			done = true
		default:
			f = append(f, field)
		}
	}
	return f, r, done
}

// execError returns a string format of err including stderr if the
// err is an ExitError, useful for errors from e.g. exec.Cmd.Output().
func execError(err error) string {
	if ee, ok := err.(*exec.ExitError); ok {
		return fmt.Sprintf("%v (output: %q)", err, string(ee.Stderr))
	}
	return err.Error()
}
<|endoftext|>


<commit_before>package stun

import (
	"encoding/base64"
	"encoding/hex"
	"net"
	"testing"
	//"encoding/binary"

	"encoding/binary"
)

func TestMessage_AddSoftware(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	v := "Client v0.0.1"
	m.AddSoftware(v)
	m.WriteHeader()

	m2 := AcquireMessage()
	defer ReleaseMessage(m2)
	if err := m2.Get(m.buf.B); err != nil {
		t.Error(err)
	}
	vRead := m.GetSoftware()
	if vRead != v {
		t.Errorf("Expected %s, got %s.", v, vRead)
	}
}

func TestMessage_AddSoftwareBytes(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	v := "Client v0.0.1"
	m.AddSoftwareBytes([]byte(v))
	m.WriteHeader()

	m2 := AcquireMessage()
	defer ReleaseMessage(m2)
	if err := m2.Get(m.buf.B); err != nil {
		t.Error(err)
	}
	vRead := m.GetSoftware()
	if vRead != v {
		t.Errorf("Expected %s, got %s.", v, vRead)
	}
}

func TestMessage_GetSoftware(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)

	v := m.GetSoftware()
	if v != "" {
		t.Errorf("%s should be blank.", v)
	}
	vByte := m.GetSoftwareBytes()
	if vByte != nil {
		t.Errorf("%s should be nil.", vByte)
	}
}

func BenchmarkMessage_AddXORMappedAddress(b *testing.B) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	b.ReportAllocs()
	ip := net.ParseIP("192.168.1.32")
	for i := 0; i < b.N; i++ {
		m.AddXORMappedAddress(ip, 3654)
		m.Reset()
	}
}

func BenchmarkMessage_GetXORMappedAddress(b *testing.B) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	b.ReportAllocs()
	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er")
	if err != nil {
		b.Error(err)
	}
	copy(m.TransactionID[:], transactionID)
	addrValue, err := hex.DecodeString("00019cd5f49f38ae")
	if err != nil {
		b.Error(err)
	}
	for i := 0; i < b.N; i++ {
		m.Add(AttrXORMappedAddress, addrValue)
		m.GetXORMappedAddress()
		m.Reset()
	}
}

func TestMessage_GetXORMappedAddress(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er")
	if err != nil {
		t.Error(err)
	}
	copy(m.TransactionID[:], transactionID)
	addrValue, err := hex.DecodeString("00019cd5f49f38ae")
	if err != nil {
		t.Error(err)
	}
	m.Add(AttrXORMappedAddress, addrValue)
	ip, port, err := m.GetXORMappedAddress()
	if err != nil {
		t.Error(err)
	}
	if !ip.Equal(net.ParseIP("213.141.156.236")) {
		t.Error("bad ip", ip, "!=", "213.141.156.236")
	}
	if port != 48583 {
		t.Error("bad port", port, "!=", 48583)
	}
}

func TestMessage_GetXORMappedAddressBad(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er")
	if err != nil {
		t.Error(err)
	}
	copy(m.TransactionID[:], transactionID)
	expectedIP := net.ParseIP("213.141.156.236")
	expectedPort := 21254

	_, _, err = m.GetXORMappedAddress()
	if err == nil {
		t.Fatal(err, "should be nil")
	}

	m.AddXORMappedAddress(expectedIP, expectedPort)
	m.WriteHeader()

	mRes := AcquireMessage()
	defer ReleaseMessage(mRes)
	binary.BigEndian.PutUint16(m.buf.B[20+4:20+4+2], 0x21)
	if err := mRes.Get(m.buf.B); err != nil {
		t.Fatal(err)
	}
	_, _, err = m.GetXORMappedAddress()
	if err == nil {
		t.Fatal(err, "should not be nil")
	}
}

func TestMessage_AddXORMappedAddress(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er")
	if err != nil {
		t.Error(err)
	}
	copy(m.TransactionID[:], transactionID)
	expectedIP := net.ParseIP("213.141.156.236")
	expectedPort := 21254
	m.AddXORMappedAddress(expectedIP, expectedPort)
	m.WriteHeader()

	mRes := AcquireMessage()
	defer ReleaseMessage(mRes)
	if err := mRes.Get(m.buf.B); err != nil {
		t.Fatal(err)
	}
	ip, port, err := m.GetXORMappedAddress()
	if err != nil {
		t.Fatal(err)
	}
	if !ip.Equal(expectedIP) {
		t.Error("bad ip", ip, "!=", expectedIP)
	}
	if port != expectedPort {
		t.Error("bad port", port, "!=", expectedPort)
	}
}
<commit_msg>attr: improve coverage<commit_after>package stun

import (
	"encoding/base64"
	"encoding/hex"
	"net"
	"testing"
	//"encoding/binary"

	"encoding/binary"
	"strings"
)

func TestMessage_AddSoftware(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	v := "Client v0.0.1"
	m.AddSoftware(v)
	m.WriteHeader()

	m2 := AcquireMessage()
	defer ReleaseMessage(m2)
	if err := m2.Get(m.buf.B); err != nil {
		t.Error(err)
	}
	vRead := m.GetSoftware()
	if vRead != v {
		t.Errorf("Expected %s, got %s.", v, vRead)
	}

	sAttr := m.Attributes.Get(AttrSoftware)
	s := sAttr.String()
	if !strings.HasPrefix(s, "SOFTWARE:") {
		t.Error("bad string representation", s)
	}
}

func TestMessage_AddSoftwareBytes(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	v := "Client v0.0.1"
	m.AddSoftwareBytes([]byte(v))
	m.WriteHeader()

	m2 := AcquireMessage()
	defer ReleaseMessage(m2)
	if err := m2.Get(m.buf.B); err != nil {
		t.Error(err)
	}
	vRead := m.GetSoftware()
	if vRead != v {
		t.Errorf("Expected %s, got %s.", v, vRead)
	}
}

func TestMessage_GetSoftware(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)

	v := m.GetSoftware()
	if v != "" {
		t.Errorf("%s should be blank.", v)
	}
	vByte := m.GetSoftwareBytes()
	if vByte != nil {
		t.Errorf("%s should be nil.", vByte)
	}
}

func BenchmarkMessage_AddXORMappedAddress(b *testing.B) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	b.ReportAllocs()
	ip := net.ParseIP("192.168.1.32")
	for i := 0; i < b.N; i++ {
		m.AddXORMappedAddress(ip, 3654)
		m.Reset()
	}
}

func BenchmarkMessage_GetXORMappedAddress(b *testing.B) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	b.ReportAllocs()
	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er")
	if err != nil {
		b.Error(err)
	}
	copy(m.TransactionID[:], transactionID)
	addrValue, err := hex.DecodeString("00019cd5f49f38ae")
	if err != nil {
		b.Error(err)
	}
	for i := 0; i < b.N; i++ {
		m.Add(AttrXORMappedAddress, addrValue)
		m.GetXORMappedAddress()
		m.Reset()
	}
}

func TestMessage_GetXORMappedAddress(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er")
	if err != nil {
		t.Error(err)
	}
	copy(m.TransactionID[:], transactionID)
	addrValue, err := hex.DecodeString("00019cd5f49f38ae")
	if err != nil {
		t.Error(err)
	}
	m.Add(AttrXORMappedAddress, addrValue)
	ip, port, err := m.GetXORMappedAddress()
	if err != nil {
		t.Error(err)
	}
	if !ip.Equal(net.ParseIP("213.141.156.236")) {
		t.Error("bad ip", ip, "!=", "213.141.156.236")
	}
	if port != 48583 {
		t.Error("bad port", port, "!=", 48583)
	}
}

func TestMessage_GetXORMappedAddressBad(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er")
	if err != nil {
		t.Error(err)
	}
	copy(m.TransactionID[:], transactionID)
	expectedIP := net.ParseIP("213.141.156.236")
	expectedPort := 21254

	_, _, err = m.GetXORMappedAddress()
	if err == nil {
		t.Fatal(err, "should be nil")
	}

	m.AddXORMappedAddress(expectedIP, expectedPort)
	m.WriteHeader()

	mRes := AcquireMessage()
	defer ReleaseMessage(mRes)
	binary.BigEndian.PutUint16(m.buf.B[20+4:20+4+2], 0x21)
	if err := mRes.Get(m.buf.B); err != nil {
		t.Fatal(err)
	}
	_, _, err = m.GetXORMappedAddress()
	if err == nil {
		t.Fatal(err, "should not be nil")
	}
}

func TestMessage_AddXORMappedAddress(t *testing.T) {
	m := AcquireMessage()
	defer ReleaseMessage(m)
	transactionID, err := base64.StdEncoding.DecodeString("jxhBARZwX+rsC6er")
	if err != nil {
		t.Error(err)
	}
	copy(m.TransactionID[:], transactionID)
	expectedIP := net.ParseIP("213.141.156.236")
	expectedPort := 21254
	m.AddXORMappedAddress(expectedIP, expectedPort)
	m.WriteHeader()

	mRes := AcquireMessage()
	defer ReleaseMessage(mRes)
	if err := mRes.Get(m.buf.B); err != nil {
		t.Fatal(err)
	}
	ip, port, err := m.GetXORMappedAddress()
	if err != nil {
		t.Fatal(err)
	}
	if !ip.Equal(expectedIP) {
		t.Error("bad ip", ip, "!=", expectedIP)
	}
	if port != expectedPort {
		t.Error("bad port", port, "!=", expectedPort)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"strconv"
	"unsafe"

	log "github.com/cihub/seelog"
	"github.com/stampzilla/stampzilla-go/nodes/basenode"
	"github.com/stampzilla/stampzilla-go/protocol"
)

/*
#cgo LDFLAGS: -ltelldus-core

#include <telldus-core.h>

extern void registerCallbacks();
extern void unregisterCallbacks();
extern int updateDevices();

*/
import "C"

var node *protocol.Node
var state *State = &State{make(map[string]*Device), make(map[string]*Sensor, 0)}
var serverConnection *basenode.Connection

func main() {
	// Load logger
	//logger, err := log.LoggerFromConfigAsFile("../logconfig.xml")
	//if err != nil {
	//panic(err)
	//}
	//log.ReplaceLogger(logger)

	//Get a config with the correct parameters
	config := basenode.NewConfig()
	basenode.SetConfig(config)

	// Load flags
	//var host string
	//var port string
	//flag.StringVar(&host, "host", "localhost", "Stampzilla server hostname")
	//flag.StringVar(&port, "port", "8282", "Stampzilla server port")
	flag.Parse()

	log.Info("Starting TELLDUS-events node")

	C.registerCallbacks()
	defer C.unregisterCallbacks()

	// Create new node description
	node = protocol.NewNode("telldus-events")
	node.SetState(state)

	// Describe available actions
	node.AddAction("set", "Set", []string{"Devices.Id"})
	node.AddAction("toggle", "Toggle", []string{"Devices.Id"})
	node.AddAction("dim", "Dim", []string{"Devices.Id", "value"})

	// Describe available layouts
	//node.AddLayout("1", "switch", "toggle", "Devices", []string{"on"}, "Switches")
	//node.AddLayout("2", "slider", "dim", "Devices", []string{"dim"}, "Dimmers")
	//node.AddLayout("3", "slider", "dim", "Devices", []string{"dim"}, "Specials")

	// Add devices
	cnt := C.updateDevices()
	log.Info("Updated devices (", cnt, " in total)")

	for _, dev := range state.Devices {
		node.AddElement(&protocol.Element{
			Type: protocol.ElementTypeToggle,
			Name: dev.Name,
			Command: &protocol.Command{
				Cmd:  "toggle",
				Args: []string{dev.Id},
			},
			Feedback: `Devices[` + dev.Id + `].State.On`,
		})
	}

	// Start the connection
	//go connection(host, port, node)

	serverConnection = basenode.Connect()
	go monitorState(serverConnection)

	// This worker recives all incomming commands
	go serverRecv(serverConnection)

	select {}
}

// WORKER that monitors the current connection state
func monitorState(connection *basenode.Connection) {
	for s := range connection.State {
		switch s {
		case basenode.ConnectionStateConnected:
			connection.Send <- node.Node()
		case basenode.ConnectionStateDisconnected:
		}
	}
}

// WORKER that recives all incomming commands
func serverRecv(connection *basenode.Connection) {
	for d := range connection.Receive {
		if err := processCommand(d); err != nil {
			log.Error(err)
		}
	}
}

func processCommand(cmd protocol.Command) error {
	var result C.int = C.TELLSTICK_ERROR_UNKNOWN
	var id C.int = 0

	i, err := strconv.Atoi(cmd.Args[0])
	if err != nil {
		return fmt.Errorf("Failed to decode arg[0] to int %s %s", err, cmd.Args[0])
	}

	id = C.int(i)

	switch cmd.Cmd {
	case "on":
		result = C.tdTurnOn(id)
	case "off":
		result = C.tdTurnOff(id)
	case "toggle":
		s := C.tdLastSentCommand(id, C.TELLSTICK_TURNON|C.TELLSTICK_TURNOFF|C.TELLSTICK_DIM)
		switch {
		case s&C.TELLSTICK_DIM != 0:
			var state *C.char = C.tdLastSentValue(id)
			log.Info("DIM: ", C.GoString(state))
			if C.GoString(state) == "0" {
				result = C.tdTurnOn(id)
			} else {
				result = C.tdTurnOff(id)
			}
			C.tdReleaseString(state)
		case s&C.TELLSTICK_TURNON != 0:
			result = C.tdTurnOff(id)
		case s&C.TELLSTICK_TURNOFF != 0:
			result = C.tdTurnOn(id)
		}
	}

	if result != C.TELLSTICK_SUCCESS {
		var errorString *C.char = C.tdGetErrorString(result)
		C.tdReleaseString(errorString)
		return errors.New(C.GoString(errorString))
	}

	return nil
}

//export newDevice
func newDevice(id int, name *C.char, methods, s int, value *C.char) {
	//log.Info(id, C.GoString(name))

	features := []string{}
	if methods&C.TELLSTICK_TURNON != 0 {
		features = append(features, "on")
	}
	if methods&C.TELLSTICK_TURNOFF != 0 {
		features = append(features, "off")
	}
	if methods&C.TELLSTICK_BELL != 0 {
		features = append(features, "bell")
	}
	if methods&C.TELLSTICK_TOGGLE != 0 {
		features = append(features, "toggle")
	}
	if methods&C.TELLSTICK_DIM != 0 {
		features = append(features, "dim")
	}
	if methods&C.TELLSTICK_EXECUTE != 0 {
		features = append(features, "execute")
	}
	if methods&C.TELLSTICK_UP != 0 {
		features = append(features, "up")
	}
	if methods&C.TELLSTICK_DOWN != 0 {
		features = append(features, "down")
	}
	if methods&C.TELLSTICK_STOP != 0 {
		features = append(features, "stop")
	}

	if s&C.TELLSTICK_TURNON != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: true, Dim: 100})
	}
	if s&C.TELLSTICK_TURNOFF != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: false})
	}
	if s&C.TELLSTICK_DIM != 0 {
		var currentState = C.GoString(value)
		level, _ := strconv.ParseUint(currentState, 10, 16)
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: level > 0, Dim: int(level)})
	}

}

//export sensorEvent
func sensorEvent(protocol, model *C.char, sensorId, dataType int, value *C.char) {
	log.Debugf("SensorEVENT %s,\t%s,\t%d -> ", C.GoString(protocol), C.GoString(model), sensorId)

	var s *Sensor
	if s = state.GetSensor(sensorId); s == nil {
		s = state.AddSensor(sensorId, "UNKNOWN")
	}

	if dataType == C.TELLSTICK_TEMPERATURE {
		t, _ := strconv.ParseFloat(C.GoString(value), 64)
		log.Debugf("Temperature:\t%s\n", t)
		s.Temp = t
	} else if dataType == C.TELLSTICK_HUMIDITY {
		h, _ := strconv.ParseFloat(C.GoString(value), 64)
		log.Debugf("Humidity:\t%s%%\n", h)
		s.Humidity = h
	}
}

//export deviceEvent
func deviceEvent(deviceId, method int, data *C.char, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceEVENT %d\t%d\t:%s\n", deviceId, method, C.GoString(data))
	device := state.GetDevice(strconv.Itoa(deviceId))
	if method&C.TELLSTICK_TURNON != 0 {
		device.State.On = true
		serverConnection.Send <- node.Node()
	}
	if method&C.TELLSTICK_TURNOFF != 0 {
		device.State.On = false
		serverConnection.Send <- node.Node()
	}
	if method&C.TELLSTICK_DIM != 0 {
		level, err := strconv.ParseUint(C.GoString(data), 10, 16)
		if err != nil {
			log.Error(err)
			return
		}
		if level == 0 {
			device.State.On = false
		}
		if level > 0 {
			device.State.On = true
		}
		device.State.Dim = int(level)
		serverConnection.Send <- node.Node()
	}
}

//export deviceChangeEvent
func deviceChangeEvent(deviceId, changeEvent, changeType, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceChangeEVENT %d\t%d\t%d\n", deviceId, changeEvent, changeType)
}

//export rawDeviceEvent
func rawDeviceEvent(data *C.char, controllerId, callbackId int, context unsafe.Pointer) {
	log.Debugf("rawDeviceEVENT (%d):%s\n", controllerId, C.GoString(data))
}
<commit_msg>send update to server on temp or humid change<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"strconv"
	"unsafe"

	log "github.com/cihub/seelog"
	"github.com/stampzilla/stampzilla-go/nodes/basenode"
	"github.com/stampzilla/stampzilla-go/protocol"
)

/*
#cgo LDFLAGS: -ltelldus-core

#include <telldus-core.h>

extern void registerCallbacks();
extern void unregisterCallbacks();
extern int updateDevices();

*/
import "C"

var node *protocol.Node
var state *State = &State{make(map[string]*Device), make(map[string]*Sensor, 0)}
var serverConnection *basenode.Connection

func main() {
	// Load logger
	//logger, err := log.LoggerFromConfigAsFile("../logconfig.xml")
	//if err != nil {
	//panic(err)
	//}
	//log.ReplaceLogger(logger)

	//Get a config with the correct parameters
	config := basenode.NewConfig()
	basenode.SetConfig(config)

	// Load flags
	//var host string
	//var port string
	//flag.StringVar(&host, "host", "localhost", "Stampzilla server hostname")
	//flag.StringVar(&port, "port", "8282", "Stampzilla server port")
	flag.Parse()

	log.Info("Starting TELLDUS-events node")

	C.registerCallbacks()
	defer C.unregisterCallbacks()

	// Create new node description
	node = protocol.NewNode("telldus-events")
	node.SetState(state)

	// Describe available actions
	node.AddAction("set", "Set", []string{"Devices.Id"})
	node.AddAction("toggle", "Toggle", []string{"Devices.Id"})
	node.AddAction("dim", "Dim", []string{"Devices.Id", "value"})

	// Describe available layouts
	//node.AddLayout("1", "switch", "toggle", "Devices", []string{"on"}, "Switches")
	//node.AddLayout("2", "slider", "dim", "Devices", []string{"dim"}, "Dimmers")
	//node.AddLayout("3", "slider", "dim", "Devices", []string{"dim"}, "Specials")

	// Add devices
	cnt := C.updateDevices()
	log.Info("Updated devices (", cnt, " in total)")

	for _, dev := range state.Devices {
		node.AddElement(&protocol.Element{
			Type: protocol.ElementTypeToggle,
			Name: dev.Name,
			Command: &protocol.Command{
				Cmd:  "toggle",
				Args: []string{dev.Id},
			},
			Feedback: `Devices[` + dev.Id + `].State.On`,
		})
	}

	// Start the connection
	//go connection(host, port, node)

	serverConnection = basenode.Connect()
	go monitorState(serverConnection)

	// This worker recives all incomming commands
	go serverRecv(serverConnection)

	select {}
}

// WORKER that monitors the current connection state
func monitorState(connection *basenode.Connection) {
	for s := range connection.State {
		switch s {
		case basenode.ConnectionStateConnected:
			connection.Send <- node.Node()
		case basenode.ConnectionStateDisconnected:
		}
	}
}

// WORKER that recives all incomming commands
func serverRecv(connection *basenode.Connection) {
	for d := range connection.Receive {
		if err := processCommand(d); err != nil {
			log.Error(err)
		}
	}
}

func processCommand(cmd protocol.Command) error {
	var result C.int = C.TELLSTICK_ERROR_UNKNOWN
	var id C.int = 0

	i, err := strconv.Atoi(cmd.Args[0])
	if err != nil {
		return fmt.Errorf("Failed to decode arg[0] to int %s %s", err, cmd.Args[0])
	}

	id = C.int(i)

	switch cmd.Cmd {
	case "on":
		result = C.tdTurnOn(id)
	case "off":
		result = C.tdTurnOff(id)
	case "toggle":
		s := C.tdLastSentCommand(id, C.TELLSTICK_TURNON|C.TELLSTICK_TURNOFF|C.TELLSTICK_DIM)
		switch {
		case s&C.TELLSTICK_DIM != 0:
			var state *C.char = C.tdLastSentValue(id)
			log.Info("DIM: ", C.GoString(state))
			if C.GoString(state) == "0" {
				result = C.tdTurnOn(id)
			} else {
				result = C.tdTurnOff(id)
			}
			C.tdReleaseString(state)
		case s&C.TELLSTICK_TURNON != 0:
			result = C.tdTurnOff(id)
		case s&C.TELLSTICK_TURNOFF != 0:
			result = C.tdTurnOn(id)
		}
	}

	if result != C.TELLSTICK_SUCCESS {
		var errorString *C.char = C.tdGetErrorString(result)
		C.tdReleaseString(errorString)
		return errors.New(C.GoString(errorString))
	}

	return nil
}

//export newDevice
func newDevice(id int, name *C.char, methods, s int, value *C.char) {
	//log.Info(id, C.GoString(name))

	features := []string{}
	if methods&C.TELLSTICK_TURNON != 0 {
		features = append(features, "on")
	}
	if methods&C.TELLSTICK_TURNOFF != 0 {
		features = append(features, "off")
	}
	if methods&C.TELLSTICK_BELL != 0 {
		features = append(features, "bell")
	}
	if methods&C.TELLSTICK_TOGGLE != 0 {
		features = append(features, "toggle")
	}
	if methods&C.TELLSTICK_DIM != 0 {
		features = append(features, "dim")
	}
	if methods&C.TELLSTICK_EXECUTE != 0 {
		features = append(features, "execute")
	}
	if methods&C.TELLSTICK_UP != 0 {
		features = append(features, "up")
	}
	if methods&C.TELLSTICK_DOWN != 0 {
		features = append(features, "down")
	}
	if methods&C.TELLSTICK_STOP != 0 {
		features = append(features, "stop")
	}

	if s&C.TELLSTICK_TURNON != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: true, Dim: 100})
	}
	if s&C.TELLSTICK_TURNOFF != 0 {
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: false})
	}
	if s&C.TELLSTICK_DIM != 0 {
		var currentState = C.GoString(value)
		level, _ := strconv.ParseUint(currentState, 10, 16)
		state.AddDevice(strconv.Itoa(id), C.GoString(name), features, DeviceState{On: level > 0, Dim: int(level)})
	}

}

//export sensorEvent
func sensorEvent(protocol, model *C.char, sensorId, dataType int, value *C.char) {
	log.Debugf("SensorEVENT %s,\t%s,\t%d -> ", C.GoString(protocol), C.GoString(model), sensorId)

	var s *Sensor
	if s = state.GetSensor(sensorId); s == nil {
		s = state.AddSensor(sensorId, "UNKNOWN")
	}

	if dataType == C.TELLSTICK_TEMPERATURE {
		t, _ := strconv.ParseFloat(C.GoString(value), 64)
		log.Debugf("Temperature:\t%s\n", t)
		s.Temp = t
		serverConnection.Send <- node.Node()
	} else if dataType == C.TELLSTICK_HUMIDITY {
		h, _ := strconv.ParseFloat(C.GoString(value), 64)
		log.Debugf("Humidity:\t%s%%\n", h)
		s.Humidity = h
		serverConnection.Send <- node.Node()
	}
}

//export deviceEvent
func deviceEvent(deviceId, method int, data *C.char, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceEVENT %d\t%d\t:%s\n", deviceId, method, C.GoString(data))
	device := state.GetDevice(strconv.Itoa(deviceId))
	if method&C.TELLSTICK_TURNON != 0 {
		device.State.On = true
		serverConnection.Send <- node.Node()
	}
	if method&C.TELLSTICK_TURNOFF != 0 {
		device.State.On = false
		serverConnection.Send <- node.Node()
	}
	if method&C.TELLSTICK_DIM != 0 {
		level, err := strconv.ParseUint(C.GoString(data), 10, 16)
		if err != nil {
			log.Error(err)
			return
		}
		if level == 0 {
			device.State.On = false
		}
		if level > 0 {
			device.State.On = true
		}
		device.State.Dim = int(level)
		serverConnection.Send <- node.Node()
	}
}

//export deviceChangeEvent
func deviceChangeEvent(deviceId, changeEvent, changeType, callbackId int, context unsafe.Pointer) {
	log.Debugf("DeviceChangeEVENT %d\t%d\t%d\n", deviceId, changeEvent, changeType)
}

//export rawDeviceEvent
func rawDeviceEvent(data *C.char, controllerId, callbackId int, context unsafe.Pointer) {
	log.Debugf("rawDeviceEVENT (%d):%s\n", controllerId, C.GoString(data))
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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.

//+build linux

package main

import (
	"fmt"
	"os"
)

var (
	cmdList = &Command{
		Name:    "list",
		Summary: "List containers",
		Usage:   "",
		Run:     runList,
	}
	flagNoLegend bool
)

func init() {
	commands = append(commands, cmdList)
	cmdList.Flags.BoolVar(&flagNoLegend, "no-legend", false, "suppress a legend with the list")
}

func runList(args []string) (exit int) {
	if !flagNoLegend {
		fmt.Printf("UUID                                 STATE\n")
	}

	if err := walkContainers(includeContainersDir|includeGarbageDir, func(c *container) {
		state := "active"
		if c.isDeleting {
			state = "deleting"
		} else if c.isExited {
			state = "inactive"
		}

		fmt.Printf("%s %s\n", c.uuid, state)
	}); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get container handles: %v\n", err)
		return 1
	}

	return 0
}
<commit_msg>list containers: use new container api<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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.

//+build linux

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/appc/spec/schema"
	common "github.com/coreos/rocket/common"
)

var (
	cmdList = &Command{
		Name:    "list",
		Summary: "List containers",
		Usage:   "",
		Run:     runList,
	}
	flagNoLegend bool
)

func init() {
	commands = append(commands, cmdList)
	cmdList.Flags.BoolVar(&flagNoLegend, "no-legend", false, "suppress a legend with the list")
}

func runList(args []string) (exit int) {
	if !flagNoLegend {
		fmt.Fprintf(out, "UUID\tACI\tSTATE\n")
	}

	if err := walkContainers(includeContainersDir|includeGarbageDir, func(c *container) {
		cdir := filepath.Join(containersDir(), c.uuid)
		manifFile, err := ioutil.ReadFile(common.ContainerManifestPath(cdir))

		m := schema.ContainerRuntimeManifest{}
		err = m.UnmarshalJSON(manifFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to load manifest: %v", err)
			return
		}

		if len(m.Apps) == 0 {
			fmt.Fprint(os.Stderr, "Container contains zero apps")
			return
		}

		state := "active"
		if c.isDeleting {
			state = "deleting"
		} else if c.isExited {
			state = "inactive"
		}

		fmt.Fprintf(out, "%s\t%s\t%s\n", c.uuid, m.Apps[0].Name.String(), state)
		for i := 1; i < len(m.Apps); i++ {
			fmt.Fprintf(out, "\t%s\n", m.Apps[i].Name.String())
		}
	}); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get container handles: %v\n", err)
		return 1
	}

	out.Flush()
	return 0
}
<|endoftext|>


<commit_before>package rpc

import (
	"bytes"
	crand "crypto/rand"
	"math/rand"
	"net/http"
	"os/exec"
	"testing"
	"time"

	client "github.com/tendermint/go-rpc/client"
	server "github.com/tendermint/go-rpc/server"
	types "github.com/tendermint/go-rpc/types"
	wire "github.com/tendermint/go-wire"
)

// Client and Server should work over tcp or unix sockets
const (
	tcpAddr = "tcp://0.0.0.0:46657"

	unixSocket = "/tmp/go-rpc.sock"
	unixAddr   = "unix:///tmp/go-rpc.sock"

	websocketEndpoint = "/websocket/endpoint"
)

// Define a type for results and register concrete versions
type Result interface{}

type ResultStatus struct {
	Value string
}

type ResultBytes struct {
	Value []byte
}

var _ = wire.RegisterInterface(
	struct{ Result }{},
	wire.ConcreteType{&ResultStatus{}, 0x1},
	wire.ConcreteType{&ResultBytes{}, 0x2},
)

// Define some routes
var Routes = map[string]*server.RPCFunc{
	"status": server.NewRPCFunc(StatusResult, "arg"),
	"bytes":  server.NewRPCFunc(BytesResult, "arg"),
}

// an rpc function
func StatusResult(v string) (Result, error) {
	return &ResultStatus{v}, nil
}

func BytesResult(v []byte) (Result, error) {
	return &ResultBytes{v}, nil
}

// launch unix and tcp servers
func init() {
	cmd := exec.Command("rm", "-f", unixSocket)
	err := cmd.Start()
	if err != nil {
		panic(err)
	}
	err = cmd.Wait()

	mux := http.NewServeMux()
	server.RegisterRPCFuncs(mux, Routes)
	wm := server.NewWebsocketManager(Routes, nil)
	mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
	go func() {
		_, err := server.StartHTTPServer(tcpAddr, mux)
		if err != nil {
			panic(err)
		}
	}()

	mux2 := http.NewServeMux()
	server.RegisterRPCFuncs(mux2, Routes)
	wm = server.NewWebsocketManager(Routes, nil)
	mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
	go func() {
		_, err := server.StartHTTPServer(unixAddr, mux2)
		if err != nil {
			panic(err)
		}
	}()

	// wait for servers to start
	time.Sleep(time.Second * 2)

}

func testURI(t *testing.T, cl *client.ClientURI) {
	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("status", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultStatus).Value
	if got != val {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}

func testJSONRPC(t *testing.T, cl *client.ClientJSONRPC) {
	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("status", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultStatus).Value
	if got != val {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}

func testWS(t *testing.T, cl *client.WSClient) {
	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	err := cl.WriteJSON(types.RPCRequest{
		JSONRPC: "2.0",
		ID:      "",
		Method:  "status",
		Params:  params,
	})
	if err != nil {
		t.Fatal(err)
	}

	msg := <-cl.ResultsCh
	result := new(Result)
	wire.ReadJSONPtr(result, msg, &err)
	if err != nil {
		t.Fatal(err)
	}
	got := (*result).(*ResultStatus).Value
	if got != val {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}

//-------------

func TestURI_TCP(t *testing.T) {
	cl := client.NewClientURI(tcpAddr)
	testURI(t, cl)
}

func TestURI_UNIX(t *testing.T) {
	cl := client.NewClientURI(unixAddr)
	testURI(t, cl)
}

func TestJSONRPC_TCP(t *testing.T) {
	cl := client.NewClientJSONRPC(tcpAddr)
	testJSONRPC(t, cl)
}

func TestJSONRPC_UNIX(t *testing.T) {
	cl := client.NewClientJSONRPC(unixAddr)
	testJSONRPC(t, cl)
}

func TestWS_TCP(t *testing.T) {
	cl := client.NewWSClient(tcpAddr, websocketEndpoint)
	_, err := cl.Start()
	if err != nil {
		t.Fatal(err)
	}
	testWS(t, cl)
}

func TestWS_UNIX(t *testing.T) {
	cl := client.NewWSClient(unixAddr, websocketEndpoint)
	_, err := cl.Start()
	if err != nil {
		t.Fatal(err)
	}
	testWS(t, cl)
}

func TestHexStringArg(t *testing.T) {
	cl := client.NewClientURI(tcpAddr)
	// should NOT be handled as hex
	val := "0xabc"
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("status", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultStatus).Value
	if got != val {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}

func TestQuotedStringArg(t *testing.T) {
	cl := client.NewClientURI(tcpAddr)
	// should NOT be unquoted
	val := "\"abc\""
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("status", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultStatus).Value
	if got != val {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}

func randBytes(t *testing.T) []byte {
	n := rand.Intn(10) + 2
	buf := make([]byte, n)
	_, err := crand.Read(buf)
	if err != nil {
		t.Fatal(err)
	}
	return bytes.Replace(buf, []byte("="), []byte{100}, -1)
}

func TestByteSliceViaJSONRPC(t *testing.T) {
	cl := client.NewClientJSONRPC(unixAddr)

	val := randBytes(t)
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("bytes", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultBytes).Value
	if bytes.Compare(got, val) != 0 {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}
<commit_msg>read from ErrorsCh also<commit_after>package rpc

import (
	"bytes"
	crand "crypto/rand"
	"math/rand"
	"net/http"
	"os/exec"
	"testing"
	"time"

	client "github.com/tendermint/go-rpc/client"
	server "github.com/tendermint/go-rpc/server"
	types "github.com/tendermint/go-rpc/types"
	wire "github.com/tendermint/go-wire"
)

// Client and Server should work over tcp or unix sockets
const (
	tcpAddr = "tcp://0.0.0.0:46657"

	unixSocket = "/tmp/go-rpc.sock"
	unixAddr   = "unix:///tmp/go-rpc.sock"

	websocketEndpoint = "/websocket/endpoint"
)

// Define a type for results and register concrete versions
type Result interface{}

type ResultStatus struct {
	Value string
}

type ResultBytes struct {
	Value []byte
}

var _ = wire.RegisterInterface(
	struct{ Result }{},
	wire.ConcreteType{&ResultStatus{}, 0x1},
	wire.ConcreteType{&ResultBytes{}, 0x2},
)

// Define some routes
var Routes = map[string]*server.RPCFunc{
	"status": server.NewRPCFunc(StatusResult, "arg"),
	"bytes":  server.NewRPCFunc(BytesResult, "arg"),
}

// an rpc function
func StatusResult(v string) (Result, error) {
	return &ResultStatus{v}, nil
}

func BytesResult(v []byte) (Result, error) {
	return &ResultBytes{v}, nil
}

// launch unix and tcp servers
func init() {
	cmd := exec.Command("rm", "-f", unixSocket)
	err := cmd.Start()
	if err != nil {
		panic(err)
	}
	err = cmd.Wait()

	mux := http.NewServeMux()
	server.RegisterRPCFuncs(mux, Routes)
	wm := server.NewWebsocketManager(Routes, nil)
	mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
	go func() {
		_, err := server.StartHTTPServer(tcpAddr, mux)
		if err != nil {
			panic(err)
		}
	}()

	mux2 := http.NewServeMux()
	server.RegisterRPCFuncs(mux2, Routes)
	wm = server.NewWebsocketManager(Routes, nil)
	mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
	go func() {
		_, err := server.StartHTTPServer(unixAddr, mux2)
		if err != nil {
			panic(err)
		}
	}()

	// wait for servers to start
	time.Sleep(time.Second * 2)

}

func testURI(t *testing.T, cl *client.ClientURI) {
	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("status", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultStatus).Value
	if got != val {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}

func testJSONRPC(t *testing.T, cl *client.ClientJSONRPC) {
	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("status", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultStatus).Value
	if got != val {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}

func testWS(t *testing.T, cl *client.WSClient) {
	val := "acbd"
	params := map[string]interface{}{
		"arg": val,
	}
	err := cl.WriteJSON(types.RPCRequest{
		JSONRPC: "2.0",
		ID:      "",
		Method:  "status",
		Params:  params,
	})
	if err != nil {
		t.Fatal(err)
	}

	select {
	case msg := <-cl.ResultsCh:
		result := new(Result)
		wire.ReadJSONPtr(result, msg, &err)
		if err != nil {
			t.Fatal(err)
		}
		got := (*result).(*ResultStatus).Value
		if got != val {
			t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
		}
	case err := <-cl.ErrorsCh:
		t.Fatal(err)
	}
}

//-------------

func TestURI_TCP(t *testing.T) {
	cl := client.NewClientURI(tcpAddr)
	testURI(t, cl)
}

func TestURI_UNIX(t *testing.T) {
	cl := client.NewClientURI(unixAddr)
	testURI(t, cl)
}

func TestJSONRPC_TCP(t *testing.T) {
	cl := client.NewClientJSONRPC(tcpAddr)
	testJSONRPC(t, cl)
}

func TestJSONRPC_UNIX(t *testing.T) {
	cl := client.NewClientJSONRPC(unixAddr)
	testJSONRPC(t, cl)
}

func TestWS_TCP(t *testing.T) {
	cl := client.NewWSClient(tcpAddr, websocketEndpoint)
	_, err := cl.Start()
	if err != nil {
		t.Fatal(err)
	}
	testWS(t, cl)
}

func TestWS_UNIX(t *testing.T) {
	cl := client.NewWSClient(unixAddr, websocketEndpoint)
	_, err := cl.Start()
	if err != nil {
		t.Fatal(err)
	}
	testWS(t, cl)
}

func TestHexStringArg(t *testing.T) {
	cl := client.NewClientURI(tcpAddr)
	// should NOT be handled as hex
	val := "0xabc"
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("status", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultStatus).Value
	if got != val {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}

func TestQuotedStringArg(t *testing.T) {
	cl := client.NewClientURI(tcpAddr)
	// should NOT be unquoted
	val := "\"abc\""
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("status", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultStatus).Value
	if got != val {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}

func randBytes(t *testing.T) []byte {
	n := rand.Intn(10) + 2
	buf := make([]byte, n)
	_, err := crand.Read(buf)
	if err != nil {
		t.Fatal(err)
	}
	return bytes.Replace(buf, []byte("="), []byte{100}, -1)
}

func TestByteSliceViaJSONRPC(t *testing.T) {
	cl := client.NewClientJSONRPC(unixAddr)

	val := randBytes(t)
	params := map[string]interface{}{
		"arg": val,
	}
	var result Result
	_, err := cl.Call("bytes", params, &result)
	if err != nil {
		t.Fatal(err)
	}
	got := result.(*ResultBytes).Value
	if bytes.Compare(got, val) != 0 {
		t.Fatalf("Got: %v   ....   Expected: %v \n", got, val)
	}
}
<|endoftext|>