<commit_before>package bitfinex

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"log"
	"net/http"
	"reflect"

	"github.com/bitfinexcom/bitfinex-api-go/utils"

	"github.com/gorilla/websocket"
)

// Pairs available
const (
	// Pairs
	BTCUSD = "BTCUSD"
	LTCUSD = "LTCUSD"
	LTCBTC = "LTCBTC"
	ETHUSD = "ETHUSD"
	ETHBTC = "ETHBTC"
	ETCUSD = "ETCUSD"
	ETCBTC = "ETCBTC"
	BFXUSD = "BFXUSD"
	BFXBTC = "BFXBTC"
	ZECUSD = "ZECUSD"
	ZECBTC = "ZECBTC"
	XMRUSD = "XMRUSD"
	XMRBTC = "XMRBTC"
	RRTUSD = "RRTUSD"
	RRTBTC = "RRTBTC"

	// Channels
	ChanBook   = "book"
	ChanTrade  = "trades"
	ChanTicker = "ticker"
)

// WebSocketService allow to connect and receive stream data
// from bitfinex.com ws service.
type WebSocketService struct {
	// http client
	client *Client
	// websocket client
	ws *websocket.Conn
	// special web socket for private messages
	privateWs *websocket.Conn
	// map internal channels to websocket's
	chanMap    map[float64]chan []float64
	subscribes []subscribeToChannel
}

type subscribeMsg struct {
	Event   string  `json:"event"`
	Channel string  `json:"channel"`
	Pair    string  `json:"pair"`
	ChanID  float64 `json:"chanId,omitempty"`
}

type subscribeToChannel struct {
	Channel string
	Pair    string
	Chan    chan []float64
}

// NewWebSocketService returns a WebSocketService using the given client.
func NewWebSocketService(c *Client) *WebSocketService {
	return &WebSocketService{
		client:     c,
		chanMap:    make(map[float64]chan []float64),
		subscribes: make([]subscribeToChannel, 0),
	}
}

// Connect create new bitfinex websocket connection
func (w *WebSocketService) Connect() error {
	var d = websocket.Dialer{
		Subprotocols:    []string{"p1", "p2"},
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		Proxy:           http.ProxyFromEnvironment,
	}

	if w.client.WebSocketTLSSkipVerify {
		d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	ws, _, err := d.Dial(w.client.WebSocketURL, nil)
	if err != nil {
		return err
	}
	w.ws = ws
	return nil
}

// Close web socket connection
func (w *WebSocketService) Close() {
	w.ws.Close()
}

func (w *WebSocketService) AddSubscribe(channel string, pair string, c chan []float64) {
	s := subscribeToChannel{
		Channel: channel,
		Pair:    pair,
		Chan:    c,
	}
	w.subscribes = append(w.subscribes, s)
}

func (w *WebSocketService) ClearSubscriptions() {
	w.subscribes = make([]subscribeToChannel, 0)
}

func (w *WebSocketService) sendSubscribeMessages() error {
	for _, s := range w.subscribes {
		msg, _ := json.Marshal(subscribeMsg{
			Event:   "subscribe",
			Channel: s.Channel,
			Pair:    s.Pair,
		})

		err := w.ws.WriteMessage(websocket.TextMessage, msg)
		if err != nil {
			return err
		}
	}
	return nil
}

// Subscribe allows to subsribe to channels and watch for new updates.
// This method supports next channels: book, trade, ticker.
func (w *WebSocketService) Subscribe() error {
	// Subscribe to each channel
	if err := w.sendSubscribeMessages(); err != nil {
		return err
	}

	for {
		_, p, err := w.ws.ReadMessage()
		if err != nil {
			return err
		}

		if bytes.Contains(p, []byte("event")) {
			w.handleEventMessage(p)
		} else {
			w.handleDataMessage(p)
		}
	}

	return nil
}

func (w *WebSocketService) handleEventMessage(msg []byte) {
	// Check for first message(event:subscribed)
	event := &subscribeMsg{}
	err := json.Unmarshal(msg, event)

	// Received "subscribed" resposne. Link channels.
	if err == nil {
		for _, k := range w.subscribes {
			if event.Event == "subscribed" && event.Pair == k.Pair && event.Channel == k.Channel {
				w.chanMap[event.ChanID] = k.Chan
			}
		}
	}
}

func (w *WebSocketService) handleDataMessage(msg []byte) {
	// Received payload or data update
	var dataUpdate []float64
	err := json.Unmarshal(msg, &dataUpdate)
	if err == nil {
		chanID := dataUpdate[0]
		// Remove chanID from data update
		// and send message to internal chan
		w.chanMap[chanID] <- dataUpdate[1:]
	}

	// Payload received
	var fullPayload []interface{}
	err = json.Unmarshal(msg, &fullPayload)

	if err != nil {
		log.Println("Error decoding fullPayload", err)
	} else {
		if len(fullPayload) > 3 {
			itemsSlice := fullPayload[3:]
			i, _ := json.Marshal(itemsSlice)
			var item []float64
			err = json.Unmarshal(i, &item)
			if err == nil {
				chanID := fullPayload[0].(float64)
				w.chanMap[chanID] <- item
			}
		} else {
			itemsSlice := fullPayload[1]
			i, _ := json.Marshal(itemsSlice)
			var items [][]float64
			err = json.Unmarshal(i, &items)
			if err == nil {
				chanID := fullPayload[0].(float64)
				for _, v := range items {
					w.chanMap[chanID] <- v
				}
			}
		}
	}
}

/////////////////////////////
// Private websocket messages
/////////////////////////////

type privateConnect struct {
	Event       string `json:"event"`
	APIKey      string `json:"apiKey"`
	AuthSig     string `json:"authSig"`
	AuthPayload string `json:"authPayload"`
}

// Private channel auth response
type privateResponse struct {
	Event  string  `json:"event"`
	Status string  `json:"status"`
	ChanID float64 `json:"chanId,omitempty"`
	UserID float64 `json:"userId"`
}

type TermData struct {
	// Data term. E.g: ps, ws, ou, etc... See official documentation for more details.
	Term string
	// Data will contain different number of elements for each term.
	// Examples:
	// Term: ws, Data: ["exchange","BTC",0.01410829,0]
	// Term: oc, Data: [0,"BTCUSD",0,-0.01,"","CANCELED",270,0,"2015-10-15T11:26:13Z",0]
	Data  []interface{}
	Error string
}

func (c *TermData) HasError() bool {
	return len(c.Error) > 0
}

func (w *WebSocketService) ConnectPrivate(ch chan TermData) {

	var d = websocket.Dialer{
		Subprotocols:    []string{"p1", "p2"},
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		Proxy:           http.ProxyFromEnvironment,
	}

	if w.client.WebSocketTLSSkipVerify {
		d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	ws, _, err := d.Dial(w.client.WebSocketURL, nil)
	if err != nil {
		ch <- TermData{
			Error: err.Error(),
		}
		return
	}

	nonce := utils.GetNonce()
	payload := "AUTH" + nonce
	connectMsg, _ := json.Marshal(&privateConnect{
		Event:       "auth",
		APIKey:      w.client.APIKey,
		AuthSig:     w.client.signPayload(payload),
		AuthPayload: payload,
	})

	// Send auth message
	err = ws.WriteMessage(websocket.TextMessage, connectMsg)
	if err != nil {
		ch <- TermData{
			Error: err.Error(),
		}
		ws.Close()
		return
	}

	for {
		_, p, err := ws.ReadMessage()
		if err != nil {
			ch <- TermData{
				Error: err.Error(),
			}
			ws.Close()
			return
		}

		event := &privateResponse{}
		err = json.Unmarshal(p, &event)
		if err != nil {
			// received data update
			var data []interface{}
			err = json.Unmarshal(p, &data)
			if err == nil {
				if len(data) == 2 { // Heartbeat
					// XXX: Consider adding a switch to enable/disable passing these along.
					ch <- TermData{Term: data[1].(string)}
					return
				}

				dataTerm := data[1].(string)
				dataList := data[2].([]interface{})

				// check for empty data
				if len(dataList) > 0 {
					if reflect.TypeOf(dataList[0]) == reflect.TypeOf([]interface{}{}) {
						// received list of lists
						for _, v := range dataList {
							ch <- TermData{
								Term: dataTerm,
								Data: v.([]interface{}),
							}
						}
					} else {
						// received flat list
						ch <- TermData{
							Term: dataTerm,
							Data: dataList,
						}
					}
				}
			}
		} else {
			// received auth response
			if event.Event == "auth" && event.Status != "OK" {
				ch <- TermData{
					Error: "Error connecting to private web socket channel.",
				}
				ws.Close()
			}
		}
	}
}
<commit_msg>add ripple currency pair constants<commit_after>package bitfinex

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"log"
	"net/http"
	"reflect"

	"github.com/bitfinexcom/bitfinex-api-go/utils"

	"github.com/gorilla/websocket"
)

// Pairs available
const (
	// Pairs
	BTCUSD = "BTCUSD"
	LTCUSD = "LTCUSD"
	LTCBTC = "LTCBTC"
	ETHUSD = "ETHUSD"
	ETHBTC = "ETHBTC"
	ETCUSD = "ETCUSD"
	ETCBTC = "ETCBTC"
	BFXUSD = "BFXUSD"
	BFXBTC = "BFXBTC"
	ZECUSD = "ZECUSD"
	ZECBTC = "ZECBTC"
	XMRUSD = "XMRUSD"
	XMRBTC = "XMRBTC"
	RRTUSD = "RRTUSD"
	RRTBTC = "RRTBTC"
	XRPUSD = "XRPUSD"
	XRPBTC = "XRPBTC"

	// Channels
	ChanBook   = "book"
	ChanTrade  = "trades"
	ChanTicker = "ticker"
)

// WebSocketService allow to connect and receive stream data
// from bitfinex.com ws service.
type WebSocketService struct {
	// http client
	client *Client
	// websocket client
	ws *websocket.Conn
	// special web socket for private messages
	privateWs *websocket.Conn
	// map internal channels to websocket's
	chanMap    map[float64]chan []float64
	subscribes []subscribeToChannel
}

type subscribeMsg struct {
	Event   string  `json:"event"`
	Channel string  `json:"channel"`
	Pair    string  `json:"pair"`
	ChanID  float64 `json:"chanId,omitempty"`
}

type subscribeToChannel struct {
	Channel string
	Pair    string
	Chan    chan []float64
}

// NewWebSocketService returns a WebSocketService using the given client.
func NewWebSocketService(c *Client) *WebSocketService {
	return &WebSocketService{
		client:     c,
		chanMap:    make(map[float64]chan []float64),
		subscribes: make([]subscribeToChannel, 0),
	}
}

// Connect create new bitfinex websocket connection
func (w *WebSocketService) Connect() error {
	var d = websocket.Dialer{
		Subprotocols:    []string{"p1", "p2"},
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		Proxy:           http.ProxyFromEnvironment,
	}

	if w.client.WebSocketTLSSkipVerify {
		d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	ws, _, err := d.Dial(w.client.WebSocketURL, nil)
	if err != nil {
		return err
	}
	w.ws = ws
	return nil
}

// Close web socket connection
func (w *WebSocketService) Close() {
	w.ws.Close()
}

func (w *WebSocketService) AddSubscribe(channel string, pair string, c chan []float64) {
	s := subscribeToChannel{
		Channel: channel,
		Pair:    pair,
		Chan:    c,
	}
	w.subscribes = append(w.subscribes, s)
}

func (w *WebSocketService) ClearSubscriptions() {
	w.subscribes = make([]subscribeToChannel, 0)
}

func (w *WebSocketService) sendSubscribeMessages() error {
	for _, s := range w.subscribes {
		msg, _ := json.Marshal(subscribeMsg{
			Event:   "subscribe",
			Channel: s.Channel,
			Pair:    s.Pair,
		})

		err := w.ws.WriteMessage(websocket.TextMessage, msg)
		if err != nil {
			return err
		}
	}
	return nil
}

// Subscribe allows to subsribe to channels and watch for new updates.
// This method supports next channels: book, trade, ticker.
func (w *WebSocketService) Subscribe() error {
	// Subscribe to each channel
	if err := w.sendSubscribeMessages(); err != nil {
		return err
	}

	for {
		_, p, err := w.ws.ReadMessage()
		if err != nil {
			return err
		}

		if bytes.Contains(p, []byte("event")) {
			w.handleEventMessage(p)
		} else {
			w.handleDataMessage(p)
		}
	}

	return nil
}

func (w *WebSocketService) handleEventMessage(msg []byte) {
	// Check for first message(event:subscribed)
	event := &subscribeMsg{}
	err := json.Unmarshal(msg, event)

	// Received "subscribed" resposne. Link channels.
	if err == nil {
		for _, k := range w.subscribes {
			if event.Event == "subscribed" && event.Pair == k.Pair && event.Channel == k.Channel {
				w.chanMap[event.ChanID] = k.Chan
			}
		}
	}
}

func (w *WebSocketService) handleDataMessage(msg []byte) {
	// Received payload or data update
	var dataUpdate []float64
	err := json.Unmarshal(msg, &dataUpdate)
	if err == nil {
		chanID := dataUpdate[0]
		// Remove chanID from data update
		// and send message to internal chan
		w.chanMap[chanID] <- dataUpdate[1:]
	}

	// Payload received
	var fullPayload []interface{}
	err = json.Unmarshal(msg, &fullPayload)

	if err != nil {
		log.Println("Error decoding fullPayload", err)
	} else {
		if len(fullPayload) > 3 {
			itemsSlice := fullPayload[3:]
			i, _ := json.Marshal(itemsSlice)
			var item []float64
			err = json.Unmarshal(i, &item)
			if err == nil {
				chanID := fullPayload[0].(float64)
				w.chanMap[chanID] <- item
			}
		} else {
			itemsSlice := fullPayload[1]
			i, _ := json.Marshal(itemsSlice)
			var items [][]float64
			err = json.Unmarshal(i, &items)
			if err == nil {
				chanID := fullPayload[0].(float64)
				for _, v := range items {
					w.chanMap[chanID] <- v
				}
			}
		}
	}
}

/////////////////////////////
// Private websocket messages
/////////////////////////////

type privateConnect struct {
	Event       string `json:"event"`
	APIKey      string `json:"apiKey"`
	AuthSig     string `json:"authSig"`
	AuthPayload string `json:"authPayload"`
}

// Private channel auth response
type privateResponse struct {
	Event  string  `json:"event"`
	Status string  `json:"status"`
	ChanID float64 `json:"chanId,omitempty"`
	UserID float64 `json:"userId"`
}

type TermData struct {
	// Data term. E.g: ps, ws, ou, etc... See official documentation for more details.
	Term string
	// Data will contain different number of elements for each term.
	// Examples:
	// Term: ws, Data: ["exchange","BTC",0.01410829,0]
	// Term: oc, Data: [0,"BTCUSD",0,-0.01,"","CANCELED",270,0,"2015-10-15T11:26:13Z",0]
	Data  []interface{}
	Error string
}

func (c *TermData) HasError() bool {
	return len(c.Error) > 0
}

func (w *WebSocketService) ConnectPrivate(ch chan TermData) {

	var d = websocket.Dialer{
		Subprotocols:    []string{"p1", "p2"},
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		Proxy:           http.ProxyFromEnvironment,
	}

	if w.client.WebSocketTLSSkipVerify {
		d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	ws, _, err := d.Dial(w.client.WebSocketURL, nil)
	if err != nil {
		ch <- TermData{
			Error: err.Error(),
		}
		return
	}

	nonce := utils.GetNonce()
	payload := "AUTH" + nonce
	connectMsg, _ := json.Marshal(&privateConnect{
		Event:       "auth",
		APIKey:      w.client.APIKey,
		AuthSig:     w.client.signPayload(payload),
		AuthPayload: payload,
	})

	// Send auth message
	err = ws.WriteMessage(websocket.TextMessage, connectMsg)
	if err != nil {
		ch <- TermData{
			Error: err.Error(),
		}
		ws.Close()
		return
	}

	for {
		_, p, err := ws.ReadMessage()
		if err != nil {
			ch <- TermData{
				Error: err.Error(),
			}
			ws.Close()
			return
		}

		event := &privateResponse{}
		err = json.Unmarshal(p, &event)
		if err != nil {
			// received data update
			var data []interface{}
			err = json.Unmarshal(p, &data)
			if err == nil {
				if len(data) == 2 { // Heartbeat
					// XXX: Consider adding a switch to enable/disable passing these along.
					ch <- TermData{Term: data[1].(string)}
					return
				}

				dataTerm := data[1].(string)
				dataList := data[2].([]interface{})

				// check for empty data
				if len(dataList) > 0 {
					if reflect.TypeOf(dataList[0]) == reflect.TypeOf([]interface{}{}) {
						// received list of lists
						for _, v := range dataList {
							ch <- TermData{
								Term: dataTerm,
								Data: v.([]interface{}),
							}
						}
					} else {
						// received flat list
						ch <- TermData{
							Term: dataTerm,
							Data: dataList,
						}
					}
				}
			}
		} else {
			// received auth response
			if event.Event == "auth" && event.Status != "OK" {
				ch <- TermData{
					Error: "Error connecting to private web socket channel.",
				}
				ws.Close()
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"testing"
)

func TestEmptyDirectoryMovesInOutAround(t *testing.T) {
	trackerTestEmptyDirectoryMovesInOutAround()
}

func TestNewFileAndRenameFileInside(t *testing.T) {
	trackerTestSmallFileMovesInOutAround()
}

func TestDirectoryCreation(t *testing.T) {
	tmpFolder, err := ioutil.TempDir("", "blank")
	defer os.RemoveAll(tmpFolder)

	tracker := new(FilesystemTracker)
	tracker.init(tmpFolder)
	defer tracker.cleanup()

	testDirectory := tmpFolder + "/" + "newbie"
	before, err := os.Stat(testDirectory)

	os.Mkdir(testDirectory, os.ModeDir+os.ModePerm)

	after, err := os.Stat(testDirectory)

	if err != nil {
		fmt.Println("GACK, directory should exist")
	}

	err = os.Remove(testDirectory)

	_, err = os.Stat(testDirectory)

	if !os.IsNotExist(err) {
		t.Fatal("The folder still exists.....oopps")
	}

	fmt.Printf("TestDirectoryCreation\nbefore: %v\nafter: %v\n", before, after)

}

func TestDirectoryStorage(t *testing.T) {
	tmpFolder, err := ioutil.TempDir("", "blank")
	defer os.RemoveAll(tmpFolder)

	tracker := new(FilesystemTracker)
	tracker.init(tmpFolder)
	defer tracker.cleanup()

	empty := make([]string, 0)
	empty = append(empty, ".")

	folderList := tracker.ListFolders()

	if reflect.DeepEqual(empty, folderList) == false {
		t.Fatal(fmt.Sprintf("expected empty folder, found something: %v\n", folderList))
	}

	folderTemplate := []string{"A", "B", "C"}

	for _, folder := range folderTemplate {
		err := tracker.CreatePath(folder, true)
		if err != nil {
			t.Fatal(err)
		}
	}

	folderList = tracker.ListFolders()

	expectedFolderList := append(folderTemplate, ".")
	sort.Strings(expectedFolderList)
	sort.Strings(folderList)

	if reflect.DeepEqual(expectedFolderList, folderList) == false {
		t.Fatal(fmt.Sprintf("Found: %v\nExpected: %v\n", folderList, expectedFolderList))
	}

	err = tracker.DeleteFolder(folderTemplate[0])
	if err != nil {
		t.Fatal(err)
	}

	folderList = tracker.ListFolders()
	sort.Strings(folderList)

	folderTemplate = folderTemplate[1:]

	expectedFolderList = append(folderTemplate, ".")
	sort.Strings(expectedFolderList)

	if reflect.DeepEqual(folderList, expectedFolderList) == false {
		t.Fatal(fmt.Sprintf("Found: %v\nExpected: %v\n", folderList, expectedFolderList))
	}

}

func TestFileChangeTrackerAutoCreateFolderAndCleanup(t *testing.T) {
	tracker := new(FilesystemTracker)

	tmpFolder, err := ioutil.TempDir("", "blank")
	defer os.RemoveAll(tmpFolder)

	tmpFolder = tmpFolder + "R"
	defer os.RemoveAll(tmpFolder)

	logger := &LogOnlyChangeHandler{}
	var loggerInterface ChangeHandler = logger

	tracker.init(tmpFolder)
	tracker.watchDirectory(&loggerInterface)

	// verify the folder was created
	_, err = filepath.EvalSymlinks(tmpFolder)
	if err != nil {
		t.Fatal(err)
	}

	tracker.cleanup()
}

func TestFileChangeTrackerAddFolders(t *testing.T) {
	trackerTestFileChangeTrackerAddFolders()
}
<commit_msg>shortening string concatination code<commit_after>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"testing"
)

func TestEmptyDirectoryMovesInOutAround(t *testing.T) {
	trackerTestEmptyDirectoryMovesInOutAround()
}

func TestNewFileAndRenameFileInside(t *testing.T) {
	trackerTestSmallFileMovesInOutAround()
}

func TestDirectoryCreation(t *testing.T) {
	tmpFolder, err := ioutil.TempDir("", "blank")
	defer os.RemoveAll(tmpFolder)

	tracker := new(FilesystemTracker)
	tracker.init(tmpFolder)
	defer tracker.cleanup()

	testDirectory := tmpFolder + "/newbie"
	before, err := os.Stat(testDirectory)

	os.Mkdir(testDirectory, os.ModeDir+os.ModePerm)

	after, err := os.Stat(testDirectory)

	if err != nil {
		fmt.Println("GACK, directory should exist")
	}

	err = os.Remove(testDirectory)

	_, err = os.Stat(testDirectory)

	if !os.IsNotExist(err) {
		t.Fatal("The folder still exists.....oopps")
	}

	fmt.Printf("TestDirectoryCreation\nbefore: %v\nafter: %v\n", before, after)

}

func TestDirectoryStorage(t *testing.T) {
	tmpFolder, err := ioutil.TempDir("", "blank")
	defer os.RemoveAll(tmpFolder)

	tracker := new(FilesystemTracker)
	tracker.init(tmpFolder)
	defer tracker.cleanup()

	empty := make([]string, 0)
	empty = append(empty, ".")

	folderList := tracker.ListFolders()

	if reflect.DeepEqual(empty, folderList) == false {
		t.Fatal(fmt.Sprintf("expected empty folder, found something: %v\n", folderList))
	}

	folderTemplate := []string{"A", "B", "C"}

	for _, folder := range folderTemplate {
		err := tracker.CreatePath(folder, true)
		if err != nil {
			t.Fatal(err)
		}
	}

	folderList = tracker.ListFolders()

	expectedFolderList := append(folderTemplate, ".")
	sort.Strings(expectedFolderList)
	sort.Strings(folderList)

	if reflect.DeepEqual(expectedFolderList, folderList) == false {
		t.Fatal(fmt.Sprintf("Found: %v\nExpected: %v\n", folderList, expectedFolderList))
	}

	err = tracker.DeleteFolder(folderTemplate[0])
	if err != nil {
		t.Fatal(err)
	}

	folderList = tracker.ListFolders()
	sort.Strings(folderList)

	folderTemplate = folderTemplate[1:]

	expectedFolderList = append(folderTemplate, ".")
	sort.Strings(expectedFolderList)

	if reflect.DeepEqual(folderList, expectedFolderList) == false {
		t.Fatal(fmt.Sprintf("Found: %v\nExpected: %v\n", folderList, expectedFolderList))
	}

}

func TestFileChangeTrackerAutoCreateFolderAndCleanup(t *testing.T) {
	tracker := new(FilesystemTracker)

	tmpFolder, err := ioutil.TempDir("", "blank")
	defer os.RemoveAll(tmpFolder)

	tmpFolder = tmpFolder + "R"
	defer os.RemoveAll(tmpFolder)

	logger := &LogOnlyChangeHandler{}
	var loggerInterface ChangeHandler = logger

	tracker.init(tmpFolder)
	tracker.watchDirectory(&loggerInterface)

	// verify the folder was created
	_, err = filepath.EvalSymlinks(tmpFolder)
	if err != nil {
		t.Fatal(err)
	}

	tracker.cleanup()
}

func TestFileChangeTrackerAddFolders(t *testing.T) {
	trackerTestFileChangeTrackerAddFolders()
}
<|endoftext|>


<commit_before>package main

import (
	"../../nsq"
	"bufio"
	"flag"
	"log"
	"math"
	"net"
	"runtime"
	"sync"
	"time"
)

var (
	num        = flag.Int("num", 1000000, "num messages")
	tcpAddress = flag.String("nsqd-tcp-address", "127.0.0.1:4150", "<addr>:<port> to connect to nsqd")
	topic      = flag.String("topic", "sub_bench", "topic to receive messages on")
	channel    = flag.String("channel", "ch", "channel to receive messages on")
)

func main() {
	flag.Parse()
	var wg sync.WaitGroup

	goChan := make(chan int)
	rdyChan := make(chan int)
	workers := runtime.GOMAXPROCS(0)
	for j := 0; j < workers; j++ {
		wg.Add(1)
		go func(id int) {
			subWorker(*num, workers, *tcpAddress, *topic, *channel, rdyChan, goChan, id)
			wg.Done()
		}(j)
		<-rdyChan
	}

	start := time.Now()
	close(goChan)
	wg.Wait()
	end := time.Now()
	duration := end.Sub(start)
	log.Printf("duration: %s - %.03fmb/s - %.03fops/s - %.03fus/op",
		duration,
		float64(*num * 200) / duration.Seconds() / 1024 / 1024,
		float64(*num) / duration.Seconds(),
		float64(duration / time.Microsecond) / float64(*num))
}

func subWorker(n int, workers int, tcpAddr string, topic string, channel string, rdyChan chan int, goChan chan int, id int) {
	conn, err := net.DialTimeout("tcp", tcpAddr, time.Second)
	if err != nil {
		panic(err.Error())
	}
	conn.Write(nsq.MagicV2)
	rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
	ci := make(map[string]interface{})
	ci["short_id"] = "test"
	ci["long_id"] = "test"
	cmd, _ := nsq.Idenfity(ci)
	cmd.Write(rw)
	nsq.Subscribe(topic, channel).Write(rw)
	rdyCount := int(math.Min(math.Max(float64(n/workers), 1), 2500))
	rdyChan <- 1
	<-goChan
	nsq.Ready(rdyCount).Write(rw)
	rw.Flush()
	num := n / workers
	numRdy := num/rdyCount - 1
	rdy := rdyCount
	for i := 0; i < num; i += 1 {
		resp, err := nsq.ReadResponse(rw)
		if err != nil {
			panic(err.Error())
		}
		frameType, data, err := nsq.UnpackResponse(resp)
		if err != nil {
			panic(err.Error())
		}
		if frameType == nsq.FrameTypeError {
			panic("got something else")
		}
		msg, err := nsq.DecodeMessage(data)
		if err != nil {
			panic(err.Error())
		}
		nsq.Finish(msg.Id).Write(rw)
		rdy--
		if rdy == 0 && numRdy > 0 {
			nsq.Ready(rdyCount).Write(rw)
			rdy = rdyCount
			numRdy--
			rw.Flush()
		}
	}
}
<commit_msg>fix spell 'Identify'<commit_after>package main

import (
	"../../nsq"
	"bufio"
	"flag"
	"log"
	"math"
	"net"
	"runtime"
	"sync"
	"time"
)

var (
	num        = flag.Int("num", 1000000, "num messages")
	tcpAddress = flag.String("nsqd-tcp-address", "127.0.0.1:4150", "<addr>:<port> to connect to nsqd")
	topic      = flag.String("topic", "sub_bench", "topic to receive messages on")
	channel    = flag.String("channel", "ch", "channel to receive messages on")
)

func main() {
	flag.Parse()
	var wg sync.WaitGroup

	goChan := make(chan int)
	rdyChan := make(chan int)
	workers := runtime.GOMAXPROCS(0)
	for j := 0; j < workers; j++ {
		wg.Add(1)
		go func(id int) {
			subWorker(*num, workers, *tcpAddress, *topic, *channel, rdyChan, goChan, id)
			wg.Done()
		}(j)
		<-rdyChan
	}

	start := time.Now()
	close(goChan)
	wg.Wait()
	end := time.Now()
	duration := end.Sub(start)
	log.Printf("duration: %s - %.03fmb/s - %.03fops/s - %.03fus/op",
		duration,
		float64(*num * 200) / duration.Seconds() / 1024 / 1024,
		float64(*num) / duration.Seconds(),
		float64(duration / time.Microsecond) / float64(*num))
}

func subWorker(n int, workers int, tcpAddr string, topic string, channel string, rdyChan chan int, goChan chan int, id int) {
	conn, err := net.DialTimeout("tcp", tcpAddr, time.Second)
	if err != nil {
		panic(err.Error())
	}
	conn.Write(nsq.MagicV2)
	rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
	ci := make(map[string]interface{})
	ci["short_id"] = "test"
	ci["long_id"] = "test"
	cmd, _ := nsq.Identify(ci)
	cmd.Write(rw)
	nsq.Subscribe(topic, channel).Write(rw)
	rdyCount := int(math.Min(math.Max(float64(n/workers), 1), 2500))
	rdyChan <- 1
	<-goChan
	nsq.Ready(rdyCount).Write(rw)
	rw.Flush()
	num := n / workers
	numRdy := num/rdyCount - 1
	rdy := rdyCount
	for i := 0; i < num; i += 1 {
		resp, err := nsq.ReadResponse(rw)
		if err != nil {
			panic(err.Error())
		}
		frameType, data, err := nsq.UnpackResponse(resp)
		if err != nil {
			panic(err.Error())
		}
		if frameType == nsq.FrameTypeError {
			panic("got something else")
		}
		msg, err := nsq.DecodeMessage(data)
		if err != nil {
			panic(err.Error())
		}
		nsq.Finish(msg.Id).Write(rw)
		rdy--
		if rdy == 0 && numRdy > 0 {
			nsq.Ready(rdyCount).Write(rw)
			rdy = rdyCount
			numRdy--
			rw.Flush()
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The Prometheus 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.

// +build !nofilesystem

package collector

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/prometheus/common/log"
	"golang.org/x/sys/unix"
)

const (
	defIgnoredMountPoints = "^/(dev|proc|sys|var/lib/docker/.+)($|/)"
	defIgnoredFSTypes     = "^(autofs|binfmt_misc|bpf|cgroup2?|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|nsfs|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|selinuxfs|squashfs|sysfs|tracefs)$"
	mountTimeout          = 30 * time.Second
)

var stuckMounts = make(map[string]struct{})
var stuckMountsMtx = &sync.Mutex{}

// GetStats returns filesystem stats.
func (c *filesystemCollector) GetStats() ([]filesystemStats, error) {
	mps, err := mountPointDetails()
	if err != nil {
		return nil, err
	}
	stats := []filesystemStats{}
	for _, labels := range mps {
		if c.ignoredMountPointsPattern.MatchString(labels.mountPoint) {
			log.Debugf("Ignoring mount point: %s", labels.mountPoint)
			continue
		}
		if c.ignoredFSTypesPattern.MatchString(labels.fsType) {
			log.Debugf("Ignoring fs type: %s", labels.fsType)
			continue
		}
		stuckMountsMtx.Lock()
		if _, ok := stuckMounts[labels.mountPoint]; ok {
			stats = append(stats, filesystemStats{
				labels:      labels,
				deviceError: 1,
			})
			log.Debugf("Mount point %q is in an unresponsive state", labels.mountPoint)
			stuckMountsMtx.Unlock()
			continue
		}
		stuckMountsMtx.Unlock()

		// The success channel is used do tell the "watcher" that the stat
		// finished successfully. The channel is closed on success.
		success := make(chan struct{})
		go stuckMountWatcher(labels.mountPoint, success)

		buf := new(unix.Statfs_t)
		err = unix.Statfs(rootfsFilePath(labels.mountPoint), buf)
		stuckMountsMtx.Lock()
		close(success)
		// If the mount has been marked as stuck, unmark it and log it's recovery.
		if _, ok := stuckMounts[labels.mountPoint]; ok {
			log.Debugf("Mount point %q has recovered, monitoring will resume", labels.mountPoint)
			delete(stuckMounts, labels.mountPoint)
		}
		stuckMountsMtx.Unlock()

		if err != nil {
			stats = append(stats, filesystemStats{
				labels:      labels,
				deviceError: 1,
			})
			log.Debugf("Error on statfs() system call for %q: %s", rootfsFilePath(labels.mountPoint), err)
			continue
		}

		var ro float64
		for _, option := range strings.Split(labels.options, ",") {
			if option == "ro" {
				ro = 1
				break
			}
		}

		stats = append(stats, filesystemStats{
			labels:    labels,
			size:      float64(buf.Blocks) * float64(buf.Bsize),
			free:      float64(buf.Bfree) * float64(buf.Bsize),
			avail:     float64(buf.Bavail) * float64(buf.Bsize),
			files:     float64(buf.Files),
			filesFree: float64(buf.Ffree),
			ro:        ro,
		})
	}
	return stats, nil
}

// stuckMountWatcher listens on the given success channel and if the channel closes
// then the watcher does nothing. If instead the timeout is reached, the
// mount point that is being watched is marked as stuck.
func stuckMountWatcher(mountPoint string, success chan struct{}) {
	select {
	case <-success:
		// Success
	case <-time.After(mountTimeout):
		// Timed out, mark mount as stuck
		stuckMountsMtx.Lock()
		select {
		case <-success:
			// Success came in just after the timeout was reached, don't label the mount as stuck
		default:
			log.Debugf("Mount point %q timed out, it is being labeled as stuck and will not be monitored", mountPoint)
			stuckMounts[mountPoint] = struct{}{}
		}
		stuckMountsMtx.Unlock()
	}
}

func mountPointDetails() ([]filesystemLabels, error) {
	file, err := os.Open(procFilePath("1/mounts"))
	if os.IsNotExist(err) {
		// Fallback to `/proc/mounts` if `/proc/1/mounts` is missing due hidepid.
		log.Debugf("Got %q reading root mounts, falling back to system mounts", err)
		file, err = os.Open(procFilePath("mounts"))
	}
	if err != nil {
		return nil, err
	}
	defer file.Close()

	return parseFilesystemLabels(file)
}

func parseFilesystemLabels(r io.Reader) ([]filesystemLabels, error) {
	var filesystems []filesystemLabels

	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		parts := strings.Fields(scanner.Text())

		if len(parts) < 4 {
			return nil, fmt.Errorf("malformed mount point information: %q", scanner.Text())
		}

		// Ensure we handle the translation of \040 and \011
		// as per fstab(5).
		parts[1] = strings.Replace(parts[1], "\\040", " ", -1)
		parts[1] = strings.Replace(parts[1], "\\011", "\t", -1)

		filesystems = append(filesystems, filesystemLabels{
			device:     parts[0],
			mountPoint: parts[1],
			fsType:     parts[2],
			options:    parts[3],
		})
	}

	return filesystems, scanner.Err()
}
<commit_msg>Ignore iso9600 filesystem on Linux (#1355)<commit_after>// Copyright 2015 The Prometheus 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.

// +build !nofilesystem

package collector

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/prometheus/common/log"
	"golang.org/x/sys/unix"
)

const (
	defIgnoredMountPoints = "^/(dev|proc|sys|var/lib/docker/.+)($|/)"
	defIgnoredFSTypes     = "^(autofs|binfmt_misc|bpf|cgroup2?|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|iso9660|mqueue|nsfs|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|selinuxfs|squashfs|sysfs|tracefs)$"
	mountTimeout          = 30 * time.Second
)

var stuckMounts = make(map[string]struct{})
var stuckMountsMtx = &sync.Mutex{}

// GetStats returns filesystem stats.
func (c *filesystemCollector) GetStats() ([]filesystemStats, error) {
	mps, err := mountPointDetails()
	if err != nil {
		return nil, err
	}
	stats := []filesystemStats{}
	for _, labels := range mps {
		if c.ignoredMountPointsPattern.MatchString(labels.mountPoint) {
			log.Debugf("Ignoring mount point: %s", labels.mountPoint)
			continue
		}
		if c.ignoredFSTypesPattern.MatchString(labels.fsType) {
			log.Debugf("Ignoring fs type: %s", labels.fsType)
			continue
		}
		stuckMountsMtx.Lock()
		if _, ok := stuckMounts[labels.mountPoint]; ok {
			stats = append(stats, filesystemStats{
				labels:      labels,
				deviceError: 1,
			})
			log.Debugf("Mount point %q is in an unresponsive state", labels.mountPoint)
			stuckMountsMtx.Unlock()
			continue
		}
		stuckMountsMtx.Unlock()

		// The success channel is used do tell the "watcher" that the stat
		// finished successfully. The channel is closed on success.
		success := make(chan struct{})
		go stuckMountWatcher(labels.mountPoint, success)

		buf := new(unix.Statfs_t)
		err = unix.Statfs(rootfsFilePath(labels.mountPoint), buf)
		stuckMountsMtx.Lock()
		close(success)
		// If the mount has been marked as stuck, unmark it and log it's recovery.
		if _, ok := stuckMounts[labels.mountPoint]; ok {
			log.Debugf("Mount point %q has recovered, monitoring will resume", labels.mountPoint)
			delete(stuckMounts, labels.mountPoint)
		}
		stuckMountsMtx.Unlock()

		if err != nil {
			stats = append(stats, filesystemStats{
				labels:      labels,
				deviceError: 1,
			})
			log.Debugf("Error on statfs() system call for %q: %s", rootfsFilePath(labels.mountPoint), err)
			continue
		}

		var ro float64
		for _, option := range strings.Split(labels.options, ",") {
			if option == "ro" {
				ro = 1
				break
			}
		}

		stats = append(stats, filesystemStats{
			labels:    labels,
			size:      float64(buf.Blocks) * float64(buf.Bsize),
			free:      float64(buf.Bfree) * float64(buf.Bsize),
			avail:     float64(buf.Bavail) * float64(buf.Bsize),
			files:     float64(buf.Files),
			filesFree: float64(buf.Ffree),
			ro:        ro,
		})
	}
	return stats, nil
}

// stuckMountWatcher listens on the given success channel and if the channel closes
// then the watcher does nothing. If instead the timeout is reached, the
// mount point that is being watched is marked as stuck.
func stuckMountWatcher(mountPoint string, success chan struct{}) {
	select {
	case <-success:
		// Success
	case <-time.After(mountTimeout):
		// Timed out, mark mount as stuck
		stuckMountsMtx.Lock()
		select {
		case <-success:
			// Success came in just after the timeout was reached, don't label the mount as stuck
		default:
			log.Debugf("Mount point %q timed out, it is being labeled as stuck and will not be monitored", mountPoint)
			stuckMounts[mountPoint] = struct{}{}
		}
		stuckMountsMtx.Unlock()
	}
}

func mountPointDetails() ([]filesystemLabels, error) {
	file, err := os.Open(procFilePath("1/mounts"))
	if os.IsNotExist(err) {
		// Fallback to `/proc/mounts` if `/proc/1/mounts` is missing due hidepid.
		log.Debugf("Got %q reading root mounts, falling back to system mounts", err)
		file, err = os.Open(procFilePath("mounts"))
	}
	if err != nil {
		return nil, err
	}
	defer file.Close()

	return parseFilesystemLabels(file)
}

func parseFilesystemLabels(r io.Reader) ([]filesystemLabels, error) {
	var filesystems []filesystemLabels

	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		parts := strings.Fields(scanner.Text())

		if len(parts) < 4 {
			return nil, fmt.Errorf("malformed mount point information: %q", scanner.Text())
		}

		// Ensure we handle the translation of \040 and \011
		// as per fstab(5).
		parts[1] = strings.Replace(parts[1], "\\040", " ", -1)
		parts[1] = strings.Replace(parts[1], "\\011", "\t", -1)

		filesystems = append(filesystems, filesystemLabels{
			device:     parts[0],
			mountPoint: parts[1],
			fsType:     parts[2],
			options:    parts[3],
		})
	}

	return filesystems, scanner.Err()
}
<|endoftext|>


<commit_before>package calendar

import (
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
    "os"
    "time"

    "github.com/jesselucas/slackcmd/slack"
    "golang.org/x/net/context"
    "golang.org/x/oauth2"
    "google.golang.org/api/calendar/v3"
)

type easyTime struct {
    time.Time
}

func (t *easyTime) beginningOfHour() time.Time {
    return t.Truncate(time.Hour)
}

func (t *easyTime) beginningOfDay() time.Time {
    d := time.Duration(-t.Hour()) * time.Hour
    return t.beginningOfHour().Add(d)
}

func (t *easyTime) endOfDay() time.Time {
    return t.beginningOfDay().Add(24*time.Hour - time.Nanosecond)
}

type Command struct {
}

func formatForSlack(s string) string {
    return fmt.Sprintf("```\n%v```", s)
}

func getAccessToken(clientID string, clientSecret string, refreshToken string) oauth2.Token {
    formValues := url.Values{
        "client_id": {clientID},
        "client_secret": {clientSecret},
        "refresh_token": {refreshToken},
        "grant_type": {"refresh_token"},
    }
    resp, _ := http.PostForm("https://accounts.google.com/o/oauth2/token", formValues)
    defer resp.Body.Close()

    bodyData, _ := ioutil.ReadAll(resp.Body)
    // body := string(bodyData)

    var token oauth2.Token
    json.Unmarshal(bodyData, &token)
    return token
}

func (cmd Command) Request(sc *slack.SlashCommand) (*slack.CommandPayload, error) {

    // Read the appropriate environment variables
    slackAPIKey := os.Getenv("SLACK_KEY_CALENDAR")
    clientID := os.Getenv("GOOGLE_CALENDAR_CLIENT_ID")
    clientSecret := os.Getenv("GOOGLE_CALENDAR_CLIENT_SECRET")
    refreshToken := os.Getenv("GOOGLE_CALENDAR_REFRESH_TOKEN")
    calendarID := os.Getenv("SLACK_CALENDAR_ID")

    if clientID == "" || clientSecret == "" || refreshToken == "" {
        err := errors.New("Server Configuration Error")
        return nil, err
    }

    // Verify the request is coming from Slack
    if sc.Token != slackAPIKey {
        err := errors.New("Unauthorized Slack")
        return nil, err
    }

    // Create initial payload
    payload := &slack.CommandPayload{
        Channel:       fmt.Sprintf("#%v", sc.ChannelName),
        Username:      "Calendar Bot",
        Emoji:         ":calendar:",
        SendPayload:   false,
        SlashResponse: true,
    }

    // Create the oauth config
    config := oauth2.Config {
        ClientID: clientID,
        ClientSecret: clientSecret,
        Endpoint: oauth2.Endpoint {
            AuthURL: "https://accounts.google.com/o/oauth2/auth",
            TokenURL: "https://accounts.google.com/o/oauth2/token",
        },
        RedirectURL: "http://localhost",
    }

    // Create a client using our config, context, and access token
    context := context.Background()
    token := getAccessToken(clientID, clientSecret, refreshToken)
    client := config.Client(context, &token)

    // Get a calendar service
    service, err := calendar.New(client)
    if err != nil {
        err := errors.New("Unable to retrieve calendar client")
        return nil, err
    }

    // Setup the parameters for our calendar request.
    // We want to request all events from now until the end of today
    now := easyTime{time.Now()}
    timeMin := now.Format(time.RFC3339)
    timeMax := now.endOfDay().Format(time.RFC3339)

    // We want to request this information for a specific calendar ID,
    // which is indicated by the CALENDAR_ID environment variable
    events, err := service.Events.List(calendarID).ShowDeleted(false).SingleEvents(true).TimeMin(timeMin).TimeMax(timeMax).MaxResults(50).OrderBy("startTime").Do()
    if err != nil {
        err := errors.New("Unable to retrieve calendar events.")
        return nil, err
    }

    // Loop through the events received, and append them to the payload text
    payloadText := "Conference Room Schedule: " + time.Now().Format("Jan 2, 2006") + "\n"
    format := "03:04PM"
    if len(events.Items) > 0 {
        for _, i := range events.Items {
            // If the DateTime is an empty string the Event is an all-day Event.
            // So only Date is available.
            var timeString string
            if i.Start.DateTime != "" {
                start, startErr := time.Parse(time.RFC3339, i.Start.DateTime)
                end, endErr := time.Parse(time.RFC3339, i.End.DateTime)
                if startErr == nil && endErr == nil {
                    timeString = start.Local().Format(format) + " to " + end.Local().Format(format)
                } else {
                    timeString = "--------------"
                }
            } else {
                timeString = "All Day       "
            }

            payloadText += fmt.Sprintf("• [%v] <%v|%v>\n", timeString, i.HtmlLink, i.Summary)
        }
    } else {
        payloadText = "No upcoming events found.\n"
    }

    payload.Text = formatForSlack(payloadText)
    return payload, nil
}<commit_msg>Calendar command now accepts a weekday string (sunday, Monday, etc).  The calendar events for the next weekday of that name will be displayed.  If today is that weekday, today's events are displayed.<commit_after>package calendar

import (
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
    "os"
    "time"
    "strings"

    "github.com/jesselucas/slackcmd/slack"
    "golang.org/x/net/context"
    "golang.org/x/oauth2"
    "google.golang.org/api/calendar/v3"
)

type easyTime struct {
    time.Time
}

func (t *easyTime) beginningOfHour() time.Time {
    return t.Truncate(time.Hour)
}

func (t *easyTime) beginningOfDay() time.Time {
    d := time.Duration(-t.Hour()) * time.Hour
    return t.beginningOfHour().Add(d)
}

func (t *easyTime) endOfDay() time.Time {
    return t.beginningOfDay().Add(24*time.Hour - time.Nanosecond)
}

type Command struct {
}

func formatForSlack(s string) string {
    return fmt.Sprintf("```\n%v```", s)
}

func getAccessToken(clientID string, clientSecret string, refreshToken string) oauth2.Token {
    formValues := url.Values{
        "client_id": {clientID},
        "client_secret": {clientSecret},
        "refresh_token": {refreshToken},
        "grant_type": {"refresh_token"},
    }
    resp, _ := http.PostForm("https://accounts.google.com/o/oauth2/token", formValues)
    defer resp.Body.Close()

    bodyData, _ := ioutil.ReadAll(resp.Body)
    // body := string(bodyData)

    var token oauth2.Token
    json.Unmarshal(bodyData, &token)
    return token
}

func getNextWeekdayOccurance(paramString string) time.Time {
    var inputString = strings.TrimSpace(paramString)
    now := time.Now()
    for i:=0; i<7; i++ {
        weekday := time.Weekday(i)
        if strings.ToLower(inputString) == strings.ToLower(weekday.String()) {
            daysUntilRequestedDay := (int(weekday) - int(now.Weekday()) + 7) % 7
            t := easyTime{now.AddDate(0, 0, daysUntilRequestedDay)}
            return t.beginningOfDay()
        }
    }
    return time.Now()
}

func (cmd Command) Request(sc *slack.SlashCommand) (*slack.CommandPayload, error) {

    // Read the appropriate environment variables
    slackAPIKey := os.Getenv("SLACK_KEY_CALENDAR")
    clientID := os.Getenv("GOOGLE_CALENDAR_CLIENT_ID")
    clientSecret := os.Getenv("GOOGLE_CALENDAR_CLIENT_SECRET")
    refreshToken := os.Getenv("GOOGLE_CALENDAR_REFRESH_TOKEN")
    calendarID := os.Getenv("SLACK_CALENDAR_ID")

    if clientID == "" || clientSecret == "" || refreshToken == "" {
        err := errors.New("Server Configuration Error")
        return nil, err
    }

    // Verify the request is coming from Slack
    if sc.Token != slackAPIKey {
        err := errors.New("Unauthorized Slack")
        return nil, err
    }

    // Create initial payload
    payload := &slack.CommandPayload{
        Channel:       fmt.Sprintf("#%v", sc.ChannelName),
        Username:      "Calendar Bot",
        Emoji:         ":calendar:",
        SendPayload:   false,
        SlashResponse: true,
    }

    // Create the oauth config
    config := oauth2.Config {
        ClientID: clientID,
        ClientSecret: clientSecret,
        Endpoint: oauth2.Endpoint {
            AuthURL: "https://accounts.google.com/o/oauth2/auth",
            TokenURL: "https://accounts.google.com/o/oauth2/token",
        },
        RedirectURL: "http://localhost",
    }

    // Create a client using our config, context, and access token
    context := context.Background()
    token := getAccessToken(clientID, clientSecret, refreshToken)
    client := config.Client(context, &token)

    // Get a calendar service
    service, err := calendar.New(client)
    if err != nil {
        err := errors.New("Unable to retrieve calendar client")
        return nil, err
    }

    // Setup the parameters for our calendar request.
    // We want to request all events from the specified
    // date until the end of that day
    requestDate := easyTime{getNextWeekdayOccurance(sc.Text)}
    timeMin := requestDate.Format(time.RFC3339)
    timeMax := requestDate.endOfDay().Format(time.RFC3339)

    // We want to request this information for a specific calendar ID
    events, err := service.Events.List(calendarID).ShowDeleted(false).SingleEvents(true).TimeMin(timeMin).TimeMax(timeMax).MaxResults(50).OrderBy("startTime").Do()
    if err != nil {
        err := errors.New("Unable to retrieve calendar events.")
        return nil, err
    }

    // Loop through the events received, and append them to the payload text
    payloadText := "Conference Room Schedule: " + requestDate.Format("Mon. Jan 2, 2006") + "\n"
    format := "03:04PM"
    if len(events.Items) > 0 {
        for _, i := range events.Items {
            // If the DateTime is an empty string the Event is an all-day Event.
            // So only Date is available.
            var timeString string
            if i.Start.DateTime != "" {
                start, startErr := time.Parse(time.RFC3339, i.Start.DateTime)
                end, endErr := time.Parse(time.RFC3339, i.End.DateTime)
                if startErr == nil && endErr == nil {
                    timeString = start.Local().Format(format) + " to " + end.Local().Format(format)
                } else {
                    timeString = "--------------"
                }
            } else {
                timeString = "All Day       "
            }

            payloadText += fmt.Sprintf("• [%v] <%v|%v>\n", timeString, i.HtmlLink, i.Summary)
        }
    } else {
        payloadText += "• No events scheduled.\n"
    }

    payload.Text = formatForSlack(payloadText)
    return payload, nil
}<|endoftext|>


<commit_before>// Copyright 2015 CNI 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 (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/rpc"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"

	"github.com/containernetworking/cni/pkg/skel"
	current "github.com/containernetworking/cni/pkg/types/100"
	"github.com/coreos/go-systemd/v22/activation"
)

var errNoMoreTries = errors.New("no more tries")

type DHCP struct {
	mux             sync.Mutex
	leases          map[string]*DHCPLease
	hostNetnsPrefix string
	clientTimeout   time.Duration
	clientResendMax time.Duration
	broadcast       bool
}

func newDHCP(clientTimeout, clientResendMax time.Duration) *DHCP {
	return &DHCP{
		leases:          make(map[string]*DHCPLease),
		clientTimeout:   clientTimeout,
		clientResendMax: clientResendMax,
	}
}

// TODO: current client ID is too long. At least the container ID should not be used directly.
// A seperate issue is necessary to ensure no breaking change is affecting other users.
func generateClientID(containerID string, netName string, ifName string) string {
	clientID := containerID + "/" + netName + "/" + ifName
	// defined in RFC 2132, length size can not be larger than 1 octet. So we truncate 254 to make everyone happy.
	if len(clientID) > 254 {
		clientID = clientID[0:254]
	}
	return clientID
}

// Allocate acquires an IP from a DHCP server for a specified container.
// The acquired lease will be maintained until Release() is called.
func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
	conf := NetConf{}
	if err := json.Unmarshal(args.StdinData, &conf); err != nil {
		return fmt.Errorf("error parsing netconf: %v", err)
	}

	optsRequesting, optsProviding, err := prepareOptions(args.Args, conf.IPAM.ProvideOptions, conf.IPAM.RequestOptions)
	if err != nil {
		return err
	}

	clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
	hostNetns := d.hostNetnsPrefix + args.Netns
	l, err := AcquireLease(clientID, hostNetns, args.IfName,
		optsRequesting, optsProviding,
		d.clientTimeout, d.clientResendMax, d.broadcast)
	if err != nil {
		return err
	}

	ipn, err := l.IPNet()
	if err != nil {
		l.Stop()
		return err
	}

	d.setLease(clientID, l)

	result.IPs = []*current.IPConfig{{
		Address: *ipn,
		Gateway: l.Gateway(),
	}}
	result.Routes = l.Routes()

	return nil
}

// Release stops maintenance of the lease acquired in Allocate()
// and sends a release msg to the DHCP server.
func (d *DHCP) Release(args *skel.CmdArgs, reply *struct{}) error {
	conf := NetConf{}
	if err := json.Unmarshal(args.StdinData, &conf); err != nil {
		return fmt.Errorf("error parsing netconf: %v", err)
	}

	clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
	if l := d.getLease(clientID); l != nil {
		l.Stop()
		d.clearLease(clientID)
	}

	return nil
}

func (d *DHCP) getLease(clientID string) *DHCPLease {
	d.mux.Lock()
	defer d.mux.Unlock()

	// TODO(eyakubovich): hash it to avoid collisions
	l, ok := d.leases[clientID]
	if !ok {
		return nil
	}
	return l
}

func (d *DHCP) setLease(clientID string, l *DHCPLease) {
	d.mux.Lock()
	defer d.mux.Unlock()

	// TODO(eyakubovich): hash it to avoid collisions
	d.leases[clientID] = l
}

//func (d *DHCP) clearLease(contID, netName, ifName string) {
func (d *DHCP) clearLease(clientID string) {
	d.mux.Lock()
	defer d.mux.Unlock()

	// TODO(eyakubovich): hash it to avoid collisions
	delete(d.leases, clientID)
}

func getListener(socketPath string) (net.Listener, error) {
	l, err := activation.Listeners()
	if err != nil {
		return nil, err
	}

	switch {
	case len(l) == 0:
		if err := os.MkdirAll(filepath.Dir(socketPath), 0700); err != nil {
			return nil, err
		}
		return net.Listen("unix", socketPath)

	case len(l) == 1:
		if l[0] == nil {
			return nil, fmt.Errorf("LISTEN_FDS=1 but no FD found")
		}
		return l[0], nil

	default:
		return nil, fmt.Errorf("Too many (%v) FDs passed through socket activation", len(l))
	}
}

func runDaemon(
	pidfilePath, hostPrefix, socketPath string,
	dhcpClientTimeout time.Duration, resendMax time.Duration, broadcast bool,
) error {
	// since other goroutines (on separate threads) will change namespaces,
	// ensure the RPC server does not get scheduled onto those
	runtime.LockOSThread()

	// Write the pidfile
	if pidfilePath != "" {
		if !filepath.IsAbs(pidfilePath) {
			return fmt.Errorf("Error writing pidfile %q: path not absolute", pidfilePath)
		}
		if err := ioutil.WriteFile(pidfilePath, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
			return fmt.Errorf("Error writing pidfile %q: %v", pidfilePath, err)
		}
	}

	l, err := getListener(hostPrefix + socketPath)
	if err != nil {
		return fmt.Errorf("Error getting listener: %v", err)
	}

	dhcp := newDHCP(dhcpClientTimeout, resendMax)
	dhcp.hostNetnsPrefix = hostPrefix
	dhcp.broadcast = broadcast
	rpc.Register(dhcp)
	rpc.HandleHTTP()
	http.Serve(l, nil)
	return nil
}
<commit_msg>Cleanup Socket and Pidfile on exit<commit_after>// Copyright 2015 CNI 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 (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/rpc"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"sync"
	"syscall"
	"time"

	"github.com/containernetworking/cni/pkg/skel"
	current "github.com/containernetworking/cni/pkg/types/100"
	"github.com/coreos/go-systemd/v22/activation"
)

var errNoMoreTries = errors.New("no more tries")

type DHCP struct {
	mux             sync.Mutex
	leases          map[string]*DHCPLease
	hostNetnsPrefix string
	clientTimeout   time.Duration
	clientResendMax time.Duration
	broadcast       bool
}

func newDHCP(clientTimeout, clientResendMax time.Duration) *DHCP {
	return &DHCP{
		leases:          make(map[string]*DHCPLease),
		clientTimeout:   clientTimeout,
		clientResendMax: clientResendMax,
	}
}

// TODO: current client ID is too long. At least the container ID should not be used directly.
// A seperate issue is necessary to ensure no breaking change is affecting other users.
func generateClientID(containerID string, netName string, ifName string) string {
	clientID := containerID + "/" + netName + "/" + ifName
	// defined in RFC 2132, length size can not be larger than 1 octet. So we truncate 254 to make everyone happy.
	if len(clientID) > 254 {
		clientID = clientID[0:254]
	}
	return clientID
}

// Allocate acquires an IP from a DHCP server for a specified container.
// The acquired lease will be maintained until Release() is called.
func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
	conf := NetConf{}
	if err := json.Unmarshal(args.StdinData, &conf); err != nil {
		return fmt.Errorf("error parsing netconf: %v", err)
	}

	optsRequesting, optsProviding, err := prepareOptions(args.Args, conf.IPAM.ProvideOptions, conf.IPAM.RequestOptions)
	if err != nil {
		return err
	}

	clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
	hostNetns := d.hostNetnsPrefix + args.Netns
	l, err := AcquireLease(clientID, hostNetns, args.IfName,
		optsRequesting, optsProviding,
		d.clientTimeout, d.clientResendMax, d.broadcast)
	if err != nil {
		return err
	}

	ipn, err := l.IPNet()
	if err != nil {
		l.Stop()
		return err
	}

	d.setLease(clientID, l)

	result.IPs = []*current.IPConfig{{
		Address: *ipn,
		Gateway: l.Gateway(),
	}}
	result.Routes = l.Routes()

	return nil
}

// Release stops maintenance of the lease acquired in Allocate()
// and sends a release msg to the DHCP server.
func (d *DHCP) Release(args *skel.CmdArgs, reply *struct{}) error {
	conf := NetConf{}
	if err := json.Unmarshal(args.StdinData, &conf); err != nil {
		return fmt.Errorf("error parsing netconf: %v", err)
	}

	clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
	if l := d.getLease(clientID); l != nil {
		l.Stop()
		d.clearLease(clientID)
	}

	return nil
}

func (d *DHCP) getLease(clientID string) *DHCPLease {
	d.mux.Lock()
	defer d.mux.Unlock()

	// TODO(eyakubovich): hash it to avoid collisions
	l, ok := d.leases[clientID]
	if !ok {
		return nil
	}
	return l
}

func (d *DHCP) setLease(clientID string, l *DHCPLease) {
	d.mux.Lock()
	defer d.mux.Unlock()

	// TODO(eyakubovich): hash it to avoid collisions
	d.leases[clientID] = l
}

//func (d *DHCP) clearLease(contID, netName, ifName string) {
func (d *DHCP) clearLease(clientID string) {
	d.mux.Lock()
	defer d.mux.Unlock()

	// TODO(eyakubovich): hash it to avoid collisions
	delete(d.leases, clientID)
}

func getListener(socketPath string) (net.Listener, error) {
	l, err := activation.Listeners()
	if err != nil {
		return nil, err
	}

	switch {
	case len(l) == 0:
		if err := os.MkdirAll(filepath.Dir(socketPath), 0700); err != nil {
			return nil, err
		}
		return net.Listen("unix", socketPath)

	case len(l) == 1:
		if l[0] == nil {
			return nil, fmt.Errorf("LISTEN_FDS=1 but no FD found")
		}
		return l[0], nil

	default:
		return nil, fmt.Errorf("Too many (%v) FDs passed through socket activation", len(l))
	}
}

func runDaemon(
	pidfilePath, hostPrefix, socketPath string,
	dhcpClientTimeout time.Duration, resendMax time.Duration, broadcast bool,
) error {
	// since other goroutines (on separate threads) will change namespaces,
	// ensure the RPC server does not get scheduled onto those
	runtime.LockOSThread()

	// Write the pidfile
	if pidfilePath != "" {
		if !filepath.IsAbs(pidfilePath) {
			return fmt.Errorf("Error writing pidfile %q: path not absolute", pidfilePath)
		}
		if err := ioutil.WriteFile(pidfilePath, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
			return fmt.Errorf("Error writing pidfile %q: %v", pidfilePath, err)
		}
	}

	l, err := getListener(hostPrefix + socketPath)
	if err != nil {
		return fmt.Errorf("Error getting listener: %v", err)
	}

	srv := http.Server{}
	exit := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(exit, os.Interrupt, syscall.SIGTERM)

	go func() {
		<-exit
		srv.Shutdown(context.TODO())
		os.Remove(hostPrefix + socketPath)
		os.Remove(pidfilePath)

		done <- true
	}()

	dhcp := newDHCP(dhcpClientTimeout, resendMax)
	dhcp.hostNetnsPrefix = hostPrefix
	dhcp.broadcast = broadcast
	rpc.Register(dhcp)
	rpc.HandleHTTP()
	srv.Serve(l)

	<-done
	return nil
}
<|endoftext|>


<commit_before>// Code generated by protoc-gen-go.
// source: examples/greeter/server/proto/hello/hello.proto
// DO NOT EDIT!

/*
Package go_micro_srv_greeter is a generated protocol buffer package.

It is generated from these files:
	examples/greeter/server/proto/hello/hello.proto

It has these top-level messages:
	Request
	Response
*/
package go_micro_srv_greeter

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

import (
	client "github.com/micro/go-micro/client"
	server "github.com/micro/go-micro/server"
	context "golang.org/x/net/context"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

type Request struct {
	Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}

func (m *Request) Reset()                    { *m = Request{} }
func (m *Request) String() string            { return proto.CompactTextString(m) }
func (*Request) ProtoMessage()               {}
func (*Request) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }

type Response struct {
	Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}

func (m *Response) Reset()                    { *m = Response{} }
func (m *Response) String() string            { return proto.CompactTextString(m) }
func (*Response) ProtoMessage()               {}
func (*Response) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }

func init() {
	proto.RegisterType((*Request)(nil), "go.micro.srv.greeter.Request")
	proto.RegisterType((*Response)(nil), "go.micro.srv.greeter.Response")
}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ client.Option
var _ server.Option

// Client API for Say service

type SayClient interface {
	Hello(ctx context.Context, in *Request) (*Response, error)
}

type sayClient struct {
	c           client.Client
	serviceName string
}

func NewSayClient(serviceName string, c client.Client) SayClient {
	if c == nil {
		c = client.NewClient()
	}
	if len(serviceName) == 0 {
		serviceName = "go.micro.srv.greeter"
	}
	return &sayClient{
		c:           c,
		serviceName: serviceName,
	}
}

func (c *sayClient) Hello(ctx context.Context, in *Request) (*Response, error) {
	req := c.c.NewRequest(c.serviceName, "Say.Hello", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// Server API for Say service

type SayHandler interface {
	Hello(context.Context, *Request, *Response) error
}

func RegisterSayHandler(s server.Server, hdlr SayHandler) {
	s.Handle(s.NewHandler(hdlr))
}

var fileDescriptor0 = []byte{
	// 161 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xd2, 0x4f, 0xad, 0x48, 0xcc,
	0x2d, 0xc8, 0x49, 0x2d, 0xd6, 0x4f, 0x2f, 0x4a, 0x4d, 0x2d, 0x49, 0x2d, 0xd2, 0x2f, 0x4e, 0x2d,
	0x2a, 0x03, 0x52, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0xfa, 0x19, 0xa9, 0x39, 0x39, 0x50, 0x52, 0x0f,
	0x2c, 0x22, 0x24, 0x92, 0x9e, 0xaf, 0x97, 0x9b, 0x99, 0x5c, 0x94, 0xaf, 0x57, 0x5c, 0x54, 0xa6,
	0x07, 0xd5, 0xa4, 0x24, 0xce, 0xc5, 0x1e, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x22, 0xc4, 0xc3,
	0xc5, 0x92, 0x97, 0x98, 0x9b, 0x2a, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x09, 0x94, 0xe0, 0x08, 0x4a,
	0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x15, 0xe2, 0xe6, 0x62, 0xce, 0x2d, 0x4e, 0x87, 0x48, 0x18,
	0xf9, 0x73, 0x31, 0x07, 0x27, 0x56, 0x0a, 0x79, 0x70, 0xb1, 0x7a, 0x80, 0x4c, 0x17, 0x92, 0xd5,
	0xc3, 0x66, 0xb0, 0x1e, 0xd4, 0x54, 0x29, 0x39, 0x5c, 0xd2, 0x10, 0xb3, 0x95, 0x18, 0x92, 0xd8,
	0xc0, 0xee, 0x33, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x03, 0x56, 0x54, 0x80, 0xd2, 0x00, 0x00,
	0x00,
}
<commit_msg>Update proto<commit_after>// Code generated by protoc-gen-go.
// source: examples/greeter/server/proto/hello/hello.proto
// DO NOT EDIT!

/*
Package go_micro_srv_greeter is a generated protocol buffer package.

It is generated from these files:
	examples/greeter/server/proto/hello/hello.proto

It has these top-level messages:
	Request
	Response
*/
package go_micro_srv_greeter

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

import (
	client "github.com/micro/go-micro/client"
	server "github.com/micro/go-micro/server"
	context "golang.org/x/net/context"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

type Request struct {
	Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}

func (m *Request) Reset()                    { *m = Request{} }
func (m *Request) String() string            { return proto.CompactTextString(m) }
func (*Request) ProtoMessage()               {}
func (*Request) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }

type Response struct {
	Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}

func (m *Response) Reset()                    { *m = Response{} }
func (m *Response) String() string            { return proto.CompactTextString(m) }
func (*Response) ProtoMessage()               {}
func (*Response) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }

func init() {
	proto.RegisterType((*Request)(nil), "go.micro.srv.greeter.Request")
	proto.RegisterType((*Response)(nil), "go.micro.srv.greeter.Response")
}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ client.Option
var _ server.Option

// Client API for Say service

type SayClient interface {
	Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
}

type sayClient struct {
	c           client.Client
	serviceName string
}

func NewSayClient(serviceName string, c client.Client) SayClient {
	if c == nil {
		c = client.NewClient()
	}
	if len(serviceName) == 0 {
		serviceName = "go.micro.srv.greeter"
	}
	return &sayClient{
		c:           c,
		serviceName: serviceName,
	}
}

func (c *sayClient) Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
	req := c.c.NewRequest(c.serviceName, "Say.Hello", in)
	out := new(Response)
	err := c.c.Call(ctx, req, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// Server API for Say service

type SayHandler interface {
	Hello(context.Context, *Request, *Response) error
}

func RegisterSayHandler(s server.Server, hdlr SayHandler) {
	s.Handle(s.NewHandler(hdlr))
}

var fileDescriptor0 = []byte{
	// 161 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xd2, 0x4f, 0xad, 0x48, 0xcc,
	0x2d, 0xc8, 0x49, 0x2d, 0xd6, 0x4f, 0x2f, 0x4a, 0x4d, 0x2d, 0x49, 0x2d, 0xd2, 0x2f, 0x4e, 0x2d,
	0x2a, 0x03, 0x52, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0xfa, 0x19, 0xa9, 0x39, 0x39, 0x50, 0x52, 0x0f,
	0x2c, 0x22, 0x24, 0x92, 0x9e, 0xaf, 0x97, 0x9b, 0x99, 0x5c, 0x94, 0xaf, 0x57, 0x5c, 0x54, 0xa6,
	0x07, 0xd5, 0xa4, 0x24, 0xce, 0xc5, 0x1e, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x22, 0xc4, 0xc3,
	0xc5, 0x92, 0x97, 0x98, 0x9b, 0x2a, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x09, 0x94, 0xe0, 0x08, 0x4a,
	0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x15, 0xe2, 0xe6, 0x62, 0xce, 0x2d, 0x4e, 0x87, 0x48, 0x18,
	0xf9, 0x73, 0x31, 0x07, 0x27, 0x56, 0x0a, 0x79, 0x70, 0xb1, 0x7a, 0x80, 0x4c, 0x17, 0x92, 0xd5,
	0xc3, 0x66, 0xb0, 0x1e, 0xd4, 0x54, 0x29, 0x39, 0x5c, 0xd2, 0x10, 0xb3, 0x95, 0x18, 0x92, 0xd8,
	0xc0, 0xee, 0x33, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x03, 0x56, 0x54, 0x80, 0xd2, 0x00, 0x00,
	0x00,
}
<|endoftext|>


<commit_before>// 25 july 2014

package ui

import (
	"unsafe"
)

// #include "winapi_windows.h"
import "C"

/*
On Windows, container controls are just regular controls; their children have to be children of the parent window, and changing the contents of a switching container (such as a tab control) must be done manually.

We'll create a dummy window using the pre-existing Window window class for each tab page. This makes showing and hiding tabs a matter of showing and hiding one control.

TODO
- make sure all tabs cannot be deselected (that is, make sure the current tab can never have index -1)
*/

type tab struct {
	_hwnd	C.HWND
	tabs		[]*container
	parent	*controlParent
}

func newTab() Tab {
	hwnd := C.newControl(C.xWC_TABCONTROL,
		C.TCS_TOOLTIPS | C.WS_TABSTOP,
		0)
	t := &tab{
		_hwnd:	hwnd,
	}
	C.controlSetControlFont(t._hwnd)
	C.setTabSubclass(t._hwnd, unsafe.Pointer(t))
	return t
}

func (t *tab) Append(name string, control Control) {
	c := newContainer(control)
	t.tabs = append(t.tabs, c)
	if t.parent != nil {
		c.setParent(t.parent)
	}
	// initially hide tab 1..n controls; if we don't, they'll appear over other tabs, resulting in weird behavior
	if len(t.tabs) != 1 {
		t.tabs[len(t.tabs) - 1].hide()
	}
	C.tabAppend(t._hwnd, toUTF16(name))
}

//export tabChanging
func tabChanging(data unsafe.Pointer, current C.LRESULT) {
	t := (*tab)(data)
	t.tabs[int(current)].hide()
}

//export tabChanged
func tabChanged(data unsafe.Pointer, new C.LRESULT) {
	t := (*tab)(data)
	t.tabs[int(new)].show()
}

func (t *tab) hwnd() C.HWND {
	return t._hwnd
}

func (t *tab) setParent(p *controlParent) {
	basesetParent(t, p)
	for _, c := range t.tabs {
		c.setParent(p)
	}
	t.parent = p
}

func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
	return baseallocate(t, x, y, width, height, d)
}

func (t *tab) preferredSize(d *sizing) (width, height int) {
	// TODO only consider the size of the current tab?
	for _, s := range t.tabs {
		w, h := s.child.preferredSize(d)
		if width < w {
			width = w
		}
		if height < h {
			height = h
		}
	}
	return width, height + int(C.tabGetTabHeight(t._hwnd))
}

// a tab control contains other controls; size appropriately
func (t *tab) commitResize(c *allocation, d *sizing) {
	var r C.RECT

	// figure out what the rect for each child is...
	r.left = C.LONG(c.x)				// load structure with the window's rect
	r.top = C.LONG(c.y)
	r.right = C.LONG(c.x + c.width)
	r.bottom = C.LONG(c.y + c.height)
	C.tabGetContentRect(t._hwnd, &r)
	// and resize tabs
	// don't resize just the current tab; resize all tabs!
	for _, c := range t.tabs {
		// because each widget is actually a child of the Window, the origin is the one we calculated above
		c.move(&r)
	}
	// and now resize the tab control itself
	basecommitResize(t, c, d)
}

func (t *tab) getAuxResizeInfo(d *sizing) {
	basegetAuxResizeInfo(t, d)
}
<commit_msg>Made the tabs of Tab controls on Windows children of the main tab control itself; this will let us be transparent... hopefully.<commit_after>// 25 july 2014

package ui

import (
	"unsafe"
)

// #include "winapi_windows.h"
import "C"

/*
On Windows, container controls are just regular controls that notify their parent when the user wants to do things; changing the contents of a switching container (such as a tab control) must be done manually.

We'll create a dummy window using the pre-existing Window window class for each tab page. This makes showing and hiding tabs a matter of showing and hiding one control.

TODO
- make sure all tabs cannot be deselected (that is, make sure the current tab can never have index -1)
*/

type tab struct {
	_hwnd	C.HWND
	tabs		[]*container
}

func newTab() Tab {
	hwnd := C.newControl(C.xWC_TABCONTROL,
		C.TCS_TOOLTIPS | C.WS_TABSTOP,
		0)
	t := &tab{
		_hwnd:	hwnd,
	}
	C.controlSetControlFont(t._hwnd)
	C.setTabSubclass(t._hwnd, unsafe.Pointer(t))
	return t
}

func (t *tab) Append(name string, control Control) {
	c := newContainer(control)
	c.setParent(&controlParent{t._hwnd})
	t.tabs = append(t.tabs, c)
	// initially hide tab 1..n controls; if we don't, they'll appear over other tabs, resulting in weird behavior
	if len(t.tabs) != 1 {
		t.tabs[len(t.tabs) - 1].hide()
	}
	C.tabAppend(t._hwnd, toUTF16(name))
}

//export tabChanging
func tabChanging(data unsafe.Pointer, current C.LRESULT) {
	t := (*tab)(data)
	t.tabs[int(current)].hide()
}

//export tabChanged
func tabChanged(data unsafe.Pointer, new C.LRESULT) {
	t := (*tab)(data)
	t.tabs[int(new)].show()
}

func (t *tab) hwnd() C.HWND {
	return t._hwnd
}

func (t *tab) setParent(p *controlParent) {
	basesetParent(t, p)
}

func (t *tab) allocate(x int, y int, width int, height int, d *sizing) []*allocation {
	return baseallocate(t, x, y, width, height, d)
}

func (t *tab) preferredSize(d *sizing) (width, height int) {
	// TODO only consider the size of the current tab?
	for _, s := range t.tabs {
		w, h := s.child.preferredSize(d)
		if width < w {
			width = w
		}
		if height < h {
			height = h
		}
	}
	return width, height + int(C.tabGetTabHeight(t._hwnd))
}

// a tab control contains other controls; size appropriately
func (t *tab) commitResize(c *allocation, d *sizing) {
	var r C.RECT

	// figure out what the rect for each child is...
	// the tab contents are children of the tab itself, so ignore c.x and c.y, which are relative to the window!
	r.left = C.LONG(0)
	r.top = C.LONG(0)
	r.right = C.LONG(c.width)
	r.bottom = C.LONG(c.height)
println(r.left, r.top, r.right, r.bottom)
	C.tabGetContentRect(t._hwnd, &r)
	// and resize tabs
	// don't resize just the current tab; resize all tabs!
println(r.left, r.top, r.right, r.bottom)
	for _, c := range t.tabs {
		// because each widget is actually a child of the Window, the origin is the one we calculated above
		c.move(&r)
	}
	// and now resize the tab control itself
	basecommitResize(t, c, d)
}

func (t *tab) getAuxResizeInfo(d *sizing) {
	basegetAuxResizeInfo(t, d)
}
<|endoftext|>


<commit_before>package regex_test

import (
	"fmt"
	"github.com/hydroo/gomochex/automaton/nfa"
	"github.com/hydroo/gomochex/basic/set"
	"github.com/hydroo/gomochex/regex"
	"testing"
)

func TestExpressionFromString(t *testing.T) {

	//correct concatenation
	if e, ok := regex.ExpressionFromString("(asdf.π)"); ok != true || fmt.Sprint(e) != "(asdf.π)" {
		t.Error()
	}

	//correct letter
	if e, ok := regex.ExpressionFromString("π"); ok != true || fmt.Sprint(e) != "π" {
		t.Error()
	}

	//wrong letter
	if _, ok := regex.ExpressionFromString("π."); ok != false {
		t.Error()
	}

	//correct letter
	if e, ok := regex.ExpressionFromString("πasdf"); ok != true || fmt.Sprint(e) != "πasdf" {
		t.Error()
	}

	//correct or
	if e, ok := regex.ExpressionFromString("(asdf+π)"); ok != true || fmt.Sprint(e) != "(asdf+π)" {
		t.Error()
	}

	//wrong or
	if _, ok := regex.ExpressionFromString("((asdf+π)"); ok != false {
		t.Error()
	}

	//correct star
	if e, ok := regex.ExpressionFromString("((asdf+π))*"); ok != true || fmt.Sprint(e) != "((asdf+π))*" {
		t.Error()
	}

	//wrong star
	if _, ok := regex.ExpressionFromString("π*"); ok != false {
		t.Error()
	}

	//correct complex expr
	if e, ok := regex.ExpressionFromString("(a.((π+b).(c)*))"); ok != true || fmt.Sprint(e) != "(a.((π+b).(c)*))" {
		t.Error()
	}

}

func TestConcatNfa(t *testing.T) {
	a := nfa.Letter("a")
	b := nfa.Letter("π")
	c := nfa.Letter("c")

	A := regex.Concat(regex.Letter("a"), regex.Or(regex.Letter("π"), regex.Letter("c"))).Nfa()

	if A.Alphabet().Size() != 3 || A.States().Size() < 5 || A.States().Size() > 6 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 2 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true || A.Alphabet().Probe(b) != true || A.Alphabet().Probe(c) != true {
		t.Error()
	}

	//       -- a --> o -- π --> □
	//      /
	// --> o
	//      \
	//       -- a --> o -- c --> □
	transitionCount := 0
	var lastLetter set.Element = nil //makes sure no two π or c transitions occur
	visitedRStates := set.NewSet()   //makes sure the two π and c transitions use entirely different states
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() == 2 { // init -- a --> 2x
				u, _ := S.At(0)
				v, _ := S.At(1)
				if A.InitialStates().Probe(s) != true || A.InitialStates().Probe(u) != false || A.InitialStates().Probe(v) != false || A.FinalStates().Probe(s) != false || A.FinalStates().Probe(u) != false || A.FinalStates().Probe(v) != false || x.IsEqual(a) != true {
					t.Error()
				}
			} else if S.Size() == 1 { // o -- c --> □ or o -- π --> □
				u, _ := S.At(0)

				if x.IsEqual(b) == false && x.IsEqual(c) == false {
					t.Error()
				}

				if A.InitialStates().Probe(s) != false || A.FinalStates().Probe(s) != false || A.InitialStates().Probe(u) != false || A.FinalStates().Probe(u) != true {
					t.Error()
				}

				// do not visit c two times, or π two times
				if lastLetter == nil {
					lastLetter = x
				} else {
					if lastLetter.IsEqual(x) != false {
						t.Error()
					}
				}

				visitedRStates.Add(s, u)
			}

			transitionCount += 1
		}
	}

	if transitionCount != 3 || visitedRStates.Size() != 4 {
		t.Error()
	}
}

func TestConcatNfa2(t *testing.T) {
	a := nfa.Letter("a")

	A := regex.Concat(regex.Letter("a"), regex.Letter("a")).Nfa()

	if A.Alphabet().Size() != 1 || A.States().Size() < 3 || A.States().Size() > 4 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 1 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true {
		t.Error()
	}

	q0_, _ := A.InitialStates().At(0)
	q0 := q0_.(nfa.State)

	Q0 := A.Transition(q0, a)
	if Q0.Size() < 1 || Q0.Size() > 2 {
		t.Error()
	}

	q1_, _ := Q0.At(0)
	q1 := q1_.(nfa.State)
	if A.Transition(q1, a).Size() == 0 {
		q1_, _ = Q0.At(1)
		q1 = q1_.(nfa.State)
	}

	Q1 := A.Transition(q1, a)
	if Q1.Size() != 1 {
		t.Error()
	}

	if set.Intersect(Q1, A.FinalStates()).Size() != 1 {
		t.Error()
	}
}

func TestLetterNfa(t *testing.T) {

	a := nfa.Letter("a")

	A := regex.Letter("a").Nfa()

	if A.Alphabet().Size() != 1 || A.States().Size() != 2 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 1 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true {
		t.Error()
	}

	//has exactly one transition which is not a loop,
	//and goes from an initial to a final state
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() != 1 {
				t.Error()
			}

			u, _ := S.At(0)

			if s.IsEqual(u) || A.InitialStates().Probe(s) != true || A.FinalStates().Probe(s) != false || A.FinalStates().Probe(u) != true || A.InitialStates().Probe(u) != false {
				t.Error()
			}
		}
	}
}

func TestOrNfa(t *testing.T) {
	a := nfa.Letter("a")
	b := nfa.Letter("π")

	A := regex.Or(regex.Letter("a"), regex.Letter("π")).Nfa()

	if A.Alphabet().Size() != 2 || A.States().Size() != 4 || A.InitialStates().Size() != 2 || A.FinalStates().Size() != 2 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true || A.Alphabet().Probe(b) != true {
		t.Error()
	}

	//has exactly two transitions which are not loops,
	//both go from an initial to a final state,
	//and use two different letters
	transitionCount := 0
	var lastLetter set.Element
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() != 1 {
				t.Error()
			}

			u, _ := S.At(0)

			if s.IsEqual(u) || A.InitialStates().Probe(s) != true || A.FinalStates().Probe(s) != false || A.FinalStates().Probe(u) != true || A.InitialStates().Probe(u) != false {
				t.Error()
			}

			if transitionCount == 0 {
				lastLetter = x
			} else if transitionCount == 1 {
				if lastLetter.IsEqual(x) {
					t.Error()
				}
			}

			transitionCount += 1
		}
	}

	if transitionCount != 2 {
		t.Error()
	}
}

func TestStarNfa(t *testing.T) {

	a := nfa.Letter("a")
	b := nfa.Letter("π")

	A := regex.Star(regex.Or(regex.Concat(regex.Letter("a"), regex.Letter("a")), regex.Concat(regex.Letter("π"), regex.Letter("π")))).Nfa()

	// the maximal nfa has 9 states and a lot of transitions to useless states
	//
	// minimal nfa:
	//
	// +-- a ---
	// |        \
	// | - a -> o
	// v/
	// □
	// ^\
	// | - π -> o
	// |       /
	// +-- π --

	if A.Alphabet().Size() != 2 || A.States().Size() < 3 || A.States().Size() > 9 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 1 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true || A.Alphabet().Probe(b) != true {
		t.Error()
	}
	if A.InitialStates().IsEqual(A.FinalStates()) != true {
		t.Error()
	}

	q0_, _ := A.InitialStates().At(0)
	q0 := q0_.(nfa.State)

	if A.Transition(q0, a).Size() < 1 || A.Transition(q0, a).Size() > 2 || A.Transition(q0, b).Size() < 1 || A.Transition(q0, b).Size() > 2 {
		t.Error()
	}

	q1_, _ := A.Transition(q0, a).At(0)
	q1 := q1_.(nfa.State)
	if A.Transition(q1, a).Size() == 0 {
		q1_, _ = A.Transition(q0, a).At(1)
		q1 = q1_.(nfa.State)
	}

	q2_, _ := A.Transition(q0, b).At(0)
	q2 := q2_.(nfa.State)
	if A.Transition(q2, b).Size() == 0 {
		q2_, _ = A.Transition(q0, b).At(1)
		q2 = q2_.(nfa.State)
	}

	if A.Transition(q1, a).Size() < 1 || A.Transition(q1, a).Size() > 3 || A.Transition(q2, b).Size() < 1 || A.Transition(q2, b).Size() > 3 {
		t.Error()
	}

	Q1 := A.Transition(q1, a)
	Q2 := A.Transition(q2, b)

	if Q1.Probe(q0) == false || Q2.Probe(q0) == false {
		t.Error()
	}

	var q10, q20 nfa.State

	if q, _ := Q1.At(0); q.IsEqual(q0) {
		q10_, _ := Q1.At(1)
		q10 = q10_.(nfa.State)
	} else {
		q10_, _ := Q1.At(0)
		q10 = q10_.(nfa.State)
	}
	if q, _ := Q2.At(0); q.IsEqual(q0) {
		q20_, _ := Q2.At(1)
		q20 = q20_.(nfa.State)
	} else {
		q20_, _ := Q2.At(0)
		q20 = q20_.(nfa.State)
	}

	if A.Transition(q10, a).Size() != 0 || A.Transition(q10, b).Size() != 0 || A.Transition(q20, a).Size() != 0 || A.Transition(q20, b).Size() != 0 {
		t.Error()
	}
}
<commit_msg>change regex parsing tests into something independent from print out style<commit_after>package regex_test

import (
	//"fmt"
	"github.com/hydroo/gomochex/automaton/nfa"
	"github.com/hydroo/gomochex/basic/set"
	"github.com/hydroo/gomochex/regex"
	"testing"
)

func TestExpressionFromString(t *testing.T) {

	//correct concatenation
	if e, ok := regex.ExpressionFromString("(asdf.π)"); ok != true || e.IsEqual(regex.Concat(regex.Letter("asdf"), regex.Letter("π"))) != true {
		t.Error()
	}

	//correct letter
	if e, ok := regex.ExpressionFromString("π"); ok != true || e.IsEqual(regex.Letter("π")) != true {
		t.Error()
	}

	//wrong letter
	if _, ok := regex.ExpressionFromString("π."); ok != false {
		t.Error()
	}

	//correct letter
	if e, ok := regex.ExpressionFromString("πasdf"); ok != true || e.IsEqual(regex.Letter("πasdf")) != true {
		t.Error()
	}

	//correct or
	if e, ok := regex.ExpressionFromString("(asdf+π)"); ok != true || e.IsEqual(regex.Or(regex.Letter("asdf"), regex.Letter("π"))) != true {
		t.Error()
	}

	//wrong or
	if _, ok := regex.ExpressionFromString("((asdf+π)"); ok != false {
		t.Error()
	}

	//correct star
	if e, ok := regex.ExpressionFromString("((asdf+π))*"); ok != true || e.IsEqual(regex.Star(regex.Or(regex.Letter("asdf"), regex.Letter("π")))) != true {
		t.Error()
	}

	//wrong star
	if _, ok := regex.ExpressionFromString("π*"); ok != false {
		t.Error()
	}

	//correct complex expr
	if e, ok := regex.ExpressionFromString("(a.((π+b).(c)*))"); ok != true || e.IsEqual(regex.Concat(regex.Letter("a"), regex.Concat(regex.Or(regex.Letter("π"), regex.Letter("b")), regex.Star(regex.Letter("c"))))) != true {
		t.Error()
	}

}

func TestConcatNfa(t *testing.T) {
	a := nfa.Letter("a")
	b := nfa.Letter("π")
	c := nfa.Letter("c")

	A := regex.Concat(regex.Letter("a"), regex.Or(regex.Letter("π"), regex.Letter("c"))).Nfa()

	if A.Alphabet().Size() != 3 || A.States().Size() < 5 || A.States().Size() > 6 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 2 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true || A.Alphabet().Probe(b) != true || A.Alphabet().Probe(c) != true {
		t.Error()
	}

	//       -- a --> o -- π --> □
	//      /
	// --> o
	//      \
	//       -- a --> o -- c --> □
	transitionCount := 0
	var lastLetter set.Element = nil //makes sure no two π or c transitions occur
	visitedRStates := set.NewSet()   //makes sure the two π and c transitions use entirely different states
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() == 2 { // init -- a --> 2x
				u, _ := S.At(0)
				v, _ := S.At(1)
				if A.InitialStates().Probe(s) != true || A.InitialStates().Probe(u) != false || A.InitialStates().Probe(v) != false || A.FinalStates().Probe(s) != false || A.FinalStates().Probe(u) != false || A.FinalStates().Probe(v) != false || x.IsEqual(a) != true {
					t.Error()
				}
			} else if S.Size() == 1 { // o -- c --> □ or o -- π --> □
				u, _ := S.At(0)

				if x.IsEqual(b) == false && x.IsEqual(c) == false {
					t.Error()
				}

				if A.InitialStates().Probe(s) != false || A.FinalStates().Probe(s) != false || A.InitialStates().Probe(u) != false || A.FinalStates().Probe(u) != true {
					t.Error()
				}

				// do not visit c two times, or π two times
				if lastLetter == nil {
					lastLetter = x
				} else {
					if lastLetter.IsEqual(x) != false {
						t.Error()
					}
				}

				visitedRStates.Add(s, u)
			}

			transitionCount += 1
		}
	}

	if transitionCount != 3 || visitedRStates.Size() != 4 {
		t.Error()
	}
}

func TestConcatNfa2(t *testing.T) {
	a := nfa.Letter("a")

	A := regex.Concat(regex.Letter("a"), regex.Letter("a")).Nfa()

	if A.Alphabet().Size() != 1 || A.States().Size() < 3 || A.States().Size() > 4 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 1 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true {
		t.Error()
	}

	q0_, _ := A.InitialStates().At(0)
	q0 := q0_.(nfa.State)

	Q0 := A.Transition(q0, a)
	if Q0.Size() < 1 || Q0.Size() > 2 {
		t.Error()
	}

	q1_, _ := Q0.At(0)
	q1 := q1_.(nfa.State)
	if A.Transition(q1, a).Size() == 0 {
		q1_, _ = Q0.At(1)
		q1 = q1_.(nfa.State)
	}

	Q1 := A.Transition(q1, a)
	if Q1.Size() != 1 {
		t.Error()
	}

	if set.Intersect(Q1, A.FinalStates()).Size() != 1 {
		t.Error()
	}
}

func TestLetterNfa(t *testing.T) {

	a := nfa.Letter("a")

	A := regex.Letter("a").Nfa()

	if A.Alphabet().Size() != 1 || A.States().Size() != 2 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 1 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true {
		t.Error()
	}

	//has exactly one transition which is not a loop,
	//and goes from an initial to a final state
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() != 1 {
				t.Error()
			}

			u, _ := S.At(0)

			if s.IsEqual(u) || A.InitialStates().Probe(s) != true || A.FinalStates().Probe(s) != false || A.FinalStates().Probe(u) != true || A.InitialStates().Probe(u) != false {
				t.Error()
			}
		}
	}
}

func TestOrNfa(t *testing.T) {
	a := nfa.Letter("a")
	b := nfa.Letter("π")

	A := regex.Or(regex.Letter("a"), regex.Letter("π")).Nfa()

	if A.Alphabet().Size() != 2 || A.States().Size() != 4 || A.InitialStates().Size() != 2 || A.FinalStates().Size() != 2 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true || A.Alphabet().Probe(b) != true {
		t.Error()
	}

	//has exactly two transitions which are not loops,
	//both go from an initial to a final state,
	//and use two different letters
	transitionCount := 0
	var lastLetter set.Element
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() != 1 {
				t.Error()
			}

			u, _ := S.At(0)

			if s.IsEqual(u) || A.InitialStates().Probe(s) != true || A.FinalStates().Probe(s) != false || A.FinalStates().Probe(u) != true || A.InitialStates().Probe(u) != false {
				t.Error()
			}

			if transitionCount == 0 {
				lastLetter = x
			} else if transitionCount == 1 {
				if lastLetter.IsEqual(x) {
					t.Error()
				}
			}

			transitionCount += 1
		}
	}

	if transitionCount != 2 {
		t.Error()
	}
}

func TestStarNfa(t *testing.T) {

	a := nfa.Letter("a")
	b := nfa.Letter("π")

	A := regex.Star(regex.Or(regex.Concat(regex.Letter("a"), regex.Letter("a")), regex.Concat(regex.Letter("π"), regex.Letter("π")))).Nfa()

	// the maximal nfa has 9 states and a lot of transitions to useless states
	//
	// minimal nfa:
	//
	// +-- a ---
	// |        \
	// | - a -> o
	// v/
	// □
	// ^\
	// | - π -> o
	// |       /
	// +-- π --

	if A.Alphabet().Size() != 2 || A.States().Size() < 3 || A.States().Size() > 9 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 1 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true || A.Alphabet().Probe(b) != true {
		t.Error()
	}
	if A.InitialStates().IsEqual(A.FinalStates()) != true {
		t.Error()
	}

	q0_, _ := A.InitialStates().At(0)
	q0 := q0_.(nfa.State)

	if A.Transition(q0, a).Size() < 1 || A.Transition(q0, a).Size() > 2 || A.Transition(q0, b).Size() < 1 || A.Transition(q0, b).Size() > 2 {
		t.Error()
	}

	q1_, _ := A.Transition(q0, a).At(0)
	q1 := q1_.(nfa.State)
	if A.Transition(q1, a).Size() == 0 {
		q1_, _ = A.Transition(q0, a).At(1)
		q1 = q1_.(nfa.State)
	}

	q2_, _ := A.Transition(q0, b).At(0)
	q2 := q2_.(nfa.State)
	if A.Transition(q2, b).Size() == 0 {
		q2_, _ = A.Transition(q0, b).At(1)
		q2 = q2_.(nfa.State)
	}

	if A.Transition(q1, a).Size() < 1 || A.Transition(q1, a).Size() > 3 || A.Transition(q2, b).Size() < 1 || A.Transition(q2, b).Size() > 3 {
		t.Error()
	}

	Q1 := A.Transition(q1, a)
	Q2 := A.Transition(q2, b)

	if Q1.Probe(q0) == false || Q2.Probe(q0) == false {
		t.Error()
	}

	var q10, q20 nfa.State

	if q, _ := Q1.At(0); q.IsEqual(q0) {
		q10_, _ := Q1.At(1)
		q10 = q10_.(nfa.State)
	} else {
		q10_, _ := Q1.At(0)
		q10 = q10_.(nfa.State)
	}
	if q, _ := Q2.At(0); q.IsEqual(q0) {
		q20_, _ := Q2.At(1)
		q20 = q20_.(nfa.State)
	} else {
		q20_, _ := Q2.At(0)
		q20 = q20_.(nfa.State)
	}

	if A.Transition(q10, a).Size() != 0 || A.Transition(q10, b).Size() != 0 || A.Transition(q20, a).Size() != 0 || A.Transition(q20, b).Size() != 0 {
		t.Error()
	}
}
<|endoftext|>


<commit_before>package regex_test

import (
	"fmt"
	"github.com/hydroo/gomochex/automaton/nfa"
	"github.com/hydroo/gomochex/basic/set"
	"github.com/hydroo/gomochex/regex"
	"testing"
)

func TestExpressionFromString(t *testing.T) {

	//correct concatenation
	if e, ok := regex.ExpressionFromString("(asdf.π)"); ok != true || fmt.Sprint(e) != "(asdf.π)" {
		t.Error()
	}

	//correct letter
	if e, ok := regex.ExpressionFromString("π"); ok != true || fmt.Sprint(e) != "π" {
		t.Error()
	}

	//wrong letter
	if _, ok := regex.ExpressionFromString("π."); ok != false {
		t.Error()
	}

	//correct letter
	if e, ok := regex.ExpressionFromString("πasdf"); ok != true || fmt.Sprint(e) != "πasdf" {
		t.Error()
	}

	//correct or
	if e, ok := regex.ExpressionFromString("(asdf+π)"); ok != true || fmt.Sprint(e) != "(asdf+π)" {
		t.Error()
	}

	//wrong or
	if _, ok := regex.ExpressionFromString("((asdf+π)"); ok != false {
		t.Error()
	}

	//correct star
	if e, ok := regex.ExpressionFromString("((asdf+π))*"); ok != true || fmt.Sprint(e) != "((asdf+π))*" {
		t.Error()
	}

	//wrong star
	if _, ok := regex.ExpressionFromString("π*"); ok != false {
		t.Error()
	}

	//correct complex expr
	if e, ok := regex.ExpressionFromString("(a.((π+b).(c)*))"); ok != true || fmt.Sprint(e) != "(a.((π+b).(c)*))" {
		t.Error()
	}

}

func TestConcatNfa(t *testing.T) {
	//TODO
}

func TestLetterNfa(t *testing.T) {

	a := nfa.Letter("a")

	A := regex.Letter("a").Nfa()

	if A.Alphabet().Size() != 1 || A.States().Size() != 2 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 1 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true {
		t.Error()
	}

	//has exactly one transition which is not a loop,
	//and goes from an initial to a final state
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() != 1 {
				t.Error()
			}

			u, _ := S.At(0)

			if s.IsEqual(u) || A.InitialStates().Probe(s) == false || A.FinalStates().Probe(u) == false {
				t.Error()
			}
		}
	}
}

func TestOrNfa(t *testing.T) {
	a := nfa.Letter("a")
	b := nfa.Letter("π")

	A := regex.Or(regex.Letter("a"), regex.Letter("π")).Nfa()

	if A.Alphabet().Size() != 2 || A.States().Size() != 4 || A.InitialStates().Size() != 2 || A.FinalStates().Size() != 2 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true || A.Alphabet().Probe(b) != true {
		t.Error()
	}

	//has exactly two transitions which are not loops,
	//both go from an initial to a final state,
	//and use two different letters
	transitionCount := 0
	var lastLetter set.Element
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() != 1 {
				t.Error()
			}

			u, _ := S.At(0)

			if s.IsEqual(u) || A.InitialStates().Probe(s) == false || A.FinalStates().Probe(u) == false {
				t.Error()
			}

			if transitionCount == 0 {
				lastLetter = x
			} else if transitionCount == 1 {
				if lastLetter.IsEqual(x) {
					t.Error()
				}
			}

			transitionCount += 1
		}
	}

	if transitionCount != 2 {
		t.Error()
	}
}

func TestStarNfa(t *testing.T) {
	//TODO
}
<commit_msg>stengthen tests<commit_after>package regex_test

import (
	"fmt"
	"github.com/hydroo/gomochex/automaton/nfa"
	"github.com/hydroo/gomochex/basic/set"
	"github.com/hydroo/gomochex/regex"
	"testing"
)

func TestExpressionFromString(t *testing.T) {

	//correct concatenation
	if e, ok := regex.ExpressionFromString("(asdf.π)"); ok != true || fmt.Sprint(e) != "(asdf.π)" {
		t.Error()
	}

	//correct letter
	if e, ok := regex.ExpressionFromString("π"); ok != true || fmt.Sprint(e) != "π" {
		t.Error()
	}

	//wrong letter
	if _, ok := regex.ExpressionFromString("π."); ok != false {
		t.Error()
	}

	//correct letter
	if e, ok := regex.ExpressionFromString("πasdf"); ok != true || fmt.Sprint(e) != "πasdf" {
		t.Error()
	}

	//correct or
	if e, ok := regex.ExpressionFromString("(asdf+π)"); ok != true || fmt.Sprint(e) != "(asdf+π)" {
		t.Error()
	}

	//wrong or
	if _, ok := regex.ExpressionFromString("((asdf+π)"); ok != false {
		t.Error()
	}

	//correct star
	if e, ok := regex.ExpressionFromString("((asdf+π))*"); ok != true || fmt.Sprint(e) != "((asdf+π))*" {
		t.Error()
	}

	//wrong star
	if _, ok := regex.ExpressionFromString("π*"); ok != false {
		t.Error()
	}

	//correct complex expr
	if e, ok := regex.ExpressionFromString("(a.((π+b).(c)*))"); ok != true || fmt.Sprint(e) != "(a.((π+b).(c)*))" {
		t.Error()
	}

}

func TestConcatNfa(t *testing.T) {
	//TODO
}

func TestLetterNfa(t *testing.T) {

	a := nfa.Letter("a")

	A := regex.Letter("a").Nfa()

	if A.Alphabet().Size() != 1 || A.States().Size() != 2 || A.InitialStates().Size() != 1 || A.FinalStates().Size() != 1 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true {
		t.Error()
	}

	//has exactly one transition which is not a loop,
	//and goes from an initial to a final state
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() != 1 {
				t.Error()
			}

			u, _ := S.At(0)

			if s.IsEqual(u) || A.InitialStates().Probe(s) != true || A.FinalStates().Probe(s) != false || A.FinalStates().Probe(u) != true || A.InitialStates().Probe(u) != false {
				t.Error()
			}
		}
	}
}

func TestOrNfa(t *testing.T) {
	a := nfa.Letter("a")
	b := nfa.Letter("π")

	A := regex.Or(regex.Letter("a"), regex.Letter("π")).Nfa()

	if A.Alphabet().Size() != 2 || A.States().Size() != 4 || A.InitialStates().Size() != 2 || A.FinalStates().Size() != 2 {
		t.Error()
	}
	if A.Alphabet().Probe(a) != true || A.Alphabet().Probe(b) != true {
		t.Error()
	}

	//has exactly two transitions which are not loops,
	//both go from an initial to a final state,
	//and use two different letters
	transitionCount := 0
	var lastLetter set.Element
	for i := 0; i < A.States().Size(); i += 1 {
		s, _ := A.States().At(i)
		for j := 0; j < A.Alphabet().Size(); j += 1 {
			x, _ := A.Alphabet().At(j)
			S := A.Transition(s.(nfa.State), x.(nfa.Letter))

			if S.Size() == 0 {
				continue
			}
			if S.Size() != 1 {
				t.Error()
			}

			u, _ := S.At(0)

			if s.IsEqual(u) || A.InitialStates().Probe(s) != true || A.FinalStates().Probe(s) != false || A.FinalStates().Probe(u) != true || A.InitialStates().Probe(u) != false {
				t.Error()
			}

			if transitionCount == 0 {
				lastLetter = x
			} else if transitionCount == 1 {
				if lastLetter.IsEqual(x) {
					t.Error()
				}
			}

			transitionCount += 1
		}
	}

	if transitionCount != 2 {
		t.Error()
	}
}

func TestStarNfa(t *testing.T) {
	//TODO
}
<|endoftext|>


<commit_before>package registry

import (
	"encoding/json"
	"os"

	"code.google.com/p/go-uuid/uuid"

	"github.com/wantedly/risu/schema"
)

// DefaultFilePath is default file path
const DefaultFilePath = "/tmp/risu/"

// LocalFsRegistry is sharing path
type LocalFsRegistry struct {
	path string
}

// NewLocalFsRegistry is init
func NewLocalFsRegistry(path string) Registry {
	if path == "" {
		path = DefaultFilePath
	}

	if _, err := os.Stat(path); err != nil {
		os.MkdirAll(path, 0755)
	}
	return &LocalFsRegistry{path}
}

// Set stores the build data to a json file. file name is "/tmp/risu/<UUID>.json".
func (r *LocalFsRegistry) Set(build schema.Build) error {
	b, err := json.Marshal(build)
	if err != nil {
		return err
	}

	file, err := os.Create(r.path + build.ID.String() + ".json")
	if err != nil {
		return err
	}

	defer file.Close()

	buildData := []byte(string(b))

	_, err = file.Write(buildData)
	if err != nil {
		return err
	}
	return nil
}

// Get get build data
func (r *LocalFsRegistry) Get(id uuid.UUID) (schema.Build, error) {
	// TODO: add error handling.(Expired Data and Not Found File)
	file, err := os.Open(r.path + id.String() + ".json")
	if err != nil {
		return schema.Build{}, err
	}
	defer file.Close()

	var build schema.Build

	err = json.NewDecoder(file).Decode(&build)
	if err != nil {
		return schema.Build{}, err
	}

	return build, nil
}
<commit_msg>Localfs registry implements list method<commit_after>package registry

import (
	"encoding/json"
	"io/ioutil"
	"os"

	"code.google.com/p/go-uuid/uuid"

	"github.com/wantedly/risu/schema"
)

// DefaultFilePath is default file path
const DefaultFilePath = "/tmp/risu/"

// LocalFsRegistry is sharing path
type LocalFsRegistry struct {
	path string
}

// NewLocalFsRegistry is init
func NewLocalFsRegistry(path string) Registry {
	if path == "" {
		path = DefaultFilePath
	}

	if _, err := os.Stat(path); err != nil {
		os.MkdirAll(path, 0755)
	}
	return &LocalFsRegistry{path}
}

// Set stores the build data to a json file. file name is "/tmp/risu/<UUID>.json".
func (r *LocalFsRegistry) Set(build schema.Build) error {
	b, err := json.Marshal(build)
	if err != nil {
		return err
	}

	file, err := os.Create(r.path + build.ID.String() + ".json")
	if err != nil {
		return err
	}

	defer file.Close()

	buildData := []byte(string(b))

	_, err = file.Write(buildData)
	if err != nil {
		return err
	}
	return nil
}

// Get get build data
func (r *LocalFsRegistry) Get(id uuid.UUID) (schema.Build, error) {
	// TODO: add error handling.(Expired Data and Not Found File)
	file, err := os.Open(r.path + id.String() + ".json")
	if err != nil {
		return schema.Build{}, err
	}
	defer file.Close()

	var build schema.Build

	err = json.NewDecoder(file).Decode(&build)
	if err != nil {
		return schema.Build{}, err
	}

	return build, nil
}

func (r *LocalFsRegistry) List() ([]schema.Build, error) {
	var builds []schema.Build

	fileInfos, err := ioutil.ReadDir(r.path)
	if err != nil {
		return nil, err
	}

	for _, fileInfo := range fileInfos {
		file, err := os.Open(r.path + fileInfo.Name())
		if err != nil {
			return nil, err
		}
		defer file.Close()

		var build schema.Build

		err = json.NewDecoder(file).Decode(&build)
		if err != nil {
			return nil, err
		}

		builds = append(builds, build)
	}

	return builds, nil
}
<|endoftext|>