<commit_before>package config

import "reflect"

func allSet(xs ...string) bool {
	for i := range xs {
		if xs[i] == "" {
			return false
		}
	}

	return true
}

func allBlankOrAllSet(xs ...string) bool {
	var blanks int
	for i := range xs {
		if xs[i] == "" {
			blanks++
		}
	}

	return blanks == len(xs) || blanks == 0
}

// From src/pkg/encoding/json.
func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

func merge(dst, src reflect.Value) error {
	if !src.IsValid() {
		// this means the value is the default value,
		// which we don't want to set on dest
		return nil
	}

	switch src.Kind() {
	case reflect.Struct:
		for i, n := 0, dst.NumField(); i < n; i++ {
			err := merge(dst.Field(i), src.Field(i))
			if err != nil {
				return err
			}
		}
	default:
		if dst.CanSet() && !isEmptyValue(src) {
			dst.Set(src)
		}
	}

	return nil
}
<commit_msg>Remove dead code<commit_after>package config

func allBlankOrAllSet(xs ...string) bool {
	var blanks int
	for i := range xs {
		if xs[i] == "" {
			blanks++
		}
	}

	return blanks == len(xs) || blanks == 0
}
<|endoftext|>


<commit_before>// Copyright 2015 The Chihaya Authors. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.

// Package query implements a faster single-purpose URL Query parser.
package query

import (
	"errors"
	"net/url"
	"strconv"
	"strings"
)

// Query represents a parsed URL.Query.
type Query struct {
	Infohashes []string
	Params     map[string]string
}

// New parses a raw url query.
func New(query string) (*Query, error) {
	var (
		keyStart, keyEnd int
		valStart, valEnd int
		firstInfohash    string

		onKey       = true
		hasInfohash = false

		q = &Query{
			Infohashes: nil,
			Params:     make(map[string]string),
		}
	)

	for i, length := 0, len(query); i < length; i++ {
		separator := query[i] == '&' || query[i] == ';' || query[i] == '?'
		if separator || i == length-1 {
			if onKey {
				keyStart = i + 1
				continue
			}

			if i == length-1 && !separator {
				if query[i] == '=' {
					continue
				}
				valEnd = i
			}

			keyStr, err := url.QueryUnescape(query[keyStart : keyEnd+1])
			if err != nil {
				return nil, err
			}

			valStr, err := url.QueryUnescape(query[valStart : valEnd+1])
			if err != nil {
				return nil, err
			}

			q.Params[strings.ToLower(keyStr)] = valStr

			if keyStr == "info_hash" {
				if hasInfohash {
					// Multiple infohashes
					if q.Infohashes == nil {
						q.Infohashes = []string{firstInfohash}
					}
					q.Infohashes = append(q.Infohashes, valStr)
				} else {
					firstInfohash = valStr
					hasInfohash = true
				}
			}

			onKey = true
			keyStart = i + 1

		} else if query[i] == '=' {
			onKey = false
			valStart = i + 1
		} else if onKey {
			keyEnd = i
		} else {
			valEnd = i
		}
	}

	return q, nil
}

// Uint64 is a helper to obtain a uint of any length from a Query. After being
// called, you can safely cast the uint64 to your desired length.
func (q *Query) Uint64(key string) (uint64, error) {
	str, exists := q.Params[key]
	if !exists {
		return 0, errors.New("value does not exist for key: " + key)
	}

	val, err := strconv.ParseUint(str, 10, 64)
	if err != nil {
		return 0, err
	}

	return val, nil
}
<commit_msg>Fix query parsing when blank values are present<commit_after>// Copyright 2015 The Chihaya Authors. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.

// Package query implements a faster single-purpose URL Query parser.
package query

import (
	"errors"
	"net/url"
	"strconv"
	"strings"
)

// Query represents a parsed URL.Query.
type Query struct {
	Infohashes []string
	Params     map[string]string
}

// New parses a raw url query.
func New(query string) (*Query, error) {
	var (
		keyStart, keyEnd int
		valStart, valEnd int
		firstInfohash    string

		onKey       = true
		hasInfohash = false

		q = &Query{
			Infohashes: nil,
			Params:     make(map[string]string),
		}
	)

	for i, length := 0, len(query); i < length; i++ {
		separator := query[i] == '&' || query[i] == ';' || query[i] == '?'
		last := i == length-1

		if separator || last {
			if onKey && !last {
				keyStart = i + 1
				continue
			}

			if last && !separator && !onKey {
				valEnd = i
			}

			keyStr, err := url.QueryUnescape(query[keyStart : keyEnd+1])
			if err != nil {
				return nil, err
			}

			var valStr string

			if valEnd > 0 {
				valStr, err = url.QueryUnescape(query[valStart : valEnd+1])
				if err != nil {
					return nil, err
				}
			}

			q.Params[strings.ToLower(keyStr)] = valStr

			if keyStr == "info_hash" {
				if hasInfohash {
					// Multiple infohashes
					if q.Infohashes == nil {
						q.Infohashes = []string{firstInfohash}
					}
					q.Infohashes = append(q.Infohashes, valStr)
				} else {
					firstInfohash = valStr
					hasInfohash = true
				}
			}

			valEnd = 0
			onKey = true
			keyStart = i + 1

		} else if query[i] == '=' {
			onKey = false
			valStart = i + 1
			valEnd = 0
		} else if onKey {
			keyEnd = i
		} else {
			valEnd = i
		}
	}

	return q, nil
}

// Uint64 is a helper to obtain a uint of any length from a Query. After being
// called, you can safely cast the uint64 to your desired length.
func (q *Query) Uint64(key string) (uint64, error) {
	str, exists := q.Params[key]
	if !exists {
		return 0, errors.New("value does not exist for key: " + key)
	}

	val, err := strconv.ParseUint(str, 10, 64)
	if err != nil {
		return 0, err
	}

	return val, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/temoto/robotstxt"
	"golang.org/x/net/html"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// Get the depth of a file path
func pathDepth(filePath string) int {
	cleanPath := strings.Trim(path.Clean(filePath), "/")

	// Special case: / should have length 0
	if cleanPath == "" {
		return 0
	}

	return len(strings.Split(cleanPath, "/"))
}

func HttpGetUnsafe(reqUrl string) (resp *http.Response, err error) {
	tr := http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}
	client := &http.Client{Transport: &tr}
	return client.Get(reqUrl)
}

type HttpCrawlerConfig struct {
	BodySizeLimit int64         `json:"maxBodySize"`
	Entry         string        `json:"entry"`
	MaxPathDepth  int           `json:"maxPathDepth"`
	RateLimit     time.Duration `json:"maxRequestPerSecond"`
	RobotName     string        `json:"robotName"`
	ObeyRobotsTxt bool          `json:"obeyRobotsTxt"`
}

type HttpCrawler struct {
	Config HttpCrawlerConfig

	Entry *url.URL

	RobotsTestAgent *robotstxt.Group
	Ticker          <-chan time.Time
}

func CreateHttpCrawler(rawConfig *json.RawMessage) (crawler *HttpCrawler, err error) {
	// Create a new instance
	crawler = &HttpCrawler{}

	// Parse config while providing default values
	config := HttpCrawlerConfig{
		BodySizeLimit: 10 * 1000 * 1000, // 1 MB
		MaxPathDepth:  20,
		RateLimit:     0,
		RobotName:     DEFAULT_BOTNAME,
		ObeyRobotsTxt: true,
	}
	err = json.Unmarshal(*rawConfig, &config)
	if err != nil {
		return
	}

	crawler.Config = config

	// Parse the entry URL
	entry, err := url.Parse(crawler.Config.Entry)
	if err != nil {
		return
	}
	crawler.Entry = entry

	// Initialize the RateLimit throttle ticker if needed
	if crawler.Config.RateLimit > 0 {
		crawler.Ticker = time.Tick(crawler.Config.RateLimit)
	}

	// Try to parse /robots.txt
	if crawler.Config.ObeyRobotsTxt {
		robotsURL := url.URL{}
		robotsURL = *entry
		robotsURL.Path = "/robots.txt"

		robotsRes, robotsErr := HttpGetUnsafe(robotsURL.String())
		if robotsErr == nil {
			defer robotsRes.Body.Close()

			var robots *robotstxt.RobotsData
			robots, err = robotstxt.FromResponse(robotsRes)
			if err != nil {
				return
			}

			crawler.RobotsTestAgent = robots.FindGroup(crawler.Config.RobotName)
		}
	}

	return
}

func (crawler *HttpCrawler) walker(entry *url.URL, fn WalkFunction) (err error) {
	entryStr := entry.String()

	// Check if this file is allowed to be crawled by robots.txt rules
	if crawler.RobotsTestAgent != nil && !crawler.RobotsTestAgent.Test(entryStr) {
		return
	}

	// Throttle requests as specified by the RateLimit if needed
	if crawler.Config.RateLimit > 0 {
		<-crawler.Ticker
	}

	// Do a standard HTTP GET request, but only download the first few kilobytes
	// of body data. Reasons:
	// 1. Save a request as otherwise we would have to create one HEAD request
	//    before every GET to check for the content type and content length
	// 2. Work around broken HEAD implementations
	resp, err := HttpGetUnsafe(entryStr)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	// Determine the content length in bytes
	var contentLength int64
	if len(resp.Header.Get("Content-Length")) > 0 {
		contentLength, err = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 0)
		if err != nil {
			return
		}
	}

	// We only continue walking on Content-Type: text/html files and call the
	// WalkFunction on all other files
	// TODO implement fallback mime sniffing (magic numbers parsing)
	mime, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
	if err != nil {
		return
	}

	if mime != "text/html" {
		var modTime time.Time
		lastModified := resp.Header.Get("Last-Modified")
		if lastModified != "" {
			modTime, err = http.ParseTime(resp.Header.Get("Last-Modified"))
			if err != nil {
				fmt.Println(resp.Header.Get("Last-Modified"))
				return
			}
		}

		fn(entryStr, FileInfo{
			URL:      entry,
			Size:     contentLength,
			MimeType: mime,
			ModTime:  modTime,
		})

		return
	}

	// Limit the amount of downloaded data
	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, crawler.Config.BodySizeLimit))
	if err != nil {
		return
	}

	if int64(len(body)) < contentLength {
		err = fmt.Errorf("BodySizeLimit exceeded")
		return
	}

	// Search for all anchor tags
	tokenizer := html.NewTokenizer(bytes.NewBuffer(body))

	for {
		tt := tokenizer.Next()

		switch tt {
		case html.ErrorToken:
			if tErr := tokenizer.Err(); tErr != io.EOF {
				err = tErr
			}
			return
		case html.StartTagToken:
			token := tokenizer.Token()

			if strings.ToLower(token.Data) == "a" {
				for _, a := range token.Attr {
					if a.Key == "href" {
						var u *url.URL
						u, err = url.Parse(a.Val)
						if err != nil {
							return
						}

						nextUrl := entry.ResolveReference(u)

						// Stop if we are going to leave the server
						if nextUrl.Hostname() != entry.Hostname() {
							break
						}

						// Ignore if the path depth is decreasing (e.g. href is .. )
						if pathDepth(nextUrl.Path) < pathDepth(entry.Path) {
							break
						}

						// Ignore if the url is not changing (e.g. href is . )
						if nextUrl.String() == entry.String() {
							break
						}

						// Stop if we have reached the maximum path depth
						if pathDepth(nextUrl.Path) > crawler.Config.MaxPathDepth {
							err = fmt.Errorf("MaxPathDepth exceeded")
							return
						}

						// Ignore Apache dir list sort links
						var match bool
						match, err = regexp.MatchString("^C=(.*);O=(.*)$", nextUrl.RawQuery)
						if match || err != nil {
							break
						}

						// Errors are bubbled up
						err = crawler.walker(nextUrl, fn)
						if err != nil {
							return
						}

						break
					}
				}
			}
		}
	}

	return
}

func (crawler *HttpCrawler) Walk(fn WalkFunction) error {
	return crawler.walker(crawler.Entry, fn)
}

func (crawler *HttpCrawler) Close() {

}
<commit_msg>HTTP: Some dirlists use & instead of ; for order links<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/temoto/robotstxt"
	"golang.org/x/net/html"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// Get the depth of a file path
func pathDepth(filePath string) int {
	cleanPath := strings.Trim(path.Clean(filePath), "/")

	// Special case: / should have length 0
	if cleanPath == "" {
		return 0
	}

	return len(strings.Split(cleanPath, "/"))
}

func HttpGetUnsafe(reqUrl string) (resp *http.Response, err error) {
	tr := http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}
	client := &http.Client{Transport: &tr}
	return client.Get(reqUrl)
}

type HttpCrawlerConfig struct {
	BodySizeLimit int64         `json:"maxBodySize"`
	Entry         string        `json:"entry"`
	MaxPathDepth  int           `json:"maxPathDepth"`
	RateLimit     time.Duration `json:"maxRequestPerSecond"`
	RobotName     string        `json:"robotName"`
	ObeyRobotsTxt bool          `json:"obeyRobotsTxt"`
}

type HttpCrawler struct {
	Config HttpCrawlerConfig

	Entry *url.URL

	RobotsTestAgent *robotstxt.Group
	Ticker          <-chan time.Time
}

func CreateHttpCrawler(rawConfig *json.RawMessage) (crawler *HttpCrawler, err error) {
	// Create a new instance
	crawler = &HttpCrawler{}

	// Parse config while providing default values
	config := HttpCrawlerConfig{
		BodySizeLimit: 10 * 1000 * 1000, // 1 MB
		MaxPathDepth:  20,
		RateLimit:     0,
		RobotName:     DEFAULT_BOTNAME,
		ObeyRobotsTxt: true,
	}
	err = json.Unmarshal(*rawConfig, &config)
	if err != nil {
		return
	}

	crawler.Config = config

	// Parse the entry URL
	entry, err := url.Parse(crawler.Config.Entry)
	if err != nil {
		return
	}
	crawler.Entry = entry

	// Initialize the RateLimit throttle ticker if needed
	if crawler.Config.RateLimit > 0 {
		crawler.Ticker = time.Tick(crawler.Config.RateLimit)
	}

	// Try to parse /robots.txt
	if crawler.Config.ObeyRobotsTxt {
		robotsURL := url.URL{}
		robotsURL = *entry
		robotsURL.Path = "/robots.txt"

		robotsRes, robotsErr := HttpGetUnsafe(robotsURL.String())
		if robotsErr == nil {
			defer robotsRes.Body.Close()

			var robots *robotstxt.RobotsData
			robots, err = robotstxt.FromResponse(robotsRes)
			if err != nil {
				return
			}

			crawler.RobotsTestAgent = robots.FindGroup(crawler.Config.RobotName)
		}
	}

	return
}

func (crawler *HttpCrawler) walker(entry *url.URL, fn WalkFunction) (err error) {
	entryStr := entry.String()

	// Check if this file is allowed to be crawled by robots.txt rules
	if crawler.RobotsTestAgent != nil && !crawler.RobotsTestAgent.Test(entryStr) {
		return
	}

	// Throttle requests as specified by the RateLimit if needed
	if crawler.Config.RateLimit > 0 {
		<-crawler.Ticker
	}

	// Do a standard HTTP GET request, but only download the first few kilobytes
	// of body data. Reasons:
	// 1. Save a request as otherwise we would have to create one HEAD request
	//    before every GET to check for the content type and content length
	// 2. Work around broken HEAD implementations
	resp, err := HttpGetUnsafe(entryStr)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	// Determine the content length in bytes
	var contentLength int64
	if len(resp.Header.Get("Content-Length")) > 0 {
		contentLength, err = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 0)
		if err != nil {
			return
		}
	}

	// We only continue walking on Content-Type: text/html files and call the
	// WalkFunction on all other files
	// TODO implement fallback mime sniffing (magic numbers parsing)
	mime, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
	if err != nil {
		return
	}

	if mime != "text/html" {
		var modTime time.Time
		lastModified := resp.Header.Get("Last-Modified")
		if lastModified != "" {
			modTime, err = http.ParseTime(resp.Header.Get("Last-Modified"))
			if err != nil {
				fmt.Println(resp.Header.Get("Last-Modified"))
				return
			}
		}

		fn(entryStr, FileInfo{
			URL:      entry,
			Size:     contentLength,
			MimeType: mime,
			ModTime:  modTime,
		})

		return
	}

	// Limit the amount of downloaded data
	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, crawler.Config.BodySizeLimit))
	if err != nil {
		return
	}

	if int64(len(body)) < contentLength {
		err = fmt.Errorf("BodySizeLimit exceeded")
		return
	}

	// Search for all anchor tags
	tokenizer := html.NewTokenizer(bytes.NewBuffer(body))

	for {
		tt := tokenizer.Next()

		switch tt {
		case html.ErrorToken:
			if tErr := tokenizer.Err(); tErr != io.EOF {
				err = tErr
			}
			return
		case html.StartTagToken:
			token := tokenizer.Token()

			if strings.ToLower(token.Data) == "a" {
				for _, a := range token.Attr {
					if a.Key == "href" {
						var u *url.URL
						u, err = url.Parse(a.Val)
						if err != nil {
							return
						}

						nextUrl := entry.ResolveReference(u)

						// Stop if we are going to leave the server
						if nextUrl.Hostname() != entry.Hostname() {
							break
						}

						// Ignore if the path depth is decreasing (e.g. href is .. )
						if pathDepth(nextUrl.Path) < pathDepth(entry.Path) {
							break
						}

						// Ignore if the url is not changing (e.g. href is . )
						if nextUrl.String() == entry.String() {
							break
						}

						// Stop if we have reached the maximum path depth
						if pathDepth(nextUrl.Path) > crawler.Config.MaxPathDepth {
							err = fmt.Errorf("MaxPathDepth exceeded")
							return
						}

						// Ignore Apache dir list sort links
						var match bool
						match, err = regexp.MatchString("^C=(.*)(&|;)O=(.*)$", nextUrl.RawQuery)
						if match || err != nil {
							break
						}

						// Errors are bubbled up
						err = crawler.walker(nextUrl, fn)
						if err != nil {
							return
						}

						break
					}
				}
			}
		}
	}

	return
}

func (crawler *HttpCrawler) Walk(fn WalkFunction) error {
	return crawler.walker(crawler.Entry, fn)
}

func (crawler *HttpCrawler) Close() {

}
<|endoftext|>


<commit_before>package main

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

func main() {
	server := server(routes(), 8080)
	server.Close()
}

func testServer() {
	server := httptest.NewServer(nil)
	server.Close()
}

func server(handler http.Handler, port int) httptest.Server {
	return &httptest.Server{
		Listener: newLocalListener(port),
		Config:   &http.Server{Handler: handler},
	}
}

func routes() http.Handler {
	mux := http.NewServeMux()
	//mux.HandleFunc("/", func () {
	//
	//})
	return mux
}

func newLocalListener(port int) net.Listener {
	l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	if err != nil {
		if l, err = net.Listen("tcp6", fmt.Sprintf("[::1]:%d", port)); err != nil {
			panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
		}
	}
	return l
}
<commit_msg>go fmt<commit_after>package main

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

func main() {
	server := server(routes(), 8080)
	server.Close()
}

func testServer() {
	server := httptest.NewServer(nil)
	server.Close()
}

func server(handler http.Handler, port int) *httptest.Server {
	return &httptest.Server{
		Listener: newLocalListener(port),
		Config:   &http.Server{Handler: handler},
	}
}

func routes() http.Handler {
	mux := http.NewServeMux()
	//mux.HandleFunc("/", func () {
	//
	//})
	return mux
}

func newLocalListener(port int) net.Listener {
	l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	if err != nil {
		if l, err = net.Listen("tcp6", fmt.Sprintf("[::1]:%d", port)); err != nil {
			panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
		}
	}
	return l
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"runtime"
	"testing"

	"github.com/stretchr/testify/assert"
)

type TestUtils struct {
	*MemSystem
	*MemLogger
	*MemConfig
	*MemDownloader
	*MemRunner
}

func newDockerStrategy() (*TestUtils, *DockerStrategy) {
	tu := &TestUtils{
		MemSystem:     &MemSystem{runtime.GOOS, runtime.GOARCH, 1000, 1000, make(map[string]bool), []string{}, []string{}, make(map[string][]string)},
		MemLogger:     &MemLogger{},
		MemConfig:     &MemConfig{},
		MemDownloader: &MemDownloader{},
		MemRunner:     &MemRunner{},
	}
	return tu, &DockerStrategy{
		StrategyCommon: &StrategyCommon{
			System:       tu.MemSystem,
			Logger:       tu.MemLogger,
			ConfigGetter: tu.MemConfig,
			Downloader:   tu.MemDownloader,
			Runner:       tu.MemRunner,
		},
		Data: DockerData{
			Name:       "testdocker",
			Desc:       "Test Docker Program",
			Version:    "1.9",
			Image:      "testdocker:{{.Version}}",
			OSArchData: make(map[string]map[string]string),
		},
	}
}

func TestDockerSimple(t *testing.T) {

	assert := assert.New(t)

	tu, td := newDockerStrategy()
	td.Run([]string{"first", "second"})

	assert.Equal(tu.MemRunner.History[0], "docker run --rm testdocker:1.9 first second")
}

func TestDockerAllOptions(t *testing.T) {
	assert := assert.New(t)

	tu, td := newDockerStrategy()
	td.Data.Interactive = true
	td.Data.DockerConn = true
	td.Data.PidHost = true
	td.Data.MountPwdAs = "/test"
	td.Data.MountPwd = true
	td.Data.RunAsUser = true
	td.Data.Terminal = "always"
	assert.Nil(td.Run([]string{"first", "second"}))

	wd, _ := os.Getwd()
	assert.Equal(tu.MemRunner.History[0], fmt.Sprintf("docker run -i -v /var/run/docker.sock:/var/run/docker.sock --pid host --volume %s:/test --volume %s:%s -u 1000:1000 -t --rm testdocker:1.9 first second", wd, wd, wd))
}

func TestDockerNotInstalled(t *testing.T) {
	assert := assert.New(t)

	tu, td := newDockerStrategy()
	tu.MemRunner.FailCheck("docker version")
	err := td.Run([]string{"first", "second"})
	assert.NotNil(err)
	assert.Contains(err.Error(), "docker not available")
}

func TestDockerBadImageTemplate(t *testing.T) {
	assert := assert.New(t)

	_, td := newDockerStrategy()
	td.Data.Image = "{{.Foo"

	err := td.Run([]string{"first", "second"})
	assert.NotNil(err)
	assert.Contains(err.Error(), "unclosed action")
}

func TestDockerCommandFailed(t *testing.T) {
	assert := assert.New(t)

	tu, td := newDockerStrategy()
	tu.MemRunner.FailCommand("docker run --rm testdocker:1.9 first second", fmt.Errorf("bad output"))
	err := td.Run([]string{"first", "second"})
	assert.NotNil(err)
	// assert.Contains(err.Error(), "bad output")
}

func newBinaryStrategy() (*TestUtils, *BinaryStrategy) {
	tu := &TestUtils{
		MemSystem:     &MemSystem{runtime.GOOS, runtime.GOARCH, 1000, 1000, make(map[string]bool), []string{}, []string{}, make(map[string][]string)},
		MemLogger:     &MemLogger{},
		MemConfig:     &MemConfig{},
		MemDownloader: &MemDownloader{},
		MemRunner:     &MemRunner{},
	}
	return tu, &BinaryStrategy{
		StrategyCommon: &StrategyCommon{
			System:       tu.MemSystem,
			Logger:       tu.MemLogger,
			ConfigGetter: tu.MemConfig,
			Downloader:   tu.MemDownloader,
			Runner:       tu.MemRunner,
		},
		Data: BinaryData{
			Name:       "testbinary",
			Desc:       "Test Binary Program",
			Version:    "2.1",
			BaseURL:    "https://github.com/testbinary/bin/releases/download/bin-{{.Version}}/jq-{{.OSArch}}",
			OSArchData: make(map[string]map[string]string),
		},
	}
}

func TestBinarySimple(t *testing.T) {

	assert := assert.New(t)

	tu, tb := newBinaryStrategy()
	tb.Run([]string{"first", "second"})

	binPath := path.Join(os.Getenv("HOME"), ".local/share/holen/bin/testbinary--2.1")
	remoteUrl := "https://github.com/testbinary/bin/releases/download/bin-2.1/jq-linux_amd64"

	// check download
	assert.Contains(tu.MemDownloader.Files, remoteUrl)
	assert.Contains(tu.MemDownloader.Files[remoteUrl], path.Join(os.Getenv("HOME"), ".local/share/holen/tmp"))
	assert.Contains(tu.MemDownloader.Files[remoteUrl], "testbinary--2.1")

	assert.Contains(tu.MemSystem.StderrMessages[0], "Downloading")
	assert.Contains(tu.MemSystem.StderrMessages[0], remoteUrl)

	assert.Equal(tu.MemRunner.History[0], fmt.Sprintf("%s first second", binPath))
}

func TestBinaryBadImageTemplate(t *testing.T) {
	assert := assert.New(t)

	_, tb := newBinaryStrategy()
	tb.Data.BaseURL = "https://{{.Foo"

	err := tb.Run([]string{"first", "second"})
	assert.NotNil(err)
	assert.Contains(err.Error(), "unclosed action")
}

func TestBinaryArchive(t *testing.T) {

	assert := assert.New(t)

	tu, tb := newBinaryStrategy()
	tb.Data.UnpackPath = "testbinary"
	tb.Data.BaseURL = "https://github.com/testbinary/bin/releases/download/bin-{{.Version}}/testbinary-{{.OSArch}}.zip"
	tu.MemSystem.ArchiveFiles["testbinary-linux_amd64.zip"] = []string{"testbinary"}

	err := tb.Run([]string{"first", "second"})
	assert.Nil(err)

	binPath := path.Join(os.Getenv("HOME"), ".local/share/holen/bin/testbinary--2.1")
	remoteUrl := "https://github.com/testbinary/bin/releases/download/bin-2.1/testbinary-linux_amd64.zip"

	// check download
	assert.Contains(tu.MemDownloader.Files, remoteUrl)
	assert.Contains(tu.MemDownloader.Files[remoteUrl], path.Join(os.Getenv("HOME"), ".local/share/holen/tmp"))
	assert.Contains(tu.MemDownloader.Files[remoteUrl], "testbinary-linux_amd64.zip")

	assert.Contains(tu.MemSystem.StderrMessages[0], "Downloading")
	assert.Contains(tu.MemSystem.StderrMessages[0], remoteUrl)

	assert.Equal(tu.MemRunner.History[0], fmt.Sprintf("%s first second", binPath))
}

func TestBinaryDownloadPath(t *testing.T) {
	assert := assert.New(t)

	var downloadPathTests = []struct {
		adjustment func(*TestUtils)
		err        error
		result     string
		cleanup    func(*TestUtils)
	}{
		{
			nil,
			nil,
			path.Join(os.Getenv("HOME"), ".local/share/holen/bin"),
			nil,
		},
		{
			func(tu *TestUtils) {
				os.Setenv("XDG_DATA_HOME", "/tmp")
			},
			nil,
			"/tmp/holen/bin",
			func(tu *TestUtils) {
				os.Setenv("XDG_DATA_HOME", "")
			},
		},
		{
			func(tu *TestUtils) {
				os.Setenv("HOME", "")
			},
			fmt.Errorf("$HOME not found"),
			"",
			func(tu *TestUtils) { return },
		},
	}

	for _, test := range downloadPathTests {

		tu, tb := newBinaryStrategy()
		if test.adjustment != nil {
			test.adjustment(tu)
		}

		result, err := tb.DownloadPath()
		if test.err == nil {
			assert.Nil(err)
		} else {
			assert.NotNil(err)
		}
		assert.Equal(result, test.result)
		if test.cleanup != nil {
			test.cleanup(tu)
		}
	}
}

func TestBinaryChecksumBinary(t *testing.T) {
	assert := assert.New(t)

	tempdir, _ := ioutil.TempDir("", "hash")
	defer os.RemoveAll(tempdir)
	filePath := path.Join(tempdir, "testfile")
	assert.Nil(ioutil.WriteFile(filePath, []byte("test contents\n"), 0755))

	var checksumTests = []struct {
		hashdata map[string]string
		result   error
	}{
		{
			map[string]string{"md5sum": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
			HashMismatch{algo: "md5", checksum: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", hash: "1b3c032e3e4eaad23401e1568879f150"},
		},
		{
			map[string]string{"md5sum": "1b3c032e3e4eaad23401e1568879f150"},
			nil,
		},
		{
			map[string]string{"sha1sum": "40b44f15b4b6690a90792137a03d57c4d2918271"},
			nil,
		},
		{
			map[string]string{"sha256sum": "15721d5068de16cf4eba8d0fe6a563bb177333405323b479dcf5986da440c081"},
			nil,
		},
		{
			map[string]string{
				"md5sum":    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
				"sha1sum":   "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
				"sha256sum": "15721d5068de16cf4eba8d0fe6a563bb177333405323b479dcf5986da440c081",
			},
			nil,
		},
	}

	for _, test := range checksumTests {

		_, tb := newBinaryStrategy()

		tb.Data.OSArchData[fmt.Sprintf("%s_%s", tb.OS(), tb.Arch())] = test.hashdata
		result := tb.ChecksumBinary(filePath)

		assert.Equal(result, test.result)
	}
}
<commit_msg>add simple test for Inspect<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"runtime"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
)

type TestUtils struct {
	*MemSystem
	*MemLogger
	*MemConfig
	*MemDownloader
	*MemRunner
}

func newDockerStrategy() (*TestUtils, *DockerStrategy) {
	tu := &TestUtils{
		MemSystem:     &MemSystem{runtime.GOOS, runtime.GOARCH, 1000, 1000, make(map[string]bool), []string{}, []string{}, make(map[string][]string)},
		MemLogger:     &MemLogger{},
		MemConfig:     &MemConfig{},
		MemDownloader: &MemDownloader{},
		MemRunner:     &MemRunner{},
	}
	return tu, &DockerStrategy{
		StrategyCommon: &StrategyCommon{
			System:       tu.MemSystem,
			Logger:       tu.MemLogger,
			ConfigGetter: tu.MemConfig,
			Downloader:   tu.MemDownloader,
			Runner:       tu.MemRunner,
		},
		Data: DockerData{
			Name:       "testdocker",
			Desc:       "Test Docker Program",
			Version:    "1.9",
			Image:      "testdocker:{{.Version}}",
			OSArchData: make(map[string]map[string]string),
		},
	}
}

func TestDockerSimple(t *testing.T) {

	assert := assert.New(t)

	tu, td := newDockerStrategy()
	td.Run([]string{"first", "second"})

	assert.Equal(tu.MemRunner.History[0], "docker run --rm testdocker:1.9 first second")
}

func TestDockerAllOptions(t *testing.T) {
	assert := assert.New(t)

	tu, td := newDockerStrategy()
	td.Data.Interactive = true
	td.Data.DockerConn = true
	td.Data.PidHost = true
	td.Data.MountPwdAs = "/test"
	td.Data.MountPwd = true
	td.Data.RunAsUser = true
	td.Data.Terminal = "always"
	assert.Nil(td.Run([]string{"first", "second"}))

	wd, _ := os.Getwd()
	assert.Equal(tu.MemRunner.History[0], fmt.Sprintf("docker run -i -v /var/run/docker.sock:/var/run/docker.sock --pid host --volume %s:/test --volume %s:%s -u 1000:1000 -t --rm testdocker:1.9 first second", wd, wd, wd))
}

func TestDockerNotInstalled(t *testing.T) {
	assert := assert.New(t)

	tu, td := newDockerStrategy()
	tu.MemRunner.FailCheck("docker version")
	err := td.Run([]string{"first", "second"})
	assert.NotNil(err)
	assert.Contains(err.Error(), "docker not available")
}

func TestDockerBadImageTemplate(t *testing.T) {
	assert := assert.New(t)

	_, td := newDockerStrategy()
	td.Data.Image = "{{.Foo"

	err := td.Run([]string{"first", "second"})
	assert.NotNil(err)
	assert.Contains(err.Error(), "unclosed action")
}

func TestDockerCommandFailed(t *testing.T) {
	assert := assert.New(t)

	tu, td := newDockerStrategy()
	tu.MemRunner.FailCommand("docker run --rm testdocker:1.9 first second", fmt.Errorf("bad output"))
	err := td.Run([]string{"first", "second"})
	assert.NotNil(err)
	// assert.Contains(err.Error(), "bad output")
}

func TestDockerInspect(t *testing.T) {
	assert := assert.New(t)

	tu, td := newDockerStrategy()
	td.Inspect()
	completeOutput := strings.Join(tu.MemSystem.StdoutMessages, "")

	assert.Contains(completeOutput, "final image: testdocker:1.9")
}

func newBinaryStrategy() (*TestUtils, *BinaryStrategy) {
	tu := &TestUtils{
		MemSystem:     &MemSystem{runtime.GOOS, runtime.GOARCH, 1000, 1000, make(map[string]bool), []string{}, []string{}, make(map[string][]string)},
		MemLogger:     &MemLogger{},
		MemConfig:     &MemConfig{},
		MemDownloader: &MemDownloader{},
		MemRunner:     &MemRunner{},
	}
	return tu, &BinaryStrategy{
		StrategyCommon: &StrategyCommon{
			System:       tu.MemSystem,
			Logger:       tu.MemLogger,
			ConfigGetter: tu.MemConfig,
			Downloader:   tu.MemDownloader,
			Runner:       tu.MemRunner,
		},
		Data: BinaryData{
			Name:       "testbinary",
			Desc:       "Test Binary Program",
			Version:    "2.1",
			BaseURL:    "https://github.com/testbinary/bin/releases/download/bin-{{.Version}}/jq-{{.OSArch}}",
			OSArchData: make(map[string]map[string]string),
		},
	}
}

func TestBinarySimple(t *testing.T) {

	assert := assert.New(t)

	tu, tb := newBinaryStrategy()
	tb.Run([]string{"first", "second"})

	binPath := path.Join(os.Getenv("HOME"), ".local/share/holen/bin/testbinary--2.1")
	remoteUrl := "https://github.com/testbinary/bin/releases/download/bin-2.1/jq-linux_amd64"

	// check download
	assert.Contains(tu.MemDownloader.Files, remoteUrl)
	assert.Contains(tu.MemDownloader.Files[remoteUrl], path.Join(os.Getenv("HOME"), ".local/share/holen/tmp"))
	assert.Contains(tu.MemDownloader.Files[remoteUrl], "testbinary--2.1")

	assert.Contains(tu.MemSystem.StderrMessages[0], "Downloading")
	assert.Contains(tu.MemSystem.StderrMessages[0], remoteUrl)

	assert.Equal(tu.MemRunner.History[0], fmt.Sprintf("%s first second", binPath))
}

func TestBinaryBadImageTemplate(t *testing.T) {
	assert := assert.New(t)

	_, tb := newBinaryStrategy()
	tb.Data.BaseURL = "https://{{.Foo"

	err := tb.Run([]string{"first", "second"})
	assert.NotNil(err)
	assert.Contains(err.Error(), "unclosed action")
}

func TestBinaryArchive(t *testing.T) {

	assert := assert.New(t)

	tu, tb := newBinaryStrategy()
	tb.Data.UnpackPath = "testbinary"
	tb.Data.BaseURL = "https://github.com/testbinary/bin/releases/download/bin-{{.Version}}/testbinary-{{.OSArch}}.zip"
	tu.MemSystem.ArchiveFiles["testbinary-linux_amd64.zip"] = []string{"testbinary"}

	err := tb.Run([]string{"first", "second"})
	assert.Nil(err)

	binPath := path.Join(os.Getenv("HOME"), ".local/share/holen/bin/testbinary--2.1")
	remoteUrl := "https://github.com/testbinary/bin/releases/download/bin-2.1/testbinary-linux_amd64.zip"

	// check download
	assert.Contains(tu.MemDownloader.Files, remoteUrl)
	assert.Contains(tu.MemDownloader.Files[remoteUrl], path.Join(os.Getenv("HOME"), ".local/share/holen/tmp"))
	assert.Contains(tu.MemDownloader.Files[remoteUrl], "testbinary-linux_amd64.zip")

	assert.Contains(tu.MemSystem.StderrMessages[0], "Downloading")
	assert.Contains(tu.MemSystem.StderrMessages[0], remoteUrl)

	assert.Equal(tu.MemRunner.History[0], fmt.Sprintf("%s first second", binPath))
}

func TestBinaryDownloadPath(t *testing.T) {
	assert := assert.New(t)

	var downloadPathTests = []struct {
		adjustment func(*TestUtils)
		err        error
		result     string
		cleanup    func(*TestUtils)
	}{
		{
			nil,
			nil,
			path.Join(os.Getenv("HOME"), ".local/share/holen/bin"),
			nil,
		},
		{
			func(tu *TestUtils) {
				os.Setenv("XDG_DATA_HOME", "/tmp")
			},
			nil,
			"/tmp/holen/bin",
			func(tu *TestUtils) {
				os.Setenv("XDG_DATA_HOME", "")
			},
		},
		{
			func(tu *TestUtils) {
				os.Setenv("HOME", "")
			},
			fmt.Errorf("$HOME not found"),
			"",
			func(tu *TestUtils) { return },
		},
	}

	for _, test := range downloadPathTests {

		tu, tb := newBinaryStrategy()
		if test.adjustment != nil {
			test.adjustment(tu)
		}

		result, err := tb.DownloadPath()
		if test.err == nil {
			assert.Nil(err)
		} else {
			assert.NotNil(err)
		}
		assert.Equal(result, test.result)
		if test.cleanup != nil {
			test.cleanup(tu)
		}
	}
}

func TestBinaryChecksumBinary(t *testing.T) {
	assert := assert.New(t)

	tempdir, _ := ioutil.TempDir("", "hash")
	defer os.RemoveAll(tempdir)
	filePath := path.Join(tempdir, "testfile")
	assert.Nil(ioutil.WriteFile(filePath, []byte("test contents\n"), 0755))

	var checksumTests = []struct {
		hashdata map[string]string
		result   error
	}{
		{
			map[string]string{"md5sum": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
			HashMismatch{algo: "md5", checksum: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", hash: "1b3c032e3e4eaad23401e1568879f150"},
		},
		{
			map[string]string{"md5sum": "1b3c032e3e4eaad23401e1568879f150"},
			nil,
		},
		{
			map[string]string{"sha1sum": "40b44f15b4b6690a90792137a03d57c4d2918271"},
			nil,
		},
		{
			map[string]string{"sha256sum": "15721d5068de16cf4eba8d0fe6a563bb177333405323b479dcf5986da440c081"},
			nil,
		},
		{
			map[string]string{
				"md5sum":    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
				"sha1sum":   "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
				"sha256sum": "15721d5068de16cf4eba8d0fe6a563bb177333405323b479dcf5986da440c081",
			},
			nil,
		},
	}

	for _, test := range checksumTests {

		_, tb := newBinaryStrategy()

		tb.Data.OSArchData[fmt.Sprintf("%s_%s", tb.OS(), tb.Arch())] = test.hashdata
		result := tb.ChecksumBinary(filePath)

		assert.Equal(result, test.result)
	}
}

func TestBinaryInspect(t *testing.T) {
	assert := assert.New(t)

	tu, tb := newBinaryStrategy()
	tb.Inspect()
	completeOutput := strings.Join(tu.MemSystem.StdoutMessages, "")

	assert.Contains(completeOutput, "final url: https://github.com/testbinary/bin/releases/download/bin-2.1/jq-linux_amd64")
}
<|endoftext|>


<commit_before>package sudoku

import (
	"reflect"
	"strconv"
	"strings"
	"testing"
	"time"
)

func BenchmarkHumanSolve(b *testing.B) {
	for i := 0; i < b.N; i++ {
		grid := NewGrid()
		defer grid.Done()
		grid.LoadSDK(TEST_GRID)
		grid.HumanSolve(nil)
	}
}

func TestCompoundSolveStep(t *testing.T) {

	nInRowTechnique := techniquesByName["Necessary In Row"]

	if nInRowTechnique == nil {
		t.Fatal("Couldn't find necessary in row technique")
	}

	simpleFillStep := &SolveStep{
		Technique: nInRowTechnique,
	}

	cullTechnique := techniquesByName["Hidden Quad Block"]

	if cullTechnique == nil {
		t.Fatal("Couldn't find hidden quad block technique")
	}

	cullStep := &SolveStep{
		Technique: cullTechnique,
	}

	compound := &CompoundSolveStep{
		PrecursorSteps: []*SolveStep{
			cullStep,
			cullStep,
		},
		FillStep: simpleFillStep,
	}

	if !compound.valid() {
		t.Error("A valid compound was not thought valid")
	}

	steps := compound.Steps()
	expected := []*SolveStep{
		cullStep,
		cullStep,
		simpleFillStep,
	}

	if !reflect.DeepEqual(steps, expected) {
		t.Error("compound.steps gave wrong result. Got", steps, "expected", expected)
	}

	compound.PrecursorSteps[0] = simpleFillStep

	if compound.valid() {
		t.Error("A compound tep with a fill precursor step was thought valid")
	}

	compound.PrecursorSteps = nil

	if !compound.valid() {
		t.Error("A compound step with no precursor steps was not thought valid")
	}

	compound.FillStep = nil

	if compound.valid() {
		t.Error("A compound step with no fill step was thought valid.")
	}

	createdCompound := newCompoundSolveStep([]*SolveStep{
		cullStep,
		cullStep,
		simpleFillStep,
	})

	if createdCompound == nil {
		t.Error("newCompoundSolveStep failed to create compound step")
	}

	if !createdCompound.valid() {
		t.Error("newCompoundSolveStep created invalid compound step")
	}
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	steps := grid.HumanSolution(nil)

	if steps == nil {
		t.Fatal("Human solution returned 0 techniques.")
	}

	if grid.Solved() {
		t.Log("Human Solutions mutated the grid.")
		t.Fail()
	}

	steps = grid.HumanSolve(nil)
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}
}

func TestHumanSolveOptionsNoGuess(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	options := DefaultHumanSolveOptions()
	options.TechniquesToUse = Techniques[0:3]
	options.NoGuess = true

	solution := grid.HumanSolution(options)

	if solution != nil && len(solution.CompoundSteps) != 0 {
		t.Error("A human solve with very limited techniques and no allowed guesses was still solved: ", solution)
	}
}

func TestShortTechniquesToUseHumanSolveOptions(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	shortTechniqueOptions := DefaultHumanSolveOptions()
	shortTechniqueOptions.TechniquesToUse = Techniques[0:5]

	steps := grid.HumanSolution(shortTechniqueOptions)

	if steps == nil {
		t.Fatal("Short technique Options returned nothing")
	}
}

func TestHumanSolveOptionsMethods(t *testing.T) {

	defaultOptions := &HumanSolveOptions{
		15,
		Techniques,
		false,
		nil,
	}

	options := DefaultHumanSolveOptions()

	if !reflect.DeepEqual(options, defaultOptions) {
		t.Error("defaultOptions came back incorrectly: ", options)
	}

	//Test the case where the user is deliberately trying to specify that no
	//normal techniques should use (and that they should implicitly guess
	//constantly)
	zeroLenTechniquesOptions := DefaultHumanSolveOptions()
	zeroLenTechniquesOptions.TechniquesToUse = []SolveTechnique{}

	zeroLenTechniquesOptions.validate()

	if len(zeroLenTechniquesOptions.TechniquesToUse) != 0 {
		t.Error("Validate treated a deliberate zero-len techniques to use as a nil to be replaced")
	}

	weirdOptions := &HumanSolveOptions{
		-3,
		nil,
		false,
		nil,
	}

	validatedOptions := &HumanSolveOptions{
		1,
		Techniques,
		false,
		nil,
	}

	weirdOptions.validate()

	if !reflect.DeepEqual(weirdOptions, validatedOptions) {
		t.Error("Weird options didn't validate:", weirdOptions, "wanted", validatedOptions)
	}

	guessOptions := DefaultHumanSolveOptions()
	guessOptions.TechniquesToUse = AllTechniques
	guessOptions.validate()

	for i, technique := range guessOptions.TechniquesToUse {
		if technique == GuessTechnique {
			t.Error("Validate didn't remove a guesstechnique (position", i, ")")
		}
	}

	//TODO: verify edge case of single GuessTechnique is fine.

}

func TestTechniquesToUseAfterGuessHumanSolveOptions(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	options := DefaultHumanSolveOptions()
	options.TechniquesToUse = []SolveTechnique{}
	options.techniquesToUseAfterGuess = Techniques[0:5]

	solution := grid.HumanSolution(options)

	steps := solution.Steps()

	if len(steps) == 0 {
		t.Fatal("Options with techniques to use after guess returned nil")
	}

	if steps[0].Technique != GuessTechnique {
		t.Error("First technqiu with techniques to use after guess wasn't guess")
	}

	allowedTechniques := make(map[SolveTechnique]bool)

	for _, technique := range Techniques[0:5] {
		allowedTechniques[technique] = true
	}

	//Guess is also allowed to be used later, although we don't expect that.
	allowedTechniques[GuessTechnique] = true

	for i, step := range steps[1:len(steps)] {
		if _, ok := allowedTechniques[step.Technique]; !ok {
			t.Error("Step number", i, "was not in set of allowed techniques", step.Technique)
		}
	}

}

func TestHint(t *testing.T) {

	//This is still flaky, but at least it's a little more likely to catch problems. :-/
	for i := 0; i < 10; i++ {
		hintTestHelper(t, nil, "base case"+strconv.Itoa(i))
	}

	options := DefaultHumanSolveOptions()
	options.TechniquesToUse = []SolveTechnique{}
	options.techniquesToUseAfterGuess = Techniques

	hintTestHelper(t, options, "guess")
}

func hintTestHelper(t *testing.T, options *HumanSolveOptions, description string) {
	grid := NewGrid()
	defer grid.Done()

	grid.LoadSDK(TEST_GRID)

	diagram := grid.Diagram(false)

	hint := grid.Hint(options)

	if grid.Diagram(false) != diagram {
		t.Error("Hint mutated the grid but it wasn't supposed to.")
	}

	steps := hint.CompoundSteps

	if steps == nil || len(steps) == 0 {
		t.Error("No steps returned from Hint", description)
	}

	if len(steps) != 1 {
		t.Error("Hint was wrong length")
	}

	if !steps[0].valid() {
		t.Error("Hint compound step was invalid")
	}
}

func TestHumanSolveWithGuess(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	if !grid.LoadSDKFromFile(puzzlePath("harddifficulty.sdk")) {
		t.Fatal("harddifficulty.sdk wasn't loaded")
	}

	solution := grid.HumanSolution(nil)
	steps := solution.Steps()

	if steps == nil {
		t.Fatal("Didn't find a solution to a grid that should have needed a guess")
	}

	foundGuess := false
	for i, step := range steps {
		if step.Technique.Name() == "Guess" {
			foundGuess = true
		}
		step.Apply(grid)
		if grid.Invalid() {
			t.Fatal("A solution with a guess in it got us into an invalid grid state. step", i)
		}
	}

	if !foundGuess {
		t.Error("Solution that should have used guess didn't have any guess.")
	}

	if !grid.Solved() {
		t.Error("A solution with a guess said it should solve the puzzle, but it didn't.")
	}

}

func TestStepsDescription(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	//It's really brittle that we load techniques in this way... it changes every time we add a new early technique!
	steps := SolveDirections{
		grid,
		[]*CompoundSolveStep{
			{
				FillStep: &SolveStep{
					techniquesByName["Only Legal Number"],
					CellSlice{
						grid.Cell(0, 0),
					},
					IntSlice{1},
					nil,
					nil,
					nil,
				},
			},
			{
				PrecursorSteps: []*SolveStep{
					{
						techniquesByName["Pointing Pair Col"],
						CellSlice{
							grid.Cell(1, 0),
							grid.Cell(1, 1),
						},
						IntSlice{1, 2},
						CellSlice{
							grid.Cell(1, 3),
							grid.Cell(1, 4),
						},
						nil,
						nil,
					},
				},
				FillStep: &SolveStep{
					techniquesByName["Only Legal Number"],
					CellSlice{
						grid.Cell(2, 0),
					},
					IntSlice{2},
					nil,
					nil,
					nil,
				},
			},
		},
	}

	descriptions := steps.Description()

	GOLDEN_DESCRIPTIONS := []string{
		"First, based on the other numbers you've entered, (0,0) can only be a 1. How do we know that? We put 1 in cell (0,0) because 1 is the only remaining valid number for that cell.",
		"Finally, based on the other numbers you've entered, (2,0) can only be a 2. How do we know that? We can't fill any cells right away so first we need to cull some possibilities. First, we remove the possibilities 1 and 2 from cells (1,0) and (1,1) because 1 is only possible in column 0 of block 1, which means it can't be in any other cell in that column not in that block. Finally, we put 2 in cell (2,0) because 2 is the only remaining valid number for that cell.",
	}

	if len(descriptions) != len(GOLDEN_DESCRIPTIONS) {
		t.Fatal("Descriptions had too few items. Got\n", strings.Join(descriptions, "***"), "\nwanted\n", strings.Join(GOLDEN_DESCRIPTIONS, "***"))
	}

	for i := 0; i < len(GOLDEN_DESCRIPTIONS); i++ {
		if descriptions[i] != GOLDEN_DESCRIPTIONS[i] {
			t.Log("Got wrong human solve description: ", descriptions[i], "wanted", GOLDEN_DESCRIPTIONS[i])
			t.Fail()
		}
	}
}

//TODO: this is useful. Should we use this in other tests?
func cellRefsToCells(refs []cellRef, grid *Grid) CellSlice {
	var result CellSlice
	for _, ref := range refs {
		result = append(result, ref.Cell(grid))
	}
	return result
}

func TestPuzzleDifficulty(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	//We use the cheaper one for testing so it completes faster.
	difficulty := grid.calcluateDifficulty(false)

	if grid.Solved() {
		t.Log("Difficulty shouldn't have changed the underlying grid, but it did.")
		t.Fail()
	}

	if difficulty < 0.0 || difficulty > 1.0 {
		t.Log("The grid's difficulty was outside of allowed bounds.")
		t.Fail()
	}

	puzzleFilenames := []string{"harddifficulty.sdk", "harddifficulty2.sdk"}

	for _, filename := range puzzleFilenames {
		puzzleDifficultyHelper(filename, t)
	}
}

func puzzleDifficultyHelper(filename string, t *testing.T) {
	otherGrid := NewGrid()
	if !otherGrid.LoadSDKFromFile(puzzlePath(filename)) {
		t.Log("Whoops, couldn't load the file to test:", filename)
		t.Fail()
	}

	after := time.After(time.Second * 60)

	done := make(chan bool)

	go func() {
		//We use the cheaper one for testing so it completes faster
		_ = otherGrid.calcluateDifficulty(false)
		done <- true
	}()

	select {
	case <-done:
		//totally fine.
	case <-after:
		//Uh oh.
		t.Log("We never finished solving the hard difficulty puzzle: ", filename)
		t.Fail()
	}
}
<commit_msg>Added a TODO<commit_after>package sudoku

import (
	"reflect"
	"strconv"
	"strings"
	"testing"
	"time"
)

func BenchmarkHumanSolve(b *testing.B) {
	for i := 0; i < b.N; i++ {
		grid := NewGrid()
		defer grid.Done()
		grid.LoadSDK(TEST_GRID)
		grid.HumanSolve(nil)
	}
}

func TestCompoundSolveStep(t *testing.T) {

	nInRowTechnique := techniquesByName["Necessary In Row"]

	if nInRowTechnique == nil {
		t.Fatal("Couldn't find necessary in row technique")
	}

	simpleFillStep := &SolveStep{
		Technique: nInRowTechnique,
	}

	cullTechnique := techniquesByName["Hidden Quad Block"]

	if cullTechnique == nil {
		t.Fatal("Couldn't find hidden quad block technique")
	}

	cullStep := &SolveStep{
		Technique: cullTechnique,
	}

	compound := &CompoundSolveStep{
		PrecursorSteps: []*SolveStep{
			cullStep,
			cullStep,
		},
		FillStep: simpleFillStep,
	}

	if !compound.valid() {
		t.Error("A valid compound was not thought valid")
	}

	steps := compound.Steps()
	expected := []*SolveStep{
		cullStep,
		cullStep,
		simpleFillStep,
	}

	if !reflect.DeepEqual(steps, expected) {
		t.Error("compound.steps gave wrong result. Got", steps, "expected", expected)
	}

	compound.PrecursorSteps[0] = simpleFillStep

	if compound.valid() {
		t.Error("A compound tep with a fill precursor step was thought valid")
	}

	compound.PrecursorSteps = nil

	if !compound.valid() {
		t.Error("A compound step with no precursor steps was not thought valid")
	}

	compound.FillStep = nil

	if compound.valid() {
		t.Error("A compound step with no fill step was thought valid.")
	}

	createdCompound := newCompoundSolveStep([]*SolveStep{
		cullStep,
		cullStep,
		simpleFillStep,
	})

	if createdCompound == nil {
		t.Error("newCompoundSolveStep failed to create compound step")
	}

	if !createdCompound.valid() {
		t.Error("newCompoundSolveStep created invalid compound step")
	}
}

func TestHumanSolve(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	steps := grid.HumanSolution(nil)

	if steps == nil {
		t.Fatal("Human solution returned 0 techniques.")
	}

	if grid.Solved() {
		t.Log("Human Solutions mutated the grid.")
		t.Fail()
	}

	steps = grid.HumanSolve(nil)
	//TODO: test to make sure that we use a wealth of different techniques. This will require a cooked random for testing.
	if steps == nil {
		t.Log("Human solve returned 0 techniques")
		t.Fail()
	}
	if !grid.Solved() {
		t.Log("Human solve failed to solve the simple grid.")
		t.Fail()
	}
}

func TestHumanSolveOptionsNoGuess(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	options := DefaultHumanSolveOptions()
	options.TechniquesToUse = Techniques[0:3]
	options.NoGuess = true

	solution := grid.HumanSolution(options)

	if solution != nil && len(solution.CompoundSteps) != 0 {
		t.Error("A human solve with very limited techniques and no allowed guesses was still solved: ", solution)
	}
}

func TestShortTechniquesToUseHumanSolveOptions(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	shortTechniqueOptions := DefaultHumanSolveOptions()
	shortTechniqueOptions.TechniquesToUse = Techniques[0:5]

	steps := grid.HumanSolution(shortTechniqueOptions)

	if steps == nil {
		t.Fatal("Short technique Options returned nothing")
	}
}

func TestHumanSolveOptionsMethods(t *testing.T) {

	defaultOptions := &HumanSolveOptions{
		15,
		Techniques,
		false,
		nil,
	}

	options := DefaultHumanSolveOptions()

	if !reflect.DeepEqual(options, defaultOptions) {
		t.Error("defaultOptions came back incorrectly: ", options)
	}

	//Test the case where the user is deliberately trying to specify that no
	//normal techniques should use (and that they should implicitly guess
	//constantly)
	zeroLenTechniquesOptions := DefaultHumanSolveOptions()
	zeroLenTechniquesOptions.TechniquesToUse = []SolveTechnique{}

	zeroLenTechniquesOptions.validate()

	if len(zeroLenTechniquesOptions.TechniquesToUse) != 0 {
		t.Error("Validate treated a deliberate zero-len techniques to use as a nil to be replaced")
	}

	weirdOptions := &HumanSolveOptions{
		-3,
		nil,
		false,
		nil,
	}

	validatedOptions := &HumanSolveOptions{
		1,
		Techniques,
		false,
		nil,
	}

	weirdOptions.validate()

	if !reflect.DeepEqual(weirdOptions, validatedOptions) {
		t.Error("Weird options didn't validate:", weirdOptions, "wanted", validatedOptions)
	}

	guessOptions := DefaultHumanSolveOptions()
	guessOptions.TechniquesToUse = AllTechniques
	guessOptions.validate()

	for i, technique := range guessOptions.TechniquesToUse {
		if technique == GuessTechnique {
			t.Error("Validate didn't remove a guesstechnique (position", i, ")")
		}
	}

	//TODO: verify edge case of single GuessTechnique is fine.

}

func TestTechniquesToUseAfterGuessHumanSolveOptions(t *testing.T) {

	//TODO: if we don't treat Guess that specially now, is it worth having
	//this be a separate item?

	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	options := DefaultHumanSolveOptions()
	options.TechniquesToUse = []SolveTechnique{}
	options.techniquesToUseAfterGuess = Techniques[0:5]

	solution := grid.HumanSolution(options)

	steps := solution.Steps()

	if len(steps) == 0 {
		t.Fatal("Options with techniques to use after guess returned nil")
	}

	if steps[0].Technique != GuessTechnique {
		t.Error("First technqiu with techniques to use after guess wasn't guess")
	}

	allowedTechniques := make(map[SolveTechnique]bool)

	for _, technique := range Techniques[0:5] {
		allowedTechniques[technique] = true
	}

	//Guess is also allowed to be used later, although we don't expect that.
	allowedTechniques[GuessTechnique] = true

	for i, step := range steps[1:len(steps)] {
		if _, ok := allowedTechniques[step.Technique]; !ok {
			t.Error("Step number", i, "was not in set of allowed techniques", step.Technique)
		}
	}

}

func TestHint(t *testing.T) {

	//This is still flaky, but at least it's a little more likely to catch problems. :-/
	for i := 0; i < 10; i++ {
		hintTestHelper(t, nil, "base case"+strconv.Itoa(i))
	}

	options := DefaultHumanSolveOptions()
	options.TechniquesToUse = []SolveTechnique{}
	options.techniquesToUseAfterGuess = Techniques

	hintTestHelper(t, options, "guess")
}

func hintTestHelper(t *testing.T, options *HumanSolveOptions, description string) {
	grid := NewGrid()
	defer grid.Done()

	grid.LoadSDK(TEST_GRID)

	diagram := grid.Diagram(false)

	hint := grid.Hint(options)

	if grid.Diagram(false) != diagram {
		t.Error("Hint mutated the grid but it wasn't supposed to.")
	}

	steps := hint.CompoundSteps

	if steps == nil || len(steps) == 0 {
		t.Error("No steps returned from Hint", description)
	}

	if len(steps) != 1 {
		t.Error("Hint was wrong length")
	}

	if !steps[0].valid() {
		t.Error("Hint compound step was invalid")
	}
}

func TestHumanSolveWithGuess(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	if !grid.LoadSDKFromFile(puzzlePath("harddifficulty.sdk")) {
		t.Fatal("harddifficulty.sdk wasn't loaded")
	}

	solution := grid.HumanSolution(nil)
	steps := solution.Steps()

	if steps == nil {
		t.Fatal("Didn't find a solution to a grid that should have needed a guess")
	}

	foundGuess := false
	for i, step := range steps {
		if step.Technique.Name() == "Guess" {
			foundGuess = true
		}
		step.Apply(grid)
		if grid.Invalid() {
			t.Fatal("A solution with a guess in it got us into an invalid grid state. step", i)
		}
	}

	if !foundGuess {
		t.Error("Solution that should have used guess didn't have any guess.")
	}

	if !grid.Solved() {
		t.Error("A solution with a guess said it should solve the puzzle, but it didn't.")
	}

}

func TestStepsDescription(t *testing.T) {

	grid := NewGrid()
	defer grid.Done()

	//It's really brittle that we load techniques in this way... it changes every time we add a new early technique!
	steps := SolveDirections{
		grid,
		[]*CompoundSolveStep{
			{
				FillStep: &SolveStep{
					techniquesByName["Only Legal Number"],
					CellSlice{
						grid.Cell(0, 0),
					},
					IntSlice{1},
					nil,
					nil,
					nil,
				},
			},
			{
				PrecursorSteps: []*SolveStep{
					{
						techniquesByName["Pointing Pair Col"],
						CellSlice{
							grid.Cell(1, 0),
							grid.Cell(1, 1),
						},
						IntSlice{1, 2},
						CellSlice{
							grid.Cell(1, 3),
							grid.Cell(1, 4),
						},
						nil,
						nil,
					},
				},
				FillStep: &SolveStep{
					techniquesByName["Only Legal Number"],
					CellSlice{
						grid.Cell(2, 0),
					},
					IntSlice{2},
					nil,
					nil,
					nil,
				},
			},
		},
	}

	descriptions := steps.Description()

	GOLDEN_DESCRIPTIONS := []string{
		"First, based on the other numbers you've entered, (0,0) can only be a 1. How do we know that? We put 1 in cell (0,0) because 1 is the only remaining valid number for that cell.",
		"Finally, based on the other numbers you've entered, (2,0) can only be a 2. How do we know that? We can't fill any cells right away so first we need to cull some possibilities. First, we remove the possibilities 1 and 2 from cells (1,0) and (1,1) because 1 is only possible in column 0 of block 1, which means it can't be in any other cell in that column not in that block. Finally, we put 2 in cell (2,0) because 2 is the only remaining valid number for that cell.",
	}

	if len(descriptions) != len(GOLDEN_DESCRIPTIONS) {
		t.Fatal("Descriptions had too few items. Got\n", strings.Join(descriptions, "***"), "\nwanted\n", strings.Join(GOLDEN_DESCRIPTIONS, "***"))
	}

	for i := 0; i < len(GOLDEN_DESCRIPTIONS); i++ {
		if descriptions[i] != GOLDEN_DESCRIPTIONS[i] {
			t.Log("Got wrong human solve description: ", descriptions[i], "wanted", GOLDEN_DESCRIPTIONS[i])
			t.Fail()
		}
	}
}

//TODO: this is useful. Should we use this in other tests?
func cellRefsToCells(refs []cellRef, grid *Grid) CellSlice {
	var result CellSlice
	for _, ref := range refs {
		result = append(result, ref.Cell(grid))
	}
	return result
}

func TestPuzzleDifficulty(t *testing.T) {
	grid := NewGrid()
	defer grid.Done()
	grid.LoadSDK(TEST_GRID)

	//We use the cheaper one for testing so it completes faster.
	difficulty := grid.calcluateDifficulty(false)

	if grid.Solved() {
		t.Log("Difficulty shouldn't have changed the underlying grid, but it did.")
		t.Fail()
	}

	if difficulty < 0.0 || difficulty > 1.0 {
		t.Log("The grid's difficulty was outside of allowed bounds.")
		t.Fail()
	}

	puzzleFilenames := []string{"harddifficulty.sdk", "harddifficulty2.sdk"}

	for _, filename := range puzzleFilenames {
		puzzleDifficultyHelper(filename, t)
	}
}

func puzzleDifficultyHelper(filename string, t *testing.T) {
	otherGrid := NewGrid()
	if !otherGrid.LoadSDKFromFile(puzzlePath(filename)) {
		t.Log("Whoops, couldn't load the file to test:", filename)
		t.Fail()
	}

	after := time.After(time.Second * 60)

	done := make(chan bool)

	go func() {
		//We use the cheaper one for testing so it completes faster
		_ = otherGrid.calcluateDifficulty(false)
		done <- true
	}()

	select {
	case <-done:
		//totally fine.
	case <-after:
		//Uh oh.
		t.Log("We never finished solving the hard difficulty puzzle: ", filename)
		t.Fail()
	}
}
<|endoftext|>


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

package unicode

import (
	"github.com/blevesearch/segment"

	"github.com/blevesearch/bleve/analysis"
	"github.com/blevesearch/bleve/registry"
)

const Name = "unicode"

type UnicodeTokenizer struct {
}

func NewUnicodeTokenizer() *UnicodeTokenizer {
	return &UnicodeTokenizer{}
}

func (rt *UnicodeTokenizer) Tokenize(input []byte) analysis.TokenStream {
	ta := []analysis.Token(nil)
	taNext := 0

	rv := make(analysis.TokenStream, 0)

	segmenter := segment.NewWordSegmenterDirect(input)
	start := 0
	pos := 1
	for segmenter.Segment() {
		segmentBytes := segmenter.Bytes()
		end := start + len(segmentBytes)
		if segmenter.Type() != segment.None {
			if taNext >= len(ta) {
				avgSegmentLen := end / (len(rv) + 1)
				if avgSegmentLen < 1 {
					avgSegmentLen = 1
				}

				remainingLen := len(input) - end
				remainingSegments := remainingLen / avgSegmentLen
				if remainingSegments > 1000 {
					remainingSegments = 1000
				}
				if remainingSegments < 1 {
					remainingSegments = 1
				}

				ta = make([]analysis.Token, remainingSegments)
				taNext = 0
			}

			token := &ta[taNext]
			taNext++

			token.Term = segmentBytes
			token.Start = start
			token.End = end
			token.Position = pos
			token.Type = convertType(segmenter.Type())

			rv = append(rv, token)
			pos++
		}
		start = end
	}
	return rv
}

func UnicodeTokenizerConstructor(config map[string]interface{}, cache *registry.Cache) (analysis.Tokenizer, error) {
	return NewUnicodeTokenizer(), nil
}

func init() {
	registry.RegisterTokenizer(Name, UnicodeTokenizerConstructor)
}

func convertType(segmentWordType int) analysis.TokenType {
	switch segmentWordType {
	case segment.Ideo:
		return analysis.Ideographic
	case segment.Kana:
		return analysis.Ideographic
	case segment.Number:
		return analysis.Numeric
	}
	return analysis.AlphaNumeric
}
<commit_msg>unicode.Tokenize() avoids array growth via array of arrays<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

package unicode

import (
	"github.com/blevesearch/segment"

	"github.com/blevesearch/bleve/analysis"
	"github.com/blevesearch/bleve/registry"
)

const Name = "unicode"

type UnicodeTokenizer struct {
}

func NewUnicodeTokenizer() *UnicodeTokenizer {
	return &UnicodeTokenizer{}
}

func (rt *UnicodeTokenizer) Tokenize(input []byte) analysis.TokenStream {
	rvx := make([]analysis.TokenStream, 0, 10) // When rv gets full, append to rvx.
	rv := make(analysis.TokenStream, 0, 1)

	ta := []analysis.Token(nil)
	taNext := 0

	segmenter := segment.NewWordSegmenterDirect(input)
	start := 0
	pos := 1

	guessRemaining := func(end int) int {
		avgSegmentLen := end / (len(rv) + 1)
		if avgSegmentLen < 1 {
			avgSegmentLen = 1
		}

		remainingLen := len(input) - end

		return remainingLen / avgSegmentLen
	}

	for segmenter.Segment() {
		segmentBytes := segmenter.Bytes()
		end := start + len(segmentBytes)
		if segmenter.Type() != segment.None {
			if taNext >= len(ta) {
				remainingSegments := guessRemaining(end)
				if remainingSegments > 1000 {
					remainingSegments = 1000
				}
				if remainingSegments < 1 {
					remainingSegments = 1
				}

				ta = make([]analysis.Token, remainingSegments)
				taNext = 0
			}

			token := &ta[taNext]
			taNext++

			token.Term = segmentBytes
			token.Start = start
			token.End = end
			token.Position = pos
			token.Type = convertType(segmenter.Type())

			if len(rv) >= cap(rv) { // When rv is full, save it into rvx.
				rvx = append(rvx, rv)

				rvCap := cap(rv) * 2
				if rvCap > 256 {
					rvCap = 256
				}

				rv = make(analysis.TokenStream, 0, rvCap) // Next rv cap is bigger.
			}

			rv = append(rv, token)
			pos++
		}
		start = end
	}

	if len(rvx) > 0 {
		n := len(rv)
		for _, r := range rvx {
			n += len(r)
		}
		rall := make(analysis.TokenStream, 0, n)
		for _, r := range rvx {
			rall = append(rall, r...)
		}
		return append(rall, rv...)
	}

	return rv
}

func UnicodeTokenizerConstructor(config map[string]interface{}, cache *registry.Cache) (analysis.Tokenizer, error) {
	return NewUnicodeTokenizer(), nil
}

func init() {
	registry.RegisterTokenizer(Name, UnicodeTokenizerConstructor)
}

func convertType(segmentWordType int) analysis.TokenType {
	switch segmentWordType {
	case segment.Ideo:
		return analysis.Ideographic
	case segment.Kana:
		return analysis.Ideographic
	case segment.Number:
		return analysis.Numeric
	}
	return analysis.AlphaNumeric
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/codegangsta/negroni"

	"github.com/meatballhat/negroni-logrus"
	"github.com/modcloth/mithril"
	"github.com/modcloth/mithril/store"
)

var (
	logLevels = map[string]logrus.Level{
		"debug": logrus.DebugLevel,
		"info":  logrus.InfoLevel,
		"warn":  logrus.WarnLevel,
		"error": logrus.ErrorLevel,
		"fatal": logrus.FatalLevel,
		"panic": logrus.PanicLevel,
	}

	logFormats = map[string]logrus.Formatter{
		"text": new(logrus.TextFormatter),
		"json": new(logrus.JSONFormatter),
	}
)

func main() {
	var (
		logLevelOptions  []string
		logFormatOptions []string
	)

	for s := range logLevels {
		logLevelOptions = append(logLevelOptions, s)
	}

	for s := range logFormats {
		logFormatOptions = append(logFormatOptions, s)
	}

	app := cli.NewApp()
	app.Usage = "HTTP -> AMQP proxy"
	app.Version = fmt.Sprintf("%s (%s)", mithril.Version, mithril.Rev)
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "log-level, l",
			Value: "info",
			Usage: fmt.Sprintf("Log level (options: %s)", strings.Join(logLevelOptions, ",")),
		},
		cli.StringFlag{
			Name:  "log-format, f",
			Value: "text",
			Usage: fmt.Sprintf("Log format (options: %s)", strings.Join(logFormatOptions, ",")),
		},
	}
	app.Commands = []cli.Command{
		{
			Name:        "serve",
			ShortName:   "s",
			Usage:       "start server",
			Description: "Start the AMQP -> HTTP proxy server",
			Action: func(c *cli.Context) {
				level, err := getLogLevel(c)
				if err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
				formatter, err := getLogFormatter(c)
				if err != nil {
					fmt.Println(err)
					os.Exit(1)
				}

				storer, err := store.Open(c.String("storage"), c.String("storage-uri"))
				if err != nil {
					log.Fatal(err)
				}

				amqp, err := mithril.NewAMQPPublisher(c.String("amqp-uri"))
				if err != nil {
					log.Fatal(err)
				}

				n := negroni.New(negroni.NewRecovery(), negronilogrus.NewCustomMiddleware(level, formatter, "mithril"))
				n.UseHandler(mithril.NewServer(storer, amqp))
				n.Run(c.String("bind"))
			},
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:   "debug, d",
					Usage:  "Enable debug logging.",
					EnvVar: "MITHRIL_DEBUG",
				},
				cli.StringFlag{
					Name:   "storage, s",
					Usage:  "Which storage driver to use (see `list-storage` command).",
					Value:  "",
					EnvVar: "MITHRIL_STORAGE",
				},
				cli.StringFlag{
					Name:   "storage-uri, u",
					Usage:  "The url used by the storage driver.",
					Value:  "",
					EnvVar: "MITHRIL_STORAGE_URI",
				},
				cli.StringFlag{
					Name:   "amqp-uri, a",
					Usage:  "The url of the AMQP server",
					Value:  "amqp://guest:guest@localhost:5672",
					EnvVar: "MITHRIL_AMQP_URI",
				},
				cli.StringFlag{
					Name:   "bind, b",
					Usage:  "The address to bind to",
					Value:  ":8371",
					EnvVar: "MITHRIL_BIND",
				},
			},
		},
		{
			Name:        "list-storage",
			ShortName:   "l",
			Usage:       "list storage backends",
			Description: "List the avaliable storage backends for Mithril",
			Action: func(c *cli.Context) {
				store.ShowStorage()
			},
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		fmt.Println(err)
	}
}

func getLogLevel(c *cli.Context) (level logrus.Level, err error) {
	level, ok := logLevels[c.GlobalString("log-level")]
	if !ok {
		return 0, fmt.Errorf("invalid log level %s", c.GlobalString("log-level"))
	}

	return level, nil
}

func getLogFormatter(c *cli.Context) (formatter logrus.Formatter, err error) {
	formatter, ok := logFormats[c.GlobalString("log-format")]
	if !ok {
		return nil, fmt.Errorf("invalid log format %s", c.GlobalString("log-format"))
	}

	return formatter, nil
}
<commit_msg>Remove unused debug flag<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/codegangsta/negroni"

	"github.com/meatballhat/negroni-logrus"
	"github.com/modcloth/mithril"
	"github.com/modcloth/mithril/store"
)

var (
	logLevels = map[string]logrus.Level{
		"debug": logrus.DebugLevel,
		"info":  logrus.InfoLevel,
		"warn":  logrus.WarnLevel,
		"error": logrus.ErrorLevel,
		"fatal": logrus.FatalLevel,
		"panic": logrus.PanicLevel,
	}

	logFormats = map[string]logrus.Formatter{
		"text": new(logrus.TextFormatter),
		"json": new(logrus.JSONFormatter),
	}
)

func main() {
	var (
		logLevelOptions  []string
		logFormatOptions []string
	)

	for s := range logLevels {
		logLevelOptions = append(logLevelOptions, s)
	}

	for s := range logFormats {
		logFormatOptions = append(logFormatOptions, s)
	}

	app := cli.NewApp()
	app.Usage = "HTTP -> AMQP proxy"
	app.Version = fmt.Sprintf("%s (%s)", mithril.Version, mithril.Rev)
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "log-level, l",
			Value: "info",
			Usage: fmt.Sprintf("Log level (options: %s)", strings.Join(logLevelOptions, ",")),
		},
		cli.StringFlag{
			Name:  "log-format, f",
			Value: "text",
			Usage: fmt.Sprintf("Log format (options: %s)", strings.Join(logFormatOptions, ",")),
		},
	}
	app.Commands = []cli.Command{
		{
			Name:        "serve",
			ShortName:   "s",
			Usage:       "start server",
			Description: "Start the AMQP -> HTTP proxy server",
			Action: func(c *cli.Context) {
				level, err := getLogLevel(c)
				if err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
				formatter, err := getLogFormatter(c)
				if err != nil {
					fmt.Println(err)
					os.Exit(1)
				}

				storer, err := store.Open(c.String("storage"), c.String("storage-uri"))
				if err != nil {
					log.Fatal(err)
				}

				amqp, err := mithril.NewAMQPPublisher(c.String("amqp-uri"))
				if err != nil {
					log.Fatal(err)
				}

				n := negroni.New(negroni.NewRecovery(), negronilogrus.NewCustomMiddleware(level, formatter, "mithril"))
				n.UseHandler(mithril.NewServer(storer, amqp))
				n.Run(c.String("bind"))
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:   "storage, s",
					Usage:  "Which storage driver to use (see `list-storage` command).",
					Value:  "",
					EnvVar: "MITHRIL_STORAGE",
				},
				cli.StringFlag{
					Name:   "storage-uri, u",
					Usage:  "The url used by the storage driver.",
					Value:  "",
					EnvVar: "MITHRIL_STORAGE_URI",
				},
				cli.StringFlag{
					Name:   "amqp-uri, a",
					Usage:  "The url of the AMQP server",
					Value:  "amqp://guest:guest@localhost:5672",
					EnvVar: "MITHRIL_AMQP_URI",
				},
				cli.StringFlag{
					Name:   "bind, b",
					Usage:  "The address to bind to",
					Value:  ":8371",
					EnvVar: "MITHRIL_BIND",
				},
			},
		},
		{
			Name:        "list-storage",
			ShortName:   "l",
			Usage:       "list storage backends",
			Description: "List the avaliable storage backends for Mithril",
			Action: func(c *cli.Context) {
				store.ShowStorage()
			},
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		fmt.Println(err)
	}
}

func getLogLevel(c *cli.Context) (level logrus.Level, err error) {
	level, ok := logLevels[c.GlobalString("log-level")]
	if !ok {
		return 0, fmt.Errorf("invalid log level %s", c.GlobalString("log-level"))
	}

	return level, nil
}

func getLogFormatter(c *cli.Context) (formatter logrus.Formatter, err error) {
	formatter, ok := logFormats[c.GlobalString("log-format")]
	if !ok {
		return nil, fmt.Errorf("invalid log format %s", c.GlobalString("log-format"))
	}

	return formatter, nil
}
<|endoftext|>


<commit_before>package models

import (
	"bytes"
	"html/template"
	"math"
	"github.com/astaxie/beego/orm"
	"github.com/lifei6671/mindoc/conf"
	"fmt"
)

type DocumentTree struct {
	DocumentId   int               `json:"id"`
	DocumentName string            `json:"text"`
	ParentId     interface{}       `json:"parent"`
	Identify     string            `json:"identify"`
	BookIdentify string            `json:"-"`
	Version      int64             `json:"version"`
	State        *DocumentSelected `json:"-"`
	AAttrs		 map[string]interface{}		`json:"-"`
}
type DocumentSelected struct {
	Selected bool `json:"selected"`
	Opened   bool `json:"opened"`
}

//获取项目的文档树状结构
func (m *Document) FindDocumentTree(bookId int) ([]*DocumentTree, error) {
	o := orm.NewOrm()

	trees := make([]*DocumentTree, 0)

	var docs []*Document

	count, err := o.QueryTable(m).Filter("book_id", bookId).OrderBy("order_sort", "document_id").Limit(math.MaxInt32).All(&docs, "document_id", "version", "document_name", "parent_id", "identify","is_open")

	if err != nil {
		return trees, err
	}
	book, _ := NewBook().Find(bookId)

	trees = make([]*DocumentTree, count)

	for index, item := range docs {
		tree := &DocumentTree{}
		if index == 0 {
			tree.State = &DocumentSelected{Selected: true, Opened: true}
			tree.AAttrs = map[string]interface{}{ "is_open": true}
		}else if item.IsOpen == 1 {
			tree.State = &DocumentSelected{Selected: false, Opened: true}
			tree.AAttrs = map[string]interface{}{ "is_open": true}
		}
		tree.DocumentId = item.DocumentId
		tree.Identify = item.Identify
		tree.Version = item.Version
		tree.BookIdentify = book.Identify
		if item.ParentId > 0 {
			tree.ParentId = item.ParentId
		} else {
			tree.ParentId = "#"
		}

		tree.DocumentName = item.DocumentName

		trees[index] = tree
	}

	return trees, nil
}

func (m *Document) CreateDocumentTreeForHtml(bookId, selectedId int) (string, error) {
	trees, err := m.FindDocumentTree(bookId)
	if err != nil {
		return "", err
	}
	parentId := getSelectedNode(trees, selectedId)

	buf := bytes.NewBufferString("")

	getDocumentTree(trees, 0, selectedId, parentId, buf)

	return buf.String(), nil

}

//使用递归的方式获取指定ID的顶级ID
func getSelectedNode(array []*DocumentTree, parent_id int) int {

	for _, item := range array {
		if _, ok := item.ParentId.(string); ok && item.DocumentId == parent_id {
			return item.DocumentId
		} else if pid, ok := item.ParentId.(int); ok && item.DocumentId == parent_id {
			return getSelectedNode(array, pid)
		}
	}
	return 0
}

func getDocumentTree(array []*DocumentTree, parentId int, selectedId int, selectedParentId int, buf *bytes.Buffer) {
	buf.WriteString("
")

	for _, item := range array {
		pid := 0

		if p, ok := item.ParentId.(int); ok {
			pid = p
		}
		if pid == parentId {

			selected := ""
			if item.DocumentId == selectedId {
				selected = ` class="jstree-clicked"`
			}
			selectedLi := ""
			if item.DocumentId == selectedParentId || (item.State != nil && item.State.Opened) {
				selectedLi = ` class="jstree-open"`
			}
			buf.WriteString(fmt.Sprintf("
%s",item.Version,selected,template.HTMLEscapeString(item.DocumentName)))

			for _, sub := range array {
				if p, ok := sub.ParentId.(int); ok && p == item.DocumentId {
					getDocumentTree(array, p, selectedId, selectedParentId, buf)
					break
				}
			}
			buf.WriteString("
")

		}
	}
	buf.WriteString("
")
}
<commit_msg>fix:修复编辑状态下文档展开方式读取错误<commit_after>package models

import (
	"bytes"
	"html/template"
	"math"
	"github.com/astaxie/beego/orm"
	"github.com/lifei6671/mindoc/conf"
	"fmt"
)

type DocumentTree struct {
	DocumentId   int               `json:"id"`
	DocumentName string            `json:"text"`
	ParentId     interface{}       `json:"parent"`
	Identify     string            `json:"identify"`
	BookIdentify string            `json:"-"`
	Version      int64             `json:"version"`
	State        *DocumentSelected `json:"-"`
	AAttrs		 map[string]interface{}		`json:"a_attr"`
}
type DocumentSelected struct {
	Selected bool `json:"selected"`
	Opened   bool `json:"opened"`
}

//获取项目的文档树状结构
func (m *Document) FindDocumentTree(bookId int) ([]*DocumentTree, error) {
	o := orm.NewOrm()

	trees := make([]*DocumentTree, 0)

	var docs []*Document

	count, err := o.QueryTable(m).Filter("book_id", bookId).OrderBy("order_sort", "document_id").Limit(math.MaxInt32).All(&docs, "document_id", "version", "document_name", "parent_id", "identify","is_open")

	if err != nil {
		return trees, err
	}
	book, _ := NewBook().Find(bookId)

	trees = make([]*DocumentTree, count)

	for index, item := range docs {
		tree := &DocumentTree{}
		if index == 0 {
			tree.State = &DocumentSelected{Selected: true, Opened: true}
			tree.AAttrs = map[string]interface{}{ "is_open": true}
		}else if item.IsOpen == 1 {
			tree.State = &DocumentSelected{Selected: false, Opened: true}
			tree.AAttrs = map[string]interface{}{ "is_open": true}
		}
		tree.DocumentId = item.DocumentId
		tree.Identify = item.Identify
		tree.Version = item.Version
		tree.BookIdentify = book.Identify
		if item.ParentId > 0 {
			tree.ParentId = item.ParentId
		} else {
			tree.ParentId = "#"
		}

		tree.DocumentName = item.DocumentName

		trees[index] = tree
	}

	return trees, nil
}

func (m *Document) CreateDocumentTreeForHtml(bookId, selectedId int) (string, error) {
	trees, err := m.FindDocumentTree(bookId)
	if err != nil {
		return "", err
	}
	parentId := getSelectedNode(trees, selectedId)

	buf := bytes.NewBufferString("")

	getDocumentTree(trees, 0, selectedId, parentId, buf)

	return buf.String(), nil

}

//使用递归的方式获取指定ID的顶级ID
func getSelectedNode(array []*DocumentTree, parent_id int) int {

	for _, item := range array {
		if _, ok := item.ParentId.(string); ok && item.DocumentId == parent_id {
			return item.DocumentId
		} else if pid, ok := item.ParentId.(int); ok && item.DocumentId == parent_id {
			return getSelectedNode(array, pid)
		}
	}
	return 0
}

func getDocumentTree(array []*DocumentTree, parentId int, selectedId int, selectedParentId int, buf *bytes.Buffer) {
	buf.WriteString("
")

	for _, item := range array {
		pid := 0

		if p, ok := item.ParentId.(int); ok {
			pid = p
		}
		if pid == parentId {

			selected := ""
			if item.DocumentId == selectedId {
				selected = ` class="jstree-clicked"`
			}
			selectedLi := ""
			if item.DocumentId == selectedParentId || (item.State != nil && item.State.Opened) {
				selectedLi = ` class="jstree-open"`
			}
			buf.WriteString(fmt.Sprintf("
%s",item.Version,selected,template.HTMLEscapeString(item.DocumentName)))

			for _, sub := range array {
				if p, ok := sub.ParentId.(int); ok && p == item.DocumentId {
					getDocumentTree(array, p, selectedId, selectedParentId, buf)
					break
				}
			}
			buf.WriteString("
")

		}
	}
	buf.WriteString("
")
}
<|endoftext|>


<commit_before>package mdata

import "github.com/raintank/metrictank/mdata/chunk"

type mockSearchResult struct {
	chunks []chunk.IterGen
	err    error
}

type mockStore struct {
	CurrCall int
	Results  []mockSearchResult
}

func NewMockStore() *mockStore {
	d := &mockStore{
		CurrCall: 0,
		Results:  make([]mockSearchResult, 0),
	}
	return d
}

func (c *mockStore) AddMockResult(chunks []chunk.IterGen, err error) {
	// copy chunks because we don't want to modify the source
	chunksCopy := make([]chunk.IterGen, len(chunks))
	copy(chunksCopy, chunks)
	c.Results = append(c.Results, mockSearchResult{chunksCopy, err})
}

func (c *mockStore) ResetMock() {
	c.Results = c.Results[:0]
	c.CurrCall = 0
}

func (c *mockStore) Add(cwr *ChunkWriteRequest) {
}

func (c *mockStore) Search(key string, start, end uint32) ([]chunk.IterGen, error) {
	if c.CurrCall < len(c.Results) {
		res := c.Results[c.CurrCall]
		c.CurrCall++
		return res.chunks, res.err
	}
	return make([]chunk.IterGen, 0), nil
}

func (c *mockStore) Stop() {
}
<commit_msg>add comments to mock store<commit_after>package mdata

import "github.com/raintank/metrictank/mdata/chunk"

type mockSearchResult struct {
	chunks []chunk.IterGen
	err    error
}

// a data store that satisfies the interface `mdata.Store`.
//
// it is intended to be used in unit tests where it is necessary
// that the backend store returns values, but we don't want to
// involve a real store like for example Cassandra.
// the mockstore simply returns the results it has gotten added
// via the AddMockResult() method.
// this can be extended if future unit tests require the mock
// store to be smarter, or for example if they require it to
// keep what has been passed into Add().
type mockStore struct {
	// index for the search results, pointing to which result will
	// be returned next
	CurrCall int
	// a list of results that will be returned by the Search() method
	Results []mockSearchResult
}

func NewMockStore() *mockStore {
	d := &mockStore{
		CurrCall: 0,
		Results:  make([]mockSearchResult, 0),
	}
	return d
}

// add a result to be returned on Search()
func (c *mockStore) AddMockResult(chunks []chunk.IterGen, err error) {
	// copy chunks because we don't want to modify the source
	chunksCopy := make([]chunk.IterGen, len(chunks))
	copy(chunksCopy, chunks)
	c.Results = append(c.Results, mockSearchResult{chunksCopy, err})
}

// flush and reset the mock
func (c *mockStore) ResetMock() {
	c.Results = c.Results[:0]
	c.CurrCall = 0
}

// currently that only exists to satisfy the interface
// might be extended to be useful in the future
func (c *mockStore) Add(cwr *ChunkWriteRequest) {
}

// returns the mock results, ignoring the search parameters
func (c *mockStore) Search(key string, start, end uint32) ([]chunk.IterGen, error) {
	if c.CurrCall < len(c.Results) {
		res := c.Results[c.CurrCall]
		c.CurrCall++
		return res.chunks, res.err
	}
	return make([]chunk.IterGen, 0), nil
}

func (c *mockStore) Stop() {
}
<|endoftext|>


<commit_before>package ptgen

import (
	"time"

	"github.com/intervention-engine/fhir/models"
	. "gopkg.in/check.v1"
)

type MedicationSuite struct {
}

var _ = Suite(&MedicationSuite{})

func (m *MedicationSuite) TestGenerateMedication(c *C) {
	mmd := LoadMedications()
	t := time.Now()
	med := GenerateMedication(3, &models.FHIRDateTime{Time: t, Precision: models.Timestamp}, mmd)
	c.Assert(med.MedicationCodeableConcept.Text, Equals, "Lisinopril 5mg Oral Tablet")
	c.Assert(med.EffectivePeriod.Start.Time, Equals, t)
}
<commit_msg>Fix broken test that wasn't updated after adding support for medication end dates<commit_after>package ptgen

import (
	"time"

	"github.com/intervention-engine/fhir/models"
	. "gopkg.in/check.v1"
)

type MedicationSuite struct {
}

var _ = Suite(&MedicationSuite{})

func (m *MedicationSuite) TestGenerateMedication(c *C) {
	mmd := LoadMedications()
	t := time.Now()
	start := models.FHIRDateTime{Time: t, Precision: models.Timestamp}
	end := models.FHIRDateTime{Time: t.AddDate(0, 3, 0), Precision: models.Date}
	med := GenerateMedication(3, &start, &end, mmd)
	c.Assert(med.MedicationCodeableConcept.Text, Equals, "Lisinopril 5mg Oral Tablet")
	c.Assert(med.EffectivePeriod.Start.Time, Equals, t)
	c.Assert(med.EffectivePeriod.End.Time, Equals, t.AddDate(0, 3, 0))
}
<|endoftext|>


<commit_before>package main

import (
	"configuration"
	"coordinator"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"runtime"
	"server"
	"strconv"
	"syscall"
	"time"
)

const (
	version = "dev"
	gitSha  = "HEAD"
)

func waitForSignals(stopped <-chan bool) {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT)
	for {
		sig := <-ch
		fmt.Printf("Received signal: %s\n", sig.String())
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			runtime.SetCPUProfileRate(0)
			<-stopped
			os.Exit(0)
		}
	}
}

func startProfiler(filename *string) error {
	if filename == nil || *filename == "" {
		return nil
	}

	cpuProfileFile, err := os.Create(*filename)
	if err != nil {
		return err
	}
	runtime.SetCPUProfileRate(500)
	stopped := make(chan bool)

	go waitForSignals(stopped)

	go func() {
		for {
			select {
			default:
				data := runtime.CPUProfile()
				if data == nil {
					cpuProfileFile.Close()
					stopped <- true
					break
				}
				cpuProfileFile.Write(data)
			}
		}
	}()
	return nil
}

func main() {
	fileName := flag.String("config", "config.json.sample", "Config file")
	wantsVersion := flag.Bool("v", false, "Get version number")
	resetRootPassword := flag.Bool("reset-root", false, "Reset root password")
	pidFile := flag.String("pidfile", "", "the pid file")
	cpuProfiler := flag.String("cpuprofile", "", "filename where cpu profile data will be written")

	runtime.GOMAXPROCS(runtime.NumCPU())
	flag.Parse()

	startProfiler(cpuProfiler)

	if wantsVersion != nil && *wantsVersion {
		fmt.Printf("InfluxDB v%s (git: %s)\n", version, gitSha)
		return
	}
	config := configuration.LoadConfiguration(*fileName)

	if pidFile != nil && *pidFile != "" {
		pid := strconv.Itoa(os.Getpid())
		if err := ioutil.WriteFile(*pidFile, []byte(pid), 0644); err != nil {
			panic(err)
		}
	}

	log.Println("Starting Influx Server...")
	os.MkdirAll(config.RaftDir, 0744)
	os.MkdirAll(config.DataDir, 0744)
	server, err := server.NewServer(config)
	if err != nil {
		panic(err)
	}

	if *resetRootPassword {
		// TODO: make this not suck
		// This is ghetto as hell, but it'll work for now.
		go func() {
			time.Sleep(2 * time.Second) // wait for the raft server to join the cluster

			fmt.Printf("Resetting root's password to %s", coordinator.DEFAULT_ROOT_PWD)
			if err := server.RaftServer.CreateRootUser(); err != nil {
				panic(err)
			}
		}()
	}
	server.ListenAndServe()
}
<commit_msg>add some ascii art on startup<commit_after>package main

import (
	"configuration"
	"coordinator"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"runtime"
	"server"
	"strconv"
	"syscall"
	"time"
)

const (
	version = "dev"
	gitSha  = "HEAD"
)

func waitForSignals(stopped <-chan bool) {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT)
	for {
		sig := <-ch
		fmt.Printf("Received signal: %s\n", sig.String())
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			runtime.SetCPUProfileRate(0)
			<-stopped
			os.Exit(0)
		}
	}
}

func startProfiler(filename *string) error {
	if filename == nil || *filename == "" {
		return nil
	}

	cpuProfileFile, err := os.Create(*filename)
	if err != nil {
		return err
	}
	runtime.SetCPUProfileRate(500)
	stopped := make(chan bool)

	go waitForSignals(stopped)

	go func() {
		for {
			select {
			default:
				data := runtime.CPUProfile()
				if data == nil {
					cpuProfileFile.Close()
					stopped <- true
					break
				}
				cpuProfileFile.Write(data)
			}
		}
	}()
	return nil
}

func main() {
	fileName := flag.String("config", "config.json.sample", "Config file")
	wantsVersion := flag.Bool("v", false, "Get version number")
	resetRootPassword := flag.Bool("reset-root", false, "Reset root password")
	pidFile := flag.String("pidfile", "", "the pid file")
	cpuProfiler := flag.String("cpuprofile", "", "filename where cpu profile data will be written")

	runtime.GOMAXPROCS(runtime.NumCPU())
	flag.Parse()

	startProfiler(cpuProfiler)

	if wantsVersion != nil && *wantsVersion {
		fmt.Printf("InfluxDB v%s (git: %s)\n", version, gitSha)
		return
	}
	config := configuration.LoadConfiguration(*fileName)

	if pidFile != nil && *pidFile != "" {
		pid := strconv.Itoa(os.Getpid())
		if err := ioutil.WriteFile(*pidFile, []byte(pid), 0644); err != nil {
			panic(err)
		}
	}

	log.Println("Starting Influx Server...")
	fmt.Printf(`
+---------------------------------------------+
|  _____        __ _            _____  ____   |
| |_   _|      / _| |          |  __ \|  _ \  |
|   | |  _ __ | |_| |_   ___  _| |  | | |_) | |
|   | | | '_ \|  _| | | | \ \/ / |  | |  _ <  |
|  _| |_| | | | | | | |_| |>  <| |__| | |_) | |
| |_____|_| |_|_| |_|\__,_/_/\_\_____/|____/  |
+---------------------------------------------+

`)
	os.MkdirAll(config.RaftDir, 0744)
	os.MkdirAll(config.DataDir, 0744)
	server, err := server.NewServer(config)
	if err != nil {
		panic(err)
	}

	if *resetRootPassword {
		// TODO: make this not suck
		// This is ghetto as hell, but it'll work for now.
		go func() {
			time.Sleep(2 * time.Second) // wait for the raft server to join the cluster

			fmt.Printf("Resetting root's password to %s", coordinator.DEFAULT_ROOT_PWD)
			if err := server.RaftServer.CreateRootUser(); err != nil {
				panic(err)
			}
		}()
	}
	server.ListenAndServe()
}
<|endoftext|>


<commit_before>package gitosis

import (
	ini "github.com/kless/goconfig/config"
	"github.com/timeredbull/tsuru/config"
	. "launchpad.net/gocheck"
	"os"
	"os/exec"
	"path"
)

func (s *S) TestAddGroup(c *C) {
	err := AddGroup("someGroup")
	c.Assert(err, IsNil)

	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	//ensures that project have been added to gitosis.conf
	c.Assert(conf.HasSection("group someGroup"), Equals, true)

	//ensures that file is not overriden when a new project is added
	err = AddGroup("someOtherGroup")
	c.Assert(err, IsNil)
	// it should have both sections
	conf, err = ini.ReadDefault(path.Join(s.gitRoot, "gitosis-admin/gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group someGroup"), Equals, true)
	c.Assert(conf.HasSection("group someOtherGroup"), Equals, true)
}

func (s *S) TestAddGroupShouldReturnErrorWhenSectionAlreadyExists(c *C) {
	err := AddGroup("aGroup")
	c.Assert(err, IsNil)

	err = AddGroup("aGroup")
	c.Assert(err, NotNil)
}

func (s *S) TestAddGroupShouldCommitAndPushChangesToGitosisBare(c *C) {
	err := AddGroup("gandalf")
	c.Assert(err, IsNil)
	pwd := os.Getenv("PWD")
	os.Chdir(s.gitosisBare)
	bareOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%H").CombinedOutput()
	c.Assert(err, IsNil)

	os.Chdir(s.gitosisRepo)
	repoOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%H").CombinedOutput()
	c.Assert(err, IsNil)

	os.Chdir(pwd)

	c.Assert(string(repoOutput), Equals, string(bareOutput))
}

func (s *S) TestRemoveGroup(c *C) {
	err := AddGroup("someGroup")
	c.Assert(err, IsNil)

	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group someGroup"), Equals, true)

	err = RemoveGroup("someGroup")
	conf, err = ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group someGroup"), Equals, false)

	pwd := os.Getenv("PWD")
	os.Chdir(s.gitosisBare)
	bareOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%s").CombinedOutput()
	c.Assert(err, IsNil)
	os.Chdir(pwd)

	expected := "Removing group someGroup from gitosis.conf"

	c.Assert(string(bareOutput), Equals, expected)
}

func (s *S) TestRemoveGroupCommitAndPushesChanges(c *C) {
	err := AddGroup("testGroup")
	c.Assert(err, IsNil)

	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group testGroup"), Equals, true)

	err = RemoveGroup("testGroup")
	conf, err = ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group testGroup"), Equals, false)
}

func (s *S) TestAddMemberToGroup(c *C) {
	err := AddGroup("take-over-the-world") // test also with a inexistent project
	c.Assert(err, IsNil)
	err = AddMember("take-over-the-world", "brain")
	c.Assert(err, IsNil)
	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group take-over-the-world"), Equals, true)
	c.Assert(conf.HasOption("group take-over-the-world", "members"), Equals, true)
	members, err := conf.String("group take-over-the-world", "members")
	c.Assert(err, IsNil)
	c.Assert(members, Equals, "brain")
}

func (s *S) TestAddMemberToGroupCommitsAndPush(c *C) {
	err := AddGroup("someTeam")
	c.Assert(err, IsNil)
	err = AddMember("someTeam", "brain")
	pwd := os.Getenv("PWD")
	os.Chdir(s.gitosisBare)
	bareOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%s").CombinedOutput()
	c.Assert(err, IsNil)

	os.Chdir(pwd)

	commitMsg := "Adding member brain to group someTeam"

	c.Assert(string(bareOutput), Equals, commitMsg)
}

func (s *S) TestAddTwoMembersToGroup(c *C) {
	err := AddGroup("pink-floyd")
	c.Assert(err, IsNil)
	err = AddMember("pink-floyd", "one-of-these-days")
	c.Assert(err, IsNil)
	err = AddMember("pink-floyd", "comfortably-numb")
	c.Assert(err, IsNil)
	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	members, err := conf.String("group pink-floyd", "members")
	c.Assert(err, IsNil)
	c.Assert(members, Equals, "one-of-these-days comfortably-numb")
}

func (s *S) TestAddMemberToGroupReturnsErrorIfTheMemberIsAlreadyInTheGroup(c *C) {
	err := AddGroup("pink-floyd")
	c.Assert(err, IsNil)
	err = AddMember("pink-floyd", "time")
	c.Assert(err, IsNil)
	err = AddMember("pink-floyd", "time")
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, "^This user is already member of this group$")
}

func (s *S) TestAddMemberToAGroupThatDoesNotExistReturnError(c *C) {
	err := AddMember("pink-floyd", "one-of-these-days")
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, "^Group not found$")
}

func (s *S) TestAddAndCommit(c *C) {
	confPath := path.Join(s.gitosisRepo, "gitosis.conf")
	conf, err := ini.ReadDefault(confPath)
	c.Assert(err, IsNil)
	conf.AddSection("foo bar")
	PushToGitosis("Some commit message")

	pwd := os.Getenv("PWD")
	os.Chdir(s.gitosisBare)
	bareOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%s").CombinedOutput()
	c.Assert(err, IsNil)
	os.Chdir(pwd)

	c.Assert(string(bareOutput), Equals, "Some commit message")
}

func (s *S) TestConfPathReturnsGitosisConfPath(c *C) {
	repoPath, err := config.GetString("git:gitosis-repo")
	expected := path.Join(repoPath, "gitosis.conf")
	obtained, err := ConfPath()
	c.Assert(err, IsNil)
	c.Assert(obtained, Equals, expected)
}
<commit_msg>gitosis: removed useless comment<commit_after>package gitosis

import (
	ini "github.com/kless/goconfig/config"
	"github.com/timeredbull/tsuru/config"
	. "launchpad.net/gocheck"
	"os"
	"os/exec"
	"path"
)

func (s *S) TestAddGroup(c *C) {
	err := AddGroup("someGroup")
	c.Assert(err, IsNil)

	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	//ensures that project have been added to gitosis.conf
	c.Assert(conf.HasSection("group someGroup"), Equals, true)

	//ensures that file is not overriden when a new project is added
	err = AddGroup("someOtherGroup")
	c.Assert(err, IsNil)
	// it should have both sections
	conf, err = ini.ReadDefault(path.Join(s.gitRoot, "gitosis-admin/gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group someGroup"), Equals, true)
	c.Assert(conf.HasSection("group someOtherGroup"), Equals, true)
}

func (s *S) TestAddGroupShouldReturnErrorWhenSectionAlreadyExists(c *C) {
	err := AddGroup("aGroup")
	c.Assert(err, IsNil)

	err = AddGroup("aGroup")
	c.Assert(err, NotNil)
}

func (s *S) TestAddGroupShouldCommitAndPushChangesToGitosisBare(c *C) {
	err := AddGroup("gandalf")
	c.Assert(err, IsNil)
	pwd := os.Getenv("PWD")
	os.Chdir(s.gitosisBare)
	bareOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%H").CombinedOutput()
	c.Assert(err, IsNil)

	os.Chdir(s.gitosisRepo)
	repoOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%H").CombinedOutput()
	c.Assert(err, IsNil)

	os.Chdir(pwd)

	c.Assert(string(repoOutput), Equals, string(bareOutput))
}

func (s *S) TestRemoveGroup(c *C) {
	err := AddGroup("someGroup")
	c.Assert(err, IsNil)

	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group someGroup"), Equals, true)

	err = RemoveGroup("someGroup")
	conf, err = ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group someGroup"), Equals, false)

	pwd := os.Getenv("PWD")
	os.Chdir(s.gitosisBare)
	bareOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%s").CombinedOutput()
	c.Assert(err, IsNil)
	os.Chdir(pwd)

	expected := "Removing group someGroup from gitosis.conf"

	c.Assert(string(bareOutput), Equals, expected)
}

func (s *S) TestRemoveGroupCommitAndPushesChanges(c *C) {
	err := AddGroup("testGroup")
	c.Assert(err, IsNil)

	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group testGroup"), Equals, true)

	err = RemoveGroup("testGroup")
	conf, err = ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group testGroup"), Equals, false)
}

func (s *S) TestAddMemberToGroup(c *C) {
	err := AddGroup("take-over-the-world")
	c.Assert(err, IsNil)
	err = AddMember("take-over-the-world", "brain")
	c.Assert(err, IsNil)
	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	c.Assert(err, IsNil)
	c.Assert(conf.HasSection("group take-over-the-world"), Equals, true)
	c.Assert(conf.HasOption("group take-over-the-world", "members"), Equals, true)
	members, err := conf.String("group take-over-the-world", "members")
	c.Assert(err, IsNil)
	c.Assert(members, Equals, "brain")
}

func (s *S) TestAddMemberToGroupCommitsAndPush(c *C) {
	err := AddGroup("someTeam")
	c.Assert(err, IsNil)
	err = AddMember("someTeam", "brain")
	pwd := os.Getenv("PWD")
	os.Chdir(s.gitosisBare)
	bareOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%s").CombinedOutput()
	c.Assert(err, IsNil)

	os.Chdir(pwd)

	commitMsg := "Adding member brain to group someTeam"

	c.Assert(string(bareOutput), Equals, commitMsg)
}

func (s *S) TestAddTwoMembersToGroup(c *C) {
	err := AddGroup("pink-floyd")
	c.Assert(err, IsNil)
	err = AddMember("pink-floyd", "one-of-these-days")
	c.Assert(err, IsNil)
	err = AddMember("pink-floyd", "comfortably-numb")
	c.Assert(err, IsNil)
	conf, err := ini.ReadDefault(path.Join(s.gitosisRepo, "gitosis.conf"))
	members, err := conf.String("group pink-floyd", "members")
	c.Assert(err, IsNil)
	c.Assert(members, Equals, "one-of-these-days comfortably-numb")
}

func (s *S) TestAddMemberToGroupReturnsErrorIfTheMemberIsAlreadyInTheGroup(c *C) {
	err := AddGroup("pink-floyd")
	c.Assert(err, IsNil)
	err = AddMember("pink-floyd", "time")
	c.Assert(err, IsNil)
	err = AddMember("pink-floyd", "time")
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, "^This user is already member of this group$")
}

func (s *S) TestAddMemberToAGroupThatDoesNotExistReturnError(c *C) {
	err := AddMember("pink-floyd", "one-of-these-days")
	c.Assert(err, NotNil)
	c.Assert(err, ErrorMatches, "^Group not found$")
}

func (s *S) TestAddAndCommit(c *C) {
	confPath := path.Join(s.gitosisRepo, "gitosis.conf")
	conf, err := ini.ReadDefault(confPath)
	c.Assert(err, IsNil)
	conf.AddSection("foo bar")
	PushToGitosis("Some commit message")

	pwd := os.Getenv("PWD")
	os.Chdir(s.gitosisBare)
	bareOutput, err := exec.Command("git", "log", "-1", "--pretty=format:%s").CombinedOutput()
	c.Assert(err, IsNil)
	os.Chdir(pwd)

	c.Assert(string(bareOutput), Equals, "Some commit message")
}

func (s *S) TestConfPathReturnsGitosisConfPath(c *C) {
	repoPath, err := config.GetString("git:gitosis-repo")
	expected := path.Join(repoPath, "gitosis.conf")
	obtained, err := ConfPath()
	c.Assert(err, IsNil)
	c.Assert(obtained, Equals, expected)
}
<|endoftext|>