<commit_before>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package context

import (
	"net/http"

	"github.com/issue9/logs"
	"github.com/issue9/web/errors"
)

// Critical 输出一条日志到 CRITICAL 日志通道，
// 并向用户输出一个指定状态码的页面。
// 若是输出日志的过程中出错，则 panic
func (ctx *Context) Critical(status int, v ...interface{}) {
	logs.CRITICAL().Output(2, errors.TraceStack(2, v...))

	ctx.RenderStatus(status)
}

// Error 输出一条日志到 ERROR 日志通道，
// 并向用户输出一个指定状态码的页面。
// 若是输出日志的过程中出错，则 panic
func (ctx *Context) Error(status int, v ...interface{}) {
	logs.ERROR().Output(2, errors.TraceStack(2, v...))

	ctx.RenderStatus(status)
}

// Critical 输出一条日志到 CRITICAL 日志通道，
// 并向用户输出一个指定状态码的页面。
// 若是输出日志的过程中出错，则 panic
func Critical(w http.ResponseWriter, status int, v ...interface{}) {
	logs.CRITICAL().Output(2, errors.TraceStack(2, v...))

	RenderStatus(w, status)
}

// Error 输出一条日志到 ERROR 日志通道，
// 并向用户输出一个指定状态码的页面。
// 若是输出日志的过程中出错，则 panic
func Error(w http.ResponseWriter, status int, v ...interface{}) {
	logs.ERROR().Output(2, errors.TraceStack(2, v...))

	RenderStatus(w, status)
}

// Panic 以指定的状态码抛出异常
//
// 与 Error 的不同在于：
// Error 不会主动退出当前协程，而 Panic 则会触发 panic，退出当前协程。
func Panic(status int) {
	panic(errors.HTTP(status))
}
<commit_msg>添加 context.Panic 方法<commit_after>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package context

import (
	"net/http"

	"github.com/issue9/logs"
	"github.com/issue9/web/errors"
)

// Critical 输出一条日志到 CRITICAL 日志通道，
// 并向用户输出一个指定状态码的页面。
// 若是输出日志的过程中出错，则 panic
func (ctx *Context) Critical(status int, v ...interface{}) {
	logs.CRITICAL().Output(2, errors.TraceStack(2, v...))

	ctx.RenderStatus(status)
}

// Error 输出一条日志到 ERROR 日志通道，
// 并向用户输出一个指定状态码的页面。
// 若是输出日志的过程中出错，则 panic
func (ctx *Context) Error(status int, v ...interface{}) {
	logs.ERROR().Output(2, errors.TraceStack(2, v...))

	ctx.RenderStatus(status)
}

// Panic 以指定的状态码抛出异常
//
// 与 Error 的不同在于：
// Error 不会主动退出当前协程，而 Panic 则会触发 panic，退出当前协程。
func (ctx *Context) Panic(status int) {
	Panic(status)
}

// Critical 输出一条日志到 CRITICAL 日志通道，
// 并向用户输出一个指定状态码的页面。
// 若是输出日志的过程中出错，则 panic
func Critical(w http.ResponseWriter, status int, v ...interface{}) {
	logs.CRITICAL().Output(2, errors.TraceStack(2, v...))

	RenderStatus(w, status)
}

// Error 输出一条日志到 ERROR 日志通道，
// 并向用户输出一个指定状态码的页面。
// 若是输出日志的过程中出错，则 panic
func Error(w http.ResponseWriter, status int, v ...interface{}) {
	logs.ERROR().Output(2, errors.TraceStack(2, v...))

	RenderStatus(w, status)
}

// Panic 以指定的状态码抛出异常
//
// 与 Error 的不同在于：
// Error 不会主动退出当前协程，而 Panic 则会触发 panic，退出当前协程。
func Panic(status int) {
	panic(errors.HTTP(status))
}
<|endoftext|>


<commit_before>package bufpool

import (
	"bytes"
	"fmt"
	"sync"
)

// LockPool maintains a free-list of buffers.
type LockPool struct {
	lock sync.Mutex
	free []*bytes.Buffer
	pc   poolConfig
}

// NewLockPool creates a new FreeList. The pool size, size of new buffers, and max size of buffers
// to keep when returned to the pool can all be customized.
//
//        package main
//
//        import (
//        	"log"
//
//        	"github.com/karrick/bufpool"
//        )
//
//        func main() {
//        	bp, err := bufpool.NewLockPool()
//        	if err != nil {
//        		log.Fatal(err)
//        	}
//        	for i := 0; i < 4*bufpool.DefaultPoolSize; i++ {
//        		go func() {
//        			for j := 0; j < 1000; j++ {
//        				bb := bp.Get()
//        				for k := 0; k < 3*bufpool.DefaultBufferSize; k++ {
//        					bb.WriteByte(byte(k % 256))
//        				}
//        				bp.Put(bb)
//        			}
//        		}()
//        	}
//        }
func NewLockPool(setters ...func(*poolConfig) error) (FreeList, error) {
	pc := &poolConfig{
		chSize:  DefaultPoolSize,
		defSize: DefaultBufferSize,
		maxSize: DefaultMaxBufferSize,
	}
	for _, setter := range setters {
		if err := setter(pc); err != nil {
			return nil, err
		}
	}
	if pc.maxSize < pc.defSize {
		return nil, fmt.Errorf("max buffer size must be greater or equal to default buffer size: %d, %d", pc.maxSize, pc.defSize)
	}
	bp := &LockPool{
		free: make([]*bytes.Buffer, 0, pc.chSize),
		pc:   *pc,
	}
	return bp, nil
}

// Get returns an initialized buffer from the free-list.
func (bp *LockPool) Get() *bytes.Buffer {
	bp.lock.Lock()
	defer bp.lock.Unlock()

	if len(bp.free) == 0 {
		return bytes.NewBuffer(make([]byte, 0, bp.pc.defSize))
	}
	var bb *bytes.Buffer
	bb, bp.free = bp.free[len(bp.free)-1], bp.free[:len(bp.free)-1]
	return bb
}

// Put will return a used buffer back to the free-list. If the capacity of the used buffer grew
// beyond the max buffer size, it will be discarded and its memory returned to the runtime.
func (bp *LockPool) Put(bb *bytes.Buffer) {
	if cap(bb.Bytes()) > bp.pc.maxSize {
		return // drop buffer on floor if too big
	}

	bp.lock.Lock()
	defer bp.lock.Unlock()

	if len(bp.free) == cap(bp.free) {
		return // drop buffer on floor if already have enough
	}
	bb.Reset()
	bp.free = append(bp.free, bb)
}

// Reset releases memory for all buffers presently in the free-list back to the runtime. This method
// is typically not called for long-running programs that use a free-list of buffers for a long
// time.
func (bp *LockPool) Reset() {
	bp.lock.Lock()
	defer bp.lock.Unlock()

	bp.free = bp.free[:0]
}
<commit_msg>LookPool no longer uses defer<commit_after>package bufpool

import (
	"bytes"
	"fmt"
	"sync"
)

// LockPool maintains a free-list of buffers.
type LockPool struct {
	lock sync.Mutex
	free []*bytes.Buffer
	pc   poolConfig
}

// NewLockPool creates a new FreeList. The pool size, size of new buffers, and max size of buffers
// to keep when returned to the pool can all be customized.
//
//        package main
//
//        import (
//        	"log"
//
//        	"github.com/karrick/bufpool"
//        )
//
//        func main() {
//        	bp, err := bufpool.NewLockPool()
//        	if err != nil {
//        		log.Fatal(err)
//        	}
//        	for i := 0; i < 4*bufpool.DefaultPoolSize; i++ {
//        		go func() {
//        			for j := 0; j < 1000; j++ {
//        				bb := bp.Get()
//        				for k := 0; k < 3*bufpool.DefaultBufferSize; k++ {
//        					bb.WriteByte(byte(k % 256))
//        				}
//        				bp.Put(bb)
//        			}
//        		}()
//        	}
//        }
func NewLockPool(setters ...func(*poolConfig) error) (FreeList, error) {
	pc := &poolConfig{
		chSize:  DefaultPoolSize,
		defSize: DefaultBufferSize,
		maxSize: DefaultMaxBufferSize,
	}
	for _, setter := range setters {
		if err := setter(pc); err != nil {
			return nil, err
		}
	}
	if pc.maxSize < pc.defSize {
		return nil, fmt.Errorf("max buffer size must be greater or equal to default buffer size: %d, %d", pc.maxSize, pc.defSize)
	}
	bp := &LockPool{
		free: make([]*bytes.Buffer, 0, pc.chSize),
		pc:   *pc,
	}
	return bp, nil
}

// Get returns an initialized buffer from the free-list.
func (bp *LockPool) Get() *bytes.Buffer {
	bp.lock.Lock()

	if len(bp.free) == 0 {
		bp.lock.Unlock()
		return bytes.NewBuffer(make([]byte, 0, bp.pc.defSize))
	}
	var bb *bytes.Buffer
	lmo := len(bp.free) - 1
	bb, bp.free = bp.free[lmo], bp.free[:lmo]
	bp.lock.Unlock()
	return bb
}

// Put will return a used buffer back to the free-list. If the capacity of the used buffer grew
// beyond the max buffer size, it will be discarded and its memory returned to the runtime.
func (bp *LockPool) Put(bb *bytes.Buffer) {
	if cap(bb.Bytes()) > bp.pc.maxSize {
		return // drop buffer on floor if too big
	}

	bp.lock.Lock()

	if len(bp.free) == cap(bp.free) {
		bp.lock.Unlock()
		return // drop buffer on floor if already have enough
	}
	bb.Reset()
	bp.free = append(bp.free, bb)
	bp.lock.Unlock()
}

// Reset releases memory for all buffers presently in the free-list back to the runtime. This method
// is typically not called for long-running programs that use a free-list of buffers for a long
// time.
func (bp *LockPool) Reset() {
	bp.lock.Lock()
	bp.free = bp.free[:0]
	bp.lock.Unlock()
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
)

func Error(format string, i ...interface{}) {
	Log("ERROR", format, i...)
}

func Info(format string, i ...interface{}) {
	Log("INFO ", format, i...)
}

func Log(tag, format string, i ...interface{}) {
	fmt.Printf(tag+" "+format+"\n", i...)
}
<commit_msg>add Fatal method for logging<commit_after>package log

import (
	"fmt"
	"os"
)

func Error(format string, i ...interface{}) {
	Log("ERROR", format, i...)
}

func Fatal(format string, i ...interface{}) {
	Error(format, i...)
	os.Exit(1)
}

func Info(format string, i ...interface{}) {
	Log("INFO ", format, i...)
}

func Log(tag, format string, i ...interface{}) {
	fmt.Printf(tag+" "+format+"\n", i...)
}
<|endoftext|>


<commit_before>// Copyright 2009 The GoMPD Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package mpd

import (
	"os"
	"testing"
)

var (
	serverRunning  = false
	useGoMPDServer = true
)

func localAddr() (net, addr string) {
	if useGoMPDServer {
		// Don't clash with standard MPD port 6600
		return "tcp", "127.0.0.1:6603"
	}
	net = "unix"
	addr = os.Getenv("MPD_HOST")
	if len(addr) > 0 && addr[0] == '/' {
		return
	}
	net = "tcp"
	if len(addr) == 0 {
		addr = "127.0.0.1"
	}
	port := os.Getenv("MPD_PORT")
	if len(port) == 0 {
		port = "6600"
	}
	return net, addr + ":" + port
}

func localDial(t *testing.T) *Client {
	net, addr := localAddr()
	if useGoMPDServer && !serverRunning {
		running := make(chan bool)
		go serve(net, addr, running)
		serverRunning = true
		<-running
	}
	cli, err := Dial(net, addr)
	if err != nil {
		t.Fatalf("Dial(%q) = %v, %s want PTR, nil", addr, cli, err)
	}
	return cli
}

func teardown(cli *Client, t *testing.T) {
	if err := cli.Close(); err != nil {
		t.Errorf("Client.Close() = %s need nil", err)
	}
}

func attrsEqual(left, right Attrs) bool {
	if len(left) != len(right) {
		return false
	}
	for key, lval := range left {
		if rval, ok := right[key]; !ok || lval != rval {
			return false
		}
	}
	return true
}

func TestPlaylistInfo(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	// Add songs to the current playlist.
	files, err := cli.GetFiles()
	all := 4
	if err != nil {
		t.Fatalf("Client.GetFiles failed: %s\n", err)
	}
	if len(files) < all {
		t.Fatalf("Add more then %d audio file to your MPD to run this test.", all)
	}
	for i := 0; i < all; i++ {
		if err = cli.Add(files[i]); err != nil {
			t.Fatalf("Client.Add failed: %s\n", err)
		}
	}

	pls, err := cli.PlaylistInfo(-1, -1)
	if err != nil {
		t.Fatalf("Client.PlaylistInfo(-1, -1) = %v, %s need _, nil", pls, err)
	}
	if len(pls) != all {
		t.Fatalf("Client.PlaylistInfo(-1, -1) len = %d need %d", len(pls), all)
	}
	for i, song := range pls {
		if _, ok := song["file"]; !ok {
			t.Errorf(`PlaylistInfo: song %d has no "file" attribute`, i)
		}
		pls1, err := cli.PlaylistInfo(i, -1)
		if err != nil {
			t.Errorf("Client.PlaylistInfo(%d, -1) = %v, %s need _, nil", i, pls1, err)
		}
		if !attrsEqual(pls[i], pls1[0]) {
			t.Errorf("song at position %d is %v; want %v", i, pls[i], pls1[0])
		}
	}

	pls, err = cli.PlaylistInfo(2, 4)
	if err != nil {
		t.Fatalf("Client.PlaylistInfo(2, 4) = %v, %s need _, nil", pls, err)
	}
	if len(pls) != 2 {
		t.Fatalf("Client.PlaylistInfo(2, 4) len = %d need 2", len(pls))
	}
}

func TestListInfo(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	fileCount, dirCount, plsCount := 0, 0, 0

	ls, err := cli.ListInfo("")
	if err != nil {
		t.Fatalf(`Client.ListInfo("") = %v, %s need _, nil`, ls, err)
	}
	for i, item := range ls {
		if _, ok := item["file"]; ok {
			fileCount++
			for _, field := range []string{"last-modified", "artist", "title", "track"} {
				if _, ok := item[field]; !ok {
					t.Errorf(`ListInfo: file item %d has no "%s" field`, i, field)
				}
			}
		} else if _, ok := item["directory"]; ok {
			dirCount++
		} else if _, ok := item["playlist"]; ok {
			plsCount++
		} else {
			t.Errorf("ListInfo: item %d has no file/directory/playlist attribute", i)
		}
	}

	if expected := 100; fileCount != expected {
		t.Errorf(`ListInfo: expected %d files, got %d`, expected, fileCount)
	}
	if expected := 2; dirCount != expected {
		t.Errorf(`ListInfo: expected %d directories, got %d`, expected, dirCount)
	}
	if expected := 1; plsCount != expected {
		t.Errorf(`ListInfo: expected %d playlists, got %d`, expected, plsCount)
	}
}

func TestCurrentSong(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	attrs, err := cli.CurrentSong()
	if err != nil {
		t.Fatalf("Client.CurrentSong() = %v, %s need _, nil", attrs, err)
	}
	if len(attrs) == 0 {
		return // no current song
	}
	_, ok := attrs["file"]
	if !ok {
		t.Fatalf("current song (attrs=%v) has no file attribute", attrs)
	}
}

func TestPing(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	err := cli.Ping()
	if err != nil {
		t.Errorf("Client.Ping failed: %s\n", err)
	}
}

func TestUpdate(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	id, err := cli.Update("")
	if err != nil {
		t.Fatalf("Client.Update failed: %s\n", err)
	}
	if id < 1 {
		t.Errorf("job id is too small: %d\n", id)
	}
}

func TestListOutputs(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	outputs, err := cli.ListOutputs()
	if err != nil {
		t.Fatalf(`Client.ListOutputs() = %v, %s need _, nil`, outputs, err)
	}
	expected := []Attrs{{
		"outputid":      "0",
		"outputname":    "downstairs",
		"outputenabled": "1",
	}, {
		"outputid":      "1",
		"outputname":    "upstairs",
		"outputenabled": "0",
	}}
	if len(outputs) != 2 {
		t.Errorf(`Listed %d outputs, expected %d`, len(outputs), 2)
	}
	for i, o := range outputs {
		if len(o) != 3 {
			t.Errorf(`Output should contain 3 keys, got %d`, len(o))
		}
		for k, v := range expected[i] {
			if outputs[i][k] != v {
				t.Errorf(`Expected property %o for key "%s", got %o`, v, k, outputs[i][k])
			}
		}
	}
}

func TestEnableOutput(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	err := cli.EnableOutput(1)
	if err != nil {
		t.Fatalf("Client.EnableOutput failed: %s\n", err)
	}
}

func TestDisableOutput(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	err := cli.DisableOutput(1)
	if err != nil {
		t.Fatalf("Client.DisableOutput failed: %s\n", err)
	}
}

func TestPlaylistFunctions(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	files, err := cli.GetFiles()
	if err != nil {
		t.Fatalf("Client.GetFiles failed: %s\n", err)
	}
	if len(files) < 2 {
		t.Log("Add more then 1 audio file to your MPD to run this test.")
		return
	}
	for i := 0; i < 2; i++ {
		if err = cli.PlaylistAdd("Test Playlist", files[i]); err != nil {
			t.Fatalf("Client.PlaylistAdd failed: %s\n", err)
		}
	}
	attrs, err := cli.ListPlaylists()
	if err != nil {
		t.Fatalf("Client.ListPlaylists failed: %s\n", err)
	}
	if i := attrsListIndex(attrs, "playlist", "Test Playlist"); i < 0 {
		t.Fatalf("Couldn't find playlist \"Test Playlist\" in %v\n", attrs)
	}
	attrs, err = cli.PlaylistContents("Test Playlist")
	if err != nil {
		t.Fatalf("Client.PlaylistContents failed: %s\n", err)
	}
	if i := attrsListIndex(attrs, "file", files[0]); i < 0 {
		t.Fatalf("Couldn't find song %q in %v", files[0], attrs)
	}
	if err = cli.PlaylistDelete("Test Playlist", 0); err != nil {
		t.Fatalf("Client.PlaylistDelete failed: %s\n", err)
	}
	playlist, err := cli.PlaylistContents("Test Playlist")
	if err != nil {
		t.Fatalf("Client.PlaylistContents failed: %s\n", err)
	}
	if !attrsListEqual(playlist, attrs[1:]) {
		t.Fatalf("PlaylistContents returned %v; want %v", playlist, attrs[1:])
	}
	cli.PlaylistRemove("Test Playlist 2")
	if err = cli.PlaylistRename("Test Playlist", "Test Playlist 2"); err != nil {
		t.Fatalf("Client.PlaylistRename failed: %s\n", err)
	}
	if err = cli.Clear(); err != nil {
		t.Fatalf("Client.Clear failed: %s\n", err)
	}
	if err = cli.PlaylistLoad("Test Playlist 2", -1, -1); err != nil {
		t.Fatalf("Client.Load failed: %s\n", err)
	}
	attrs, err = cli.PlaylistInfo(-1, -1)
	if err != nil {
		t.Fatalf("Client.PlaylistInfo failed: %s\n", err)
	}
	if !attrsListEqualKey(playlist, attrs, "file") {
		t.Fatalf("Unexpected playlist: %v != %v\n", attrs, playlist)
	}
	if err = cli.PlaylistClear("Test Playlist 2"); err != nil {
		t.Fatalf("Client.PlaylistClear failed: %s\n", err)
	}
	attrs, err = cli.PlaylistContents("Test Playlist 2")
	if err != nil {
		t.Fatalf("Client.PlaylistContents failed: %s\n", err)
	}
	if len(attrs) != 0 {
		t.Fatalf("Unexpected number of songs: %d != 0\n", len(attrs))
	}
	if err = cli.PlaylistRemove("Test Playlist 2"); err != nil {
		t.Fatalf("Client.PlaylistRemove failed: %s\n", err)
	}
	attrs, err = cli.ListPlaylists()
	if err != nil {
		t.Fatalf("Client.ListPlaylists failed: %s\n", err)
	}
	if i := attrsListIndex(attrs, "playlist", "Test Playlist 2"); i > -1 {
		t.Fatalf("Found playlist \"Test Playlist 2\" in %v\n", attrs)
	}
	if err = cli.PlaylistSave("Test Playlist"); err != nil {
		t.Fatalf("Client.PlaylistSave failed: %s\n", err)
	}
	attrs, err = cli.PlaylistContents("Test Playlist")
	if err != nil {
		t.Fatalf("Client.PlaylistContents failed: %s\n", err)
	}
	if !attrsListEqual(playlist, attrs) {
		t.Fatalf("Unexpected playlist: %v != %v\n", attrs, playlist)
	}
}

func attrsListIndex(attrs []Attrs, key, value string) int {
	for i, attr := range attrs {
		if attr[key] == value {
			return i
		}
	}
	return -1
}

func attrsListEqual(a, b []Attrs) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if !attrsEqual(a[i], b[i]) {
			return false
		}
	}
	return true
}

func attrsListEqualKey(a, b []Attrs, key string) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i][key] != b[i][key] {
			return false
		}
	}
	return true
}

var quoteTests = []struct {
	s, q string
}{
	{`test.ogg`, `"test.ogg"`},
	{`test "song".ogg`, `"test \"song\".ogg"`},
	{`04 - ILL - DECAYED LOVE　feat.℃iel.ogg`, `"04 - ILL - DECAYED LOVE　feat.℃iel.ogg"`},
}

func TestQuote(t *testing.T) {
	for _, test := range quoteTests {
		if q := quote(test.s); q != test.q {
			t.Errorf("quote(%s) returned %s; expected %s\n", test.s, q, test.q)
		}
	}
}
<commit_msg>use initialization statement in if's when appropriate<commit_after>// Copyright 2009 The GoMPD Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package mpd

import (
	"os"
	"testing"
)

var (
	serverRunning  = false
	useGoMPDServer = true
)

func localAddr() (net, addr string) {
	if useGoMPDServer {
		// Don't clash with standard MPD port 6600
		return "tcp", "127.0.0.1:6603"
	}
	net = "unix"
	addr = os.Getenv("MPD_HOST")
	if len(addr) > 0 && addr[0] == '/' {
		return
	}
	net = "tcp"
	if len(addr) == 0 {
		addr = "127.0.0.1"
	}
	port := os.Getenv("MPD_PORT")
	if len(port) == 0 {
		port = "6600"
	}
	return net, addr + ":" + port
}

func localDial(t *testing.T) *Client {
	net, addr := localAddr()
	if useGoMPDServer && !serverRunning {
		running := make(chan bool)
		go serve(net, addr, running)
		serverRunning = true
		<-running
	}
	cli, err := Dial(net, addr)
	if err != nil {
		t.Fatalf("Dial(%q) = %v, %s want PTR, nil", addr, cli, err)
	}
	return cli
}

func teardown(cli *Client, t *testing.T) {
	if err := cli.Close(); err != nil {
		t.Errorf("Client.Close() = %s need nil", err)
	}
}

func attrsEqual(left, right Attrs) bool {
	if len(left) != len(right) {
		return false
	}
	for key, lval := range left {
		if rval, ok := right[key]; !ok || lval != rval {
			return false
		}
	}
	return true
}

func TestPlaylistInfo(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	// Add songs to the current playlist.
	all := 4
	files, err := cli.GetFiles()
	if err != nil {
		t.Fatalf("Client.GetFiles failed: %s\n", err)
	}
	if len(files) < all {
		t.Fatalf("Add more then %d audio file to your MPD to run this test.", all)
	}
	for i := 0; i < all; i++ {
		if err = cli.Add(files[i]); err != nil {
			t.Fatalf("Client.Add failed: %s\n", err)
		}
	}

	pls, err := cli.PlaylistInfo(-1, -1)
	if err != nil {
		t.Fatalf("Client.PlaylistInfo(-1, -1) = %v, %s need _, nil", pls, err)
	}
	if len(pls) != all {
		t.Fatalf("Client.PlaylistInfo(-1, -1) len = %d need %d", len(pls), all)
	}
	for i, song := range pls {
		if _, ok := song["file"]; !ok {
			t.Errorf(`PlaylistInfo: song %d has no "file" attribute`, i)
		}
		pls1, err := cli.PlaylistInfo(i, -1)
		if err != nil {
			t.Errorf("Client.PlaylistInfo(%d, -1) = %v, %s need _, nil", i, pls1, err)
		}
		if !attrsEqual(pls[i], pls1[0]) {
			t.Errorf("song at position %d is %v; want %v", i, pls[i], pls1[0])
		}
	}

	pls, err = cli.PlaylistInfo(2, 4)
	if err != nil {
		t.Fatalf("Client.PlaylistInfo(2, 4) = %v, %s need _, nil", pls, err)
	}
	if len(pls) != 2 {
		t.Fatalf("Client.PlaylistInfo(2, 4) len = %d need 2", len(pls))
	}
}

func TestListInfo(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	fileCount, dirCount, plsCount := 0, 0, 0

	ls, err := cli.ListInfo("")
	if err != nil {
		t.Fatalf(`Client.ListInfo("") = %v, %s need _, nil`, ls, err)
	}
	for i, item := range ls {
		if _, ok := item["file"]; ok {
			fileCount++
			for _, field := range []string{"last-modified", "artist", "title", "track"} {
				if _, ok := item[field]; !ok {
					t.Errorf(`ListInfo: file item %d has no "%s" field`, i, field)
				}
			}
		} else if _, ok := item["directory"]; ok {
			dirCount++
		} else if _, ok := item["playlist"]; ok {
			plsCount++
		} else {
			t.Errorf("ListInfo: item %d has no file/directory/playlist attribute", i)
		}
	}

	if expected := 100; fileCount != expected {
		t.Errorf(`ListInfo: expected %d files, got %d`, expected, fileCount)
	}
	if expected := 2; dirCount != expected {
		t.Errorf(`ListInfo: expected %d directories, got %d`, expected, dirCount)
	}
	if expected := 1; plsCount != expected {
		t.Errorf(`ListInfo: expected %d playlists, got %d`, expected, plsCount)
	}
}

func TestCurrentSong(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	attrs, err := cli.CurrentSong()
	if err != nil {
		t.Fatalf("Client.CurrentSong() = %v, %s need _, nil", attrs, err)
	}
	if len(attrs) == 0 {
		return // no current song
	}
	if _, ok := attrs["file"]; !ok {
		t.Fatalf("current song (attrs=%v) has no file attribute", attrs)
	}
}

func TestPing(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	if err := cli.Ping(); err != nil {
		t.Errorf("Client.Ping failed: %s\n", err)
	}
}

func TestUpdate(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	id, err := cli.Update("")
	if err != nil {
		t.Fatalf("Client.Update failed: %s\n", err)
	}
	if id < 1 {
		t.Errorf("job id is too small: %d\n", id)
	}
}

func TestListOutputs(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	outputs, err := cli.ListOutputs()
	if err != nil {
		t.Fatalf(`Client.ListOutputs() = %v, %s need _, nil`, outputs, err)
	}
	expected := []Attrs{{
		"outputid":      "0",
		"outputname":    "downstairs",
		"outputenabled": "1",
	}, {
		"outputid":      "1",
		"outputname":    "upstairs",
		"outputenabled": "0",
	}}
	if len(outputs) != 2 {
		t.Errorf(`Listed %d outputs, expected %d`, len(outputs), 2)
	}
	for i, o := range outputs {
		if len(o) != 3 {
			t.Errorf(`Output should contain 3 keys, got %d`, len(o))
		}
		for k, v := range expected[i] {
			if outputs[i][k] != v {
				t.Errorf(`Expected property %o for key "%s", got %o`, v, k, outputs[i][k])
			}
		}
	}
}

func TestEnableOutput(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	if err := cli.EnableOutput(1); err != nil {
		t.Fatalf("Client.EnableOutput failed: %s\n", err)
	}
}

func TestDisableOutput(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	if err := cli.DisableOutput(1); err != nil {
		t.Fatalf("Client.DisableOutput failed: %s\n", err)
	}
}

func TestPlaylistFunctions(t *testing.T) {
	cli := localDial(t)
	defer teardown(cli, t)

	files, err := cli.GetFiles()
	if err != nil {
		t.Fatalf("Client.GetFiles failed: %s\n", err)
	}
	if len(files) < 2 {
		t.Log("Add more then 1 audio file to your MPD to run this test.")
		return
	}
	for i := 0; i < 2; i++ {
		if err = cli.PlaylistAdd("Test Playlist", files[i]); err != nil {
			t.Fatalf("Client.PlaylistAdd failed: %s\n", err)
		}
	}
	attrs, err := cli.ListPlaylists()
	if err != nil {
		t.Fatalf("Client.ListPlaylists failed: %s\n", err)
	}
	if i := attrsListIndex(attrs, "playlist", "Test Playlist"); i < 0 {
		t.Fatalf("Couldn't find playlist \"Test Playlist\" in %v\n", attrs)
	}
	attrs, err = cli.PlaylistContents("Test Playlist")
	if err != nil {
		t.Fatalf("Client.PlaylistContents failed: %s\n", err)
	}
	if i := attrsListIndex(attrs, "file", files[0]); i < 0 {
		t.Fatalf("Couldn't find song %q in %v", files[0], attrs)
	}
	if err = cli.PlaylistDelete("Test Playlist", 0); err != nil {
		t.Fatalf("Client.PlaylistDelete failed: %s\n", err)
	}
	playlist, err := cli.PlaylistContents("Test Playlist")
	if err != nil {
		t.Fatalf("Client.PlaylistContents failed: %s\n", err)
	}
	if !attrsListEqual(playlist, attrs[1:]) {
		t.Fatalf("PlaylistContents returned %v; want %v", playlist, attrs[1:])
	}
	cli.PlaylistRemove("Test Playlist 2")
	if err = cli.PlaylistRename("Test Playlist", "Test Playlist 2"); err != nil {
		t.Fatalf("Client.PlaylistRename failed: %s\n", err)
	}
	if err = cli.Clear(); err != nil {
		t.Fatalf("Client.Clear failed: %s\n", err)
	}
	if err = cli.PlaylistLoad("Test Playlist 2", -1, -1); err != nil {
		t.Fatalf("Client.Load failed: %s\n", err)
	}
	attrs, err = cli.PlaylistInfo(-1, -1)
	if err != nil {
		t.Fatalf("Client.PlaylistInfo failed: %s\n", err)
	}
	if !attrsListEqualKey(playlist, attrs, "file") {
		t.Fatalf("Unexpected playlist: %v != %v\n", attrs, playlist)
	}
	if err = cli.PlaylistClear("Test Playlist 2"); err != nil {
		t.Fatalf("Client.PlaylistClear failed: %s\n", err)
	}
	attrs, err = cli.PlaylistContents("Test Playlist 2")
	if err != nil {
		t.Fatalf("Client.PlaylistContents failed: %s\n", err)
	}
	if len(attrs) != 0 {
		t.Fatalf("Unexpected number of songs: %d != 0\n", len(attrs))
	}
	if err = cli.PlaylistRemove("Test Playlist 2"); err != nil {
		t.Fatalf("Client.PlaylistRemove failed: %s\n", err)
	}
	attrs, err = cli.ListPlaylists()
	if err != nil {
		t.Fatalf("Client.ListPlaylists failed: %s\n", err)
	}
	if i := attrsListIndex(attrs, "playlist", "Test Playlist 2"); i > -1 {
		t.Fatalf("Found playlist \"Test Playlist 2\" in %v\n", attrs)
	}
	if err = cli.PlaylistSave("Test Playlist"); err != nil {
		t.Fatalf("Client.PlaylistSave failed: %s\n", err)
	}
	attrs, err = cli.PlaylistContents("Test Playlist")
	if err != nil {
		t.Fatalf("Client.PlaylistContents failed: %s\n", err)
	}
	if !attrsListEqual(playlist, attrs) {
		t.Fatalf("Unexpected playlist: %v != %v\n", attrs, playlist)
	}
}

func attrsListIndex(attrs []Attrs, key, value string) int {
	for i, attr := range attrs {
		if attr[key] == value {
			return i
		}
	}
	return -1
}

func attrsListEqual(a, b []Attrs) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if !attrsEqual(a[i], b[i]) {
			return false
		}
	}
	return true
}

func attrsListEqualKey(a, b []Attrs, key string) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i][key] != b[i][key] {
			return false
		}
	}
	return true
}

var quoteTests = []struct {
	s, q string
}{
	{`test.ogg`, `"test.ogg"`},
	{`test "song".ogg`, `"test \"song\".ogg"`},
	{`04 - ILL - DECAYED LOVE　feat.℃iel.ogg`, `"04 - ILL - DECAYED LOVE　feat.℃iel.ogg"`},
}

func TestQuote(t *testing.T) {
	for _, test := range quoteTests {
		if q := quote(test.s); q != test.q {
			t.Errorf("quote(%s) returned %s; expected %s\n", test.s, q, test.q)
		}
	}
}
<|endoftext|>


<commit_before>// +build mage

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/magefile/mage/mg" // mg contains helpful utility functions, like Deps
	"github.com/magefile/mage/sh" // mg contains helpful utility functions, like Deps
	spartamage "github.com/mweagle/Sparta/magefile"
)

const localWorkDir = "./.sparta"

var ignoreSubdirectoryPaths = []string{
	".vendor",
	".sparta",
	".vscode",
	"/resources/describe",
}

// Default target to run when none is specified
// If not set, running mage will list available targets
// var Default = Build

func markdownSourceApply(commandParts ...string) error {
	return spartamage.ApplyToSource("md", ignoreSubdirectoryPaths, commandParts...)
}
func goSourceApply(commandParts ...string) error {
	return spartamage.ApplyToSource("go", ignoreSubdirectoryPaths, commandParts...)
}

// EnsureCleanTree ensures that the git tree is clean
func EnsureCleanTree() error {
	cleanTreeScript := [][]string{
		// No dirty trees
		{"git", "diff", "--exit-code"},
	}
	return spartamage.Script(cleanTreeScript)
}

// GenerateBuildInfo creates the automatic buildinfo.go file so that we can
// stamp the SHA into the binaries we build...
func GenerateBuildInfo() error {
	mg.SerialDeps(EnsureCleanTree)

	// The first thing we need is the `git` SHA
	cmd := exec.Command("git", "rev-parse", "HEAD")
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		return err
	}
	stdOutResult := strings.TrimSpace(string(stdout.Bytes()))

	// Super = update the buildinfo data
	buildInfoTemplate := `package sparta

// THIS FILE IS AUTOMATICALLY GENERATED
// DO NOT EDIT
// CREATED: %s

// SpartaGitHash is the commit hash of this Sparta library
const SpartaGitHash = "%s"
`
	updatedInfo := fmt.Sprintf(buildInfoTemplate, time.Now().UTC(), stdOutResult)
	// Write it to the output location...
	writeErr := ioutil.WriteFile("./buildinfo.go", []byte(updatedInfo), os.ModePerm)

	if writeErr != nil {
		return writeErr
	}
	commitGenerateCommands := [][]string{
		{"git", "diff"},
		{"git", "commit", "-a", "-m", `"Autogenerated build info"`},
	}
	return spartamage.Script(commitGenerateCommands)

}

// GenerateConstants runs the set of commands that update the embedded CONSTANTS
// for both local and AWS Lambda execution
func GenerateConstants() error {
	generateCommands := [][]string{
		// Create the embedded version
		{"go", "run", "$GOPATH/src/github.com/mjibson/esc/main.go", "-o", "./CONSTANTS.go", "-private", "-pkg", "sparta", "./resources"},
		//Create a secondary CONSTANTS_AWSBINARY.go file with empty content.
		{"go", "run", "$GOPATH/src/github.com/mjibson/esc/main.go", "-o", "./CONSTANTS_AWSBINARY.go", "-private", "-pkg", "sparta", "./resources/awsbinary/README.md"},
		//The next step will insert the
		// build tags at the head of each file so that they are mutually exclusive
		{"go", "run", "./cmd/insertTags/main.go", "./CONSTANTS", "!lambdabinary"},
		{"go", "run", "./cmd/insertTags/main.go", "./CONSTANTS_AWSBINARY", "lambdabinary"},
		{"git", "commit", "-a", "-m", "Autogenerated constants"},
	}
	return spartamage.Script(generateCommands)
}

// InstallBuildRequirements installs or updates the dependent
// packages that aren't referenced by the source, but are needed
// to build the Sparta source
func InstallBuildRequirements() error {
	spartamage.Log("`go get` update flags (env.GO_GET_FLAG): %s", os.Getenv("GO_GET_FLAG"))

	requirements := []string{
		"github.com/golang/dep/...",
		"honnef.co/go/tools/cmd/megacheck",
		"honnef.co/go/tools/cmd/gosimple",
		"honnef.co/go/tools/cmd/unused",
		"honnef.co/go/tools/cmd/staticcheck",
		"golang.org/x/tools/cmd/goimports",
		"github.com/fzipp/gocyclo",
		"golang.org/x/lint/golint",
		"github.com/mjibson/esc",
		"github.com/securego/gosec/cmd/gosec/...",
		"github.com/client9/misspell/cmd/misspell",
	}
	for _, eachDep := range requirements {
		cmdErr := sh.Run("go",
			"get",
			os.Getenv("GO_GET_FLAG"),
			eachDep)
		if cmdErr != nil {
			return cmdErr
		}
	}
	return nil
}

// EnsureSpelling ensures that there are no misspellings in the source
func EnsureSpelling() error {
	goSpelling := func() error {
		return goSourceApply("misspell", "-error")
	}
	mdSpelling := func() error {
		return markdownSourceApply("misspell", "-error")
	}
	mg.SerialDeps(
		goSpelling,
		mdSpelling)
	return nil
}

// EnsureVet ensures that the source has been `go vet`ted
func EnsureVet() error {
	return goSourceApply("go", "tool", "vet")
}

// EnsureLint ensures that the source is `golint`ed
func EnsureLint() error {
	return goSourceApply("golint")
}

// EnsureFormatted ensures that the source code is formatted with goimports
func EnsureFormatted() error {
	return goSourceApply("goimports", "-e", "-w")
}

// EnsureStaticChecks ensures that the source code passes static code checks
func EnsureStaticChecks() error {
	// Megacheck
	megacheckErr := sh.Run("megacheck",
		"-ignore",
		"github.com/mweagle/Sparta/CONSTANTS.go:*")
	if megacheckErr != nil {
		return megacheckErr
	}
	// Gosec
	quietFlag := "-quiet"
	if mg.Verbose() {
		quietFlag = ""
	}
	return sh.Run("gosec",
		quietFlag,
		"-exclude=G204,G505,G401",
		"./...")
}

// EnsureAllPreconditions ensures that the source passes *ALL* static `ensure*`
// precondition steps
func EnsureAllPreconditions() error {
	mg.SerialDeps(
		InstallBuildRequirements,
		EnsureVet,
		EnsureLint,
		EnsureFormatted,
		EnsureStaticChecks,
		EnsureSpelling,
	)
	return nil
}

// EnsureTravisBuildEnvironment is the command that sets up the Travis
// environment to run the build.
func EnsureTravisBuildEnvironment() error {
	mg.SerialDeps(InstallBuildRequirements)

	// Super run some commands
	travisComands := [][]string{
		[]string{"dep", "version"},
		[]string{"dep", "ensure", "-v"},
		[]string{"rsync", "-a", "--quiet", "--remove-source-files", "./vendor/", "$GOPATH/src"},
	}
	return spartamage.Script(travisComands)
}

// Build the application
func Build() error {
	mg.Deps(EnsureAllPreconditions)
	return sh.Run("go", "build", ".")
}

// Clean the working directory
func Clean() error {
	cleanCommands := [][]string{
		[]string{"go", "clean", "."},
		[]string{"rm", "-rf", "./graph.html"},
		[]string{"rsync", "-a", "--quiet", "--remove-source-files", "./vendor/", "$GOPATH/src"},
	}
	return spartamage.Script(cleanCommands)
}

// Describe runs the `TestDescribe` test to generate a describe HTML output
// file at graph.html
func Describe() error {
	describeCommands := [][]string{
		[]string{"rm", "-rf", "./graph.html"},
		[]string{"go", "test", "-v", "-run", "TestDescribe"},
	}
	return spartamage.Script(describeCommands)
}

// Publish the latest source
func Publish() error {
	mg.SerialDeps(GenerateBuildInfo)

	describeCommands := [][]string{
		[]string{"git", "push", "origin"},
	}
	return spartamage.Script(describeCommands)
}

// Test runs the Sparta tests
func Test() error {
	mg.SerialDeps(
		EnsureAllPreconditions,
	)
	verboseFlag := ""
	if mg.Verbose() {
		verboseFlag = "-v"
	}
	testCommand := [][]string{
		[]string{"go", "test", verboseFlag, "-cover", "-race", "./..."},
	}
	return spartamage.Script(testCommand)
}

// TestCover runs the test and opens up the resulting report
func TestCover() error {
	mg.SerialDeps(
		EnsureAllPreconditions,
	)
	coverageReport := fmt.Sprintf("%s/cover.out", localWorkDir)
	testCoverCommands := [][]string{
		[]string{"go", "test", fmt.Sprintf("-coverprofile=%s", coverageReport), "."},
		[]string{"go", "tool", "cover", fmt.Sprintf("-html=%s", coverageReport)},
		[]string{"rm", coverageReport},
		[]string{"open", fmt.Sprintf("%s/cover.html", localWorkDir)},
	}
	return spartamage.Script(testCoverCommands)
}

// TravisBuild is the task to build in the context of a Travis CI pipeline
func TravisBuild() error {
	mg.SerialDeps(EnsureTravisBuildEnvironment,
		Build,
		Test)
	return nil
}
<commit_msg>Patch up gosec CLI args<commit_after>// +build mage

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/magefile/mage/mg" // mg contains helpful utility functions, like Deps
	"github.com/magefile/mage/sh" // mg contains helpful utility functions, like Deps
	spartamage "github.com/mweagle/Sparta/magefile"
)

const localWorkDir = "./.sparta"

var ignoreSubdirectoryPaths = []string{
	".vendor",
	".sparta",
	".vscode",
	"/resources/describe",
}

// Default target to run when none is specified
// If not set, running mage will list available targets
// var Default = Build

func markdownSourceApply(commandParts ...string) error {
	return spartamage.ApplyToSource("md", ignoreSubdirectoryPaths, commandParts...)
}
func goSourceApply(commandParts ...string) error {
	return spartamage.ApplyToSource("go", ignoreSubdirectoryPaths, commandParts...)
}

// EnsureCleanTree ensures that the git tree is clean
func EnsureCleanTree() error {
	cleanTreeScript := [][]string{
		// No dirty trees
		{"git", "diff", "--exit-code"},
	}
	return spartamage.Script(cleanTreeScript)
}

// GenerateBuildInfo creates the automatic buildinfo.go file so that we can
// stamp the SHA into the binaries we build...
func GenerateBuildInfo() error {
	mg.SerialDeps(EnsureCleanTree)

	// The first thing we need is the `git` SHA
	cmd := exec.Command("git", "rev-parse", "HEAD")
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		return err
	}
	stdOutResult := strings.TrimSpace(string(stdout.Bytes()))

	// Super = update the buildinfo data
	buildInfoTemplate := `package sparta

// THIS FILE IS AUTOMATICALLY GENERATED
// DO NOT EDIT
// CREATED: %s

// SpartaGitHash is the commit hash of this Sparta library
const SpartaGitHash = "%s"
`
	updatedInfo := fmt.Sprintf(buildInfoTemplate, time.Now().UTC(), stdOutResult)
	// Write it to the output location...
	writeErr := ioutil.WriteFile("./buildinfo.go", []byte(updatedInfo), os.ModePerm)

	if writeErr != nil {
		return writeErr
	}
	commitGenerateCommands := [][]string{
		{"git", "diff"},
		{"git", "commit", "-a", "-m", `"Autogenerated build info"`},
	}
	return spartamage.Script(commitGenerateCommands)

}

// GenerateConstants runs the set of commands that update the embedded CONSTANTS
// for both local and AWS Lambda execution
func GenerateConstants() error {
	generateCommands := [][]string{
		// Create the embedded version
		{"go", "run", "$GOPATH/src/github.com/mjibson/esc/main.go", "-o", "./CONSTANTS.go", "-private", "-pkg", "sparta", "./resources"},
		//Create a secondary CONSTANTS_AWSBINARY.go file with empty content.
		{"go", "run", "$GOPATH/src/github.com/mjibson/esc/main.go", "-o", "./CONSTANTS_AWSBINARY.go", "-private", "-pkg", "sparta", "./resources/awsbinary/README.md"},
		//The next step will insert the
		// build tags at the head of each file so that they are mutually exclusive
		{"go", "run", "./cmd/insertTags/main.go", "./CONSTANTS", "!lambdabinary"},
		{"go", "run", "./cmd/insertTags/main.go", "./CONSTANTS_AWSBINARY", "lambdabinary"},
		{"git", "commit", "-a", "-m", "Autogenerated constants"},
	}
	return spartamage.Script(generateCommands)
}

// InstallBuildRequirements installs or updates the dependent
// packages that aren't referenced by the source, but are needed
// to build the Sparta source
func InstallBuildRequirements() error {
	spartamage.Log("`go get` update flags (env.GO_GET_FLAG): %s", os.Getenv("GO_GET_FLAG"))

	requirements := []string{
		"github.com/golang/dep/...",
		"honnef.co/go/tools/cmd/megacheck",
		"honnef.co/go/tools/cmd/gosimple",
		"honnef.co/go/tools/cmd/unused",
		"honnef.co/go/tools/cmd/staticcheck",
		"golang.org/x/tools/cmd/goimports",
		"github.com/fzipp/gocyclo",
		"golang.org/x/lint/golint",
		"github.com/mjibson/esc",
		"github.com/securego/gosec/cmd/gosec/...",
		"github.com/client9/misspell/cmd/misspell",
	}
	for _, eachDep := range requirements {
		cmdErr := sh.Run("go",
			"get",
			os.Getenv("GO_GET_FLAG"),
			eachDep)
		if cmdErr != nil {
			return cmdErr
		}
	}
	return nil
}

// EnsureSpelling ensures that there are no misspellings in the source
func EnsureSpelling() error {
	goSpelling := func() error {
		return goSourceApply("misspell", "-error")
	}
	mdSpelling := func() error {
		return markdownSourceApply("misspell", "-error")
	}
	mg.SerialDeps(
		goSpelling,
		mdSpelling)
	return nil
}

// EnsureVet ensures that the source has been `go vet`ted
func EnsureVet() error {
	return goSourceApply("go", "tool", "vet")
}

// EnsureLint ensures that the source is `golint`ed
func EnsureLint() error {
	return goSourceApply("golint")
}

// EnsureFormatted ensures that the source code is formatted with goimports
func EnsureFormatted() error {
	return goSourceApply("goimports", "-e", "-w")
}

// EnsureStaticChecks ensures that the source code passes static code checks
func EnsureStaticChecks() error {
	// Megacheck
	megacheckErr := sh.Run("megacheck",
		"-ignore",
		"github.com/mweagle/Sparta/CONSTANTS.go:*")
	if megacheckErr != nil {
		return megacheckErr
	}
	// Gosec
	if mg.Verbose() {
		return sh.Run("gosec",
			"-exclude=G204,G505,G401",
			"./...")
	}
	return sh.Run("gosec",
		"-exclude=G204,G505,G401",
		"-quiet",
		"./...")
}

// EnsureAllPreconditions ensures that the source passes *ALL* static `ensure*`
// precondition steps
func EnsureAllPreconditions() error {
	mg.SerialDeps(
		InstallBuildRequirements,
		EnsureVet,
		EnsureLint,
		EnsureFormatted,
		EnsureStaticChecks,
		EnsureSpelling,
	)
	return nil
}

// EnsureTravisBuildEnvironment is the command that sets up the Travis
// environment to run the build.
func EnsureTravisBuildEnvironment() error {
	mg.SerialDeps(InstallBuildRequirements)

	// Super run some commands
	travisComands := [][]string{
		[]string{"dep", "version"},
		[]string{"dep", "ensure", "-v"},
		[]string{"rsync", "-a", "--quiet", "--remove-source-files", "./vendor/", "$GOPATH/src"},
	}
	return spartamage.Script(travisComands)
}

// Build the application
func Build() error {
	mg.Deps(EnsureAllPreconditions)
	return sh.Run("go", "build", ".")
}

// Clean the working directory
func Clean() error {
	cleanCommands := [][]string{
		[]string{"go", "clean", "."},
		[]string{"rm", "-rf", "./graph.html"},
		[]string{"rsync", "-a", "--quiet", "--remove-source-files", "./vendor/", "$GOPATH/src"},
	}
	return spartamage.Script(cleanCommands)
}

// Describe runs the `TestDescribe` test to generate a describe HTML output
// file at graph.html
func Describe() error {
	describeCommands := [][]string{
		[]string{"rm", "-rf", "./graph.html"},
		[]string{"go", "test", "-v", "-run", "TestDescribe"},
	}
	return spartamage.Script(describeCommands)
}

// Publish the latest source
func Publish() error {
	mg.SerialDeps(GenerateBuildInfo)

	describeCommands := [][]string{
		[]string{"git", "push", "origin"},
	}
	return spartamage.Script(describeCommands)
}

// Test runs the Sparta tests
func Test() error {
	mg.SerialDeps(
		EnsureAllPreconditions,
	)
	verboseFlag := ""
	if mg.Verbose() {
		verboseFlag = "-v"
	}
	testCommand := [][]string{
		[]string{"go", "test", verboseFlag, "-cover", "-race", "./..."},
	}
	return spartamage.Script(testCommand)
}

// TestCover runs the test and opens up the resulting report
func TestCover() error {
	mg.SerialDeps(
		EnsureAllPreconditions,
	)
	coverageReport := fmt.Sprintf("%s/cover.out", localWorkDir)
	testCoverCommands := [][]string{
		[]string{"go", "test", fmt.Sprintf("-coverprofile=%s", coverageReport), "."},
		[]string{"go", "tool", "cover", fmt.Sprintf("-html=%s", coverageReport)},
		[]string{"rm", coverageReport},
		[]string{"open", fmt.Sprintf("%s/cover.html", localWorkDir)},
	}
	return spartamage.Script(testCoverCommands)
}

// TravisBuild is the task to build in the context of a Travis CI pipeline
func TravisBuild() error {
	mg.SerialDeps(EnsureTravisBuildEnvironment,
		Build,
		Test)
	return nil
}
<|endoftext|>


<commit_before>package lfs

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/github/git-lfs/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm"
)

func (c *Configuration) ntlmClientSession(creds Creds) (ntlm.ClientSession, error) {
	if c.ntlmSession != nil {
		return c.ntlmSession, nil
	}
	splits := strings.Split(creds["username"], "\\")

	if len(splits) != 2 {
		errorMessage := fmt.Sprintf("Your user name must be of the form DOMAIN\\user. It is currently %s", creds["username"], "string")
		return nil, errors.New(errorMessage)
	}

	session, err := ntlm.CreateClientSession(ntlm.Version2, ntlm.ConnectionOrientedMode)

	if err != nil {
		return nil, err
	}

	session.SetUserInfo(splits[1], creds["password"], strings.ToUpper(splits[0]))
	c.ntlmSession = session
	return session, nil
}

func DoNTLMRequest(request *http.Request, retry bool) (*http.Response, error) {
	handReq, err := cloneRequest(request)
	if err != nil {
		return nil, err
	}

	res, err := Config.HttpClient().Do(handReq)
	if err != nil && res == nil {
		return nil, err
	}

	//If the status is 401 then we need to re-authenticate, otherwise it was successful
	if res.StatusCode == 401 {

		creds, err := getCredsForAPI(request)
		if err != nil {
			return nil, err
		}

		negotiateReq, err := cloneRequest(request)
		if err != nil {
			return nil, err
		}

		challengeMessage, err := negotiate(negotiateReq, ntlmNegotiateMessage)
		if err != nil {
			return nil, err
		}

		challengeReq, err := cloneRequest(request)
		if err != nil {
			return nil, err
		}

		res, err := challenge(challengeReq, challengeMessage, creds)
		if err != nil {
			return nil, err
		}

		//If the status is 401 then we need to re-authenticate
		if res.StatusCode == 401 && retry == true {
			return DoNTLMRequest(challengeReq, false)
		}

		saveCredentials(creds, res)

		return res, nil
	}
	return res, nil
}

func negotiate(request *http.Request, message string) ([]byte, error) {
	request.Header.Add("Authorization", message)
	res, err := Config.HttpClient().Do(request)

	if res == nil && err != nil {
		return nil, err
	}

	io.Copy(ioutil.Discard, res.Body)
	res.Body.Close()

	ret, err := parseChallengeResponse(res)
	if err != nil {
		return nil, err
	}

	return ret, nil
}

func challenge(request *http.Request, challengeBytes []byte, creds Creds) (*http.Response, error) {
	challenge, err := ntlm.ParseChallengeMessage(challengeBytes)
	if err != nil {
		return nil, err
	}

	session, err := Config.ntlmClientSession(creds)
	if err != nil {
		return nil, err
	}

	session.ProcessChallengeMessage(challenge)
	authenticate, err := session.GenerateAuthenticateMessage()
	if err != nil {
		return nil, err
	}

	authMsg := base64.StdEncoding.EncodeToString(authenticate.Bytes())
	request.Header.Add("Authorization", "NTLM "+authMsg)
	return Config.HttpClient().Do(request)
}

func parseChallengeResponse(response *http.Response) ([]byte, error) {
	header := response.Header.Get("Www-Authenticate")
	if len(header) < 6 {
		return nil, fmt.Errorf("Invalid NTLM challenge response: %q", header)
	}

	//parse out the "NTLM " at the beginning of the response
	challenge := header[5:]
	val, err := base64.StdEncoding.DecodeString(challenge)

	if err != nil {
		return nil, err
	}
	return []byte(val), nil
}

func cloneRequest(request *http.Request) (*http.Request, error) {
	var clonedReq *http.Request
	var err error

	if request.Body != nil {
		//If we have a body (POST/PUT etc.)
		//We need to do some magic to copy the request without closing the body stream

		buf, err := ioutil.ReadAll(request.Body)

		if err != nil {
			return nil, err
		}

		cloneReqBody := ioutil.NopCloser(bytes.NewBuffer(buf))
		request.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
		clonedReq, err = http.NewRequest(request.Method, request.URL.String(), cloneReqBody)
		if err != nil {
			return nil, err
		}

	} else {
		clonedReq, err = http.NewRequest(request.Method, request.URL.String(), nil)
		if err != nil {
			return nil, err
		}
	}

	for k, v := range request.Header {
		clonedReq.Header.Add(k, v[0])
	}

	clonedReq.ContentLength = request.ContentLength

	return clonedReq, nil
}

const ntlmNegotiateMessage = "NTLM TlRMTVNTUAABAAAAB7IIogwADAAzAAAACwALACgAAAAKAAAoAAAAD1dJTExISS1NQUlOTk9SVEhBTUVSSUNB"
<commit_msg>アー アアアア アーアー<commit_after>package lfs

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/github/git-lfs/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm"
)

func (c *Configuration) ntlmClientSession(creds Creds) (ntlm.ClientSession, error) {
	if c.ntlmSession != nil {
		return c.ntlmSession, nil
	}
	splits := strings.Split(creds["username"], "\\")

	if len(splits) != 2 {
		errorMessage := fmt.Sprintf("Your user name must be of the form DOMAIN\\user. It is currently %s", creds["username"], "string")
		return nil, errors.New(errorMessage)
	}

	session, err := ntlm.CreateClientSession(ntlm.Version2, ntlm.ConnectionOrientedMode)

	if err != nil {
		return nil, err
	}

	session.SetUserInfo(splits[1], creds["password"], strings.ToUpper(splits[0]))
	c.ntlmSession = session
	return session, nil
}

func DoNTLMRequest(request *http.Request, retry bool) (*http.Response, error) {
	handReq, err := cloneRequest(request)
	if err != nil {
		return nil, err
	}

	res, err := Config.HttpClient().Do(handReq)
	if err != nil && res == nil {
		return nil, err
	}

	//If the status is 401 then we need to re-authenticate, otherwise it was successful
	if res.StatusCode == 401 {

		creds, err := getCredsForAPI(request)
		if err != nil {
			return nil, err
		}

		negotiateReq, err := cloneRequest(request)
		if err != nil {
			return nil, err
		}

		challengeMessage, err := negotiate(negotiateReq, ntlmNegotiateMessage)
		if err != nil {
			return nil, err
		}

		challengeReq, err := cloneRequest(request)
		if err != nil {
			return nil, err
		}

		res, err := challenge(challengeReq, challengeMessage, creds)
		if err != nil {
			return nil, err
		}

		//If the status is 401 then we need to re-authenticate
		if res.StatusCode == 401 && retry == true {
			return DoNTLMRequest(challengeReq, false)
		}

		saveCredentials(creds, res)

		return res, nil
	}
	return res, nil
}

func negotiate(request *http.Request, message string) ([]byte, error) {
	request.Header.Add("Authorization", message)
	res, err := Config.HttpClient().Do(request)

	if res == nil && err != nil {
		return nil, err
	}

	io.Copy(ioutil.Discard, res.Body)
	res.Body.Close()

	ret, err := parseChallengeResponse(res)
	if err != nil {
		return nil, err
	}

	return ret, nil
}

func challenge(request *http.Request, challengeBytes []byte, creds Creds) (*http.Response, error) {
	challenge, err := ntlm.ParseChallengeMessage(challengeBytes)
	if err != nil {
		return nil, err
	}

	session, err := Config.ntlmClientSession(creds)
	if err != nil {
		return nil, err
	}

	session.ProcessChallengeMessage(challenge)
	authenticate, err := session.GenerateAuthenticateMessage()
	if err != nil {
		return nil, err
	}

	authMsg := base64.StdEncoding.EncodeToString(authenticate.Bytes())
	request.Header.Add("Authorization", "NTLM "+authMsg)
	return Config.HttpClient().Do(request)
}

func parseChallengeResponse(response *http.Response) ([]byte, error) {
	header := response.Header.Get("Www-Authenticate")
	if len(header) < 6 {
		return nil, fmt.Errorf("Invalid NTLM challenge response: %q", header)
	}

	//parse out the "NTLM " at the beginning of the response
	challenge := header[5:]
	val, err := base64.StdEncoding.DecodeString(challenge)

	if err != nil {
		return nil, err
	}
	return []byte(val), nil
}

func cloneRequest(request *http.Request) (*http.Request, error) {
	cloneReqBody, err := cloneRequestBody(request)
	if err != nil {
		return nil, err
	}

	clonedReq, err := http.NewRequest(request.Method, request.URL.String(), cloneReqBody)
	if err != nil {
		return nil, err
	}

	for k, _ := range request.Header {
		clonedReq.Header.Add(k, request.Header.Get(k))
	}

	clonedReq.ContentLength = request.ContentLength

	return clonedReq, nil
}

func cloneRequestBody(req *http.Request) (io.ReadCloser, error) {
	if req.Body == nil {
		return nil, nil
	}

	buf, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, err
	}

	req.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
	return ioutil.NopCloser(bytes.NewBuffer(buf)), nil
}

const ntlmNegotiateMessage = "NTLM TlRMTVNTUAABAAAAB7IIogwADAAzAAAACwALACgAAAAKAAAoAAAAD1dJTExISS1NQUlOTk9SVEhBTUVSSUNB"
<|endoftext|>


<commit_before>package termite

import (
	"bytes"
	"exec"
	"fmt"
	"github.com/hanwen/go-fuse/unionfs"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

type WorkerTask struct {
	*WorkRequest
	*WorkResponse
	stdinConn net.Conn
	mirror    *Mirror
	cmd       *exec.Cmd
	taskInfo string
}

func (me *WorkerTask) Kill() {
	// TODO - racy.
	if me.cmd.Process != nil {
		pid := me.cmd.Process.Pid
		errNo := syscall.Kill(pid, syscall.SIGQUIT)
		log.Printf("Killed pid %d, result %d", pid, errNo)
	}
}

func (me *WorkerTask) String() string {
	return me.taskInfo
}

func (me *WorkResponse) resetClock() {
	me.LastTime = time.Nanoseconds()
}

func (me *WorkResponse) clock(name string) {
	t := time.Nanoseconds()
	me.Timings = append(me.Timings,
		Timing{name, 1.0e-6 * float64(t-me.LastTime)})
	me.LastTime = t
}

func (me *WorkerTask) clock(name string) {
	me.WorkResponse.clock(name)
}

func (me *WorkerTask) Run() os.Error {
	fuseFs, err := me.mirror.newFs(me)
	if err != nil {
		return err
	}

	me.resetClock()
	err = me.runInFuse(fuseFs)
	defer me.mirror.returnFs(fuseFs)
	if me.mirror.considerReap(fuseFs, me) {
		me.WorkResponse.FileSet, me.WorkResponse.TaskIds = me.mirror.reapFuse(fuseFs)
	}

	return err
}

func (me *WorkerTask) runInFuse(fuseFs *workerFuseFs) os.Error {
	fuseFs.SetDebug(me.WorkRequest.Debug)
	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	// See /bin/true for the background of
	// /bin/true. http://code.google.com/p/go/issues/detail?id=2373
	me.cmd = exec.Command("/bin/true",
		me.WorkRequest.Argv[1:]...)

	cmd := me.cmd
	cmd.Path = me.WorkRequest.Binary
	cmd.Args[0] = me.WorkRequest.Argv[0]

	if os.Geteuid() == 0 {
		attr := &syscall.SysProcAttr{}
		attr.Credential = &syscall.Credential{
			Uid: uint32(me.mirror.daemon.Nobody.Uid),
			Gid: uint32(me.mirror.daemon.Nobody.Gid),
		}
		attr.Chroot = fuseFs.mount

		cmd.SysProcAttr = attr
		cmd.Dir = me.WorkRequest.Dir
	} else {
		cmd.Path = filepath.Join(fuseFs.mount, me.WorkRequest.Binary)
		cmd.Dir = filepath.Join(fuseFs.mount, me.WorkRequest.Dir)
	}

	cmd.Env = me.WorkRequest.Env
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	if me.stdinConn != nil {
		cmd.Stdin = me.stdinConn
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	printCmd := fmt.Sprintf("%v", cmd.Args)
	if me.WorkRequest.Debug {
		printCmd = fmt.Sprintf("%v", cmd)
	}
	me.taskInfo = fmt.Sprintf("%v, dir %v, fuse FS %d",
		printCmd, cmd.Dir, fuseFs.id)
	err := cmd.Wait()

	waitMsg, ok := err.(*os.Waitmsg)
	if ok {
		me.WorkResponse.Exit = *waitMsg
		err = nil
	}

	// No waiting: if the process exited, we kill the connection.
	if me.stdinConn != nil {
		me.stdinConn.Close()
	}

	// We could use a connection here too, but this is simpler.
	me.WorkResponse.Stdout = stdout.String()
	me.WorkResponse.Stderr = stderr.String()

	me.clock("worker.runCommand")
	return err
}

// Sorts FileAttr such deletions come reversed before additions.

func (me *Mirror) fillReply(ufs *unionfs.MemUnionFs) *FileSet {
	yield := ufs.Reap()
	wrRoot := strings.TrimLeft(me.writableRoot, "/")
	cache := me.daemon.contentCache

	files := []*FileAttr{}
	reapedHashes := map[string]string{}
	for path, v := range yield {
		f := &FileAttr{
			Path: filepath.Join(wrRoot, path),
		}

		f.FileInfo = v.FileInfo
		f.Link = v.Link
		if f.FileInfo != nil && f.FileInfo.IsRegular() {
			contentPath := filepath.Join(wrRoot, v.Original)
			if v.Original != "" && v.Original != contentPath {
				fa := me.rpcFs.attr.Get(contentPath)
				if fa.Hash == "" {
					log.Panicf("Contents for %q disappeared.", contentPath)
				}
				f.Hash = fa.Hash
			}
			if v.Backing != "" {
				f.Hash = reapedHashes[v.Backing]
				var err os.Error
				if f.Hash == "" {
					f.Hash, err = cache.DestructiveSavePath(v.Backing)
				}
				if err != nil {
					log.Fatalf("DestructiveSavePath fail %q: %v", v.Backing, err)
				} else {
					reapedHashes[v.Backing] = f.Hash
				}
			}
		}

		files = append(files, f)
	}
	fs := FileSet{files}
	ufs.Clear()
	fs.Sort()

	return &fs
}
<commit_msg>Use exec.Cmd directly rather than through exec.Command().<commit_after>package termite

import (
	"bytes"
	"exec"
	"fmt"
	"github.com/hanwen/go-fuse/unionfs"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

type WorkerTask struct {
	*WorkRequest
	*WorkResponse
	stdinConn net.Conn
	mirror    *Mirror
	cmd       *exec.Cmd
	taskInfo string
}

func (me *WorkerTask) Kill() {
	// TODO - racy.
	if me.cmd.Process != nil {
		pid := me.cmd.Process.Pid
		errNo := syscall.Kill(pid, syscall.SIGQUIT)
		log.Printf("Killed pid %d, result %d", pid, errNo)
	}
}

func (me *WorkerTask) String() string {
	return me.taskInfo
}

func (me *WorkResponse) resetClock() {
	me.LastTime = time.Nanoseconds()
}

func (me *WorkResponse) clock(name string) {
	t := time.Nanoseconds()
	me.Timings = append(me.Timings,
		Timing{name, 1.0e-6 * float64(t-me.LastTime)})
	me.LastTime = t
}

func (me *WorkerTask) clock(name string) {
	me.WorkResponse.clock(name)
}

func (me *WorkerTask) Run() os.Error {
	fuseFs, err := me.mirror.newFs(me)
	if err != nil {
		return err
	}

	me.resetClock()
	err = me.runInFuse(fuseFs)
	defer me.mirror.returnFs(fuseFs)
	if me.mirror.considerReap(fuseFs, me) {
		me.WorkResponse.FileSet, me.WorkResponse.TaskIds = me.mirror.reapFuse(fuseFs)
	}

	return err
}

func (me *WorkerTask) runInFuse(fuseFs *workerFuseFs) os.Error {
	fuseFs.SetDebug(me.WorkRequest.Debug)
	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	// See /bin/true for the background of
	// /bin/true. http://code.google.com/p/go/issues/detail?id=2373
	me.cmd = &exec.Cmd{
		Path: me.WorkRequest.Binary,
		Args: me.WorkRequest.Argv,
	}
	cmd := me.cmd
	if os.Geteuid() == 0 {
		attr := &syscall.SysProcAttr{}
		attr.Credential = &syscall.Credential{
			Uid: uint32(me.mirror.daemon.Nobody.Uid),
			Gid: uint32(me.mirror.daemon.Nobody.Gid),
		}
		attr.Chroot = fuseFs.mount

		cmd.SysProcAttr = attr
		cmd.Dir = me.WorkRequest.Dir
	} else {
		cmd.Path = filepath.Join(fuseFs.mount, me.WorkRequest.Binary)
		cmd.Dir = filepath.Join(fuseFs.mount, me.WorkRequest.Dir)
	}

	cmd.Env = me.WorkRequest.Env
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	if me.stdinConn != nil {
		cmd.Stdin = me.stdinConn
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	printCmd := fmt.Sprintf("%v", cmd.Args)
	if me.WorkRequest.Debug {
		printCmd = fmt.Sprintf("%v", cmd)
	}
	me.taskInfo = fmt.Sprintf("%v, dir %v, fuse FS %d",
		printCmd, cmd.Dir, fuseFs.id)
	err := cmd.Wait()

	waitMsg, ok := err.(*os.Waitmsg)
	if ok {
		me.WorkResponse.Exit = *waitMsg
		err = nil
	}

	// No waiting: if the process exited, we kill the connection.
	if me.stdinConn != nil {
		me.stdinConn.Close()
	}

	// We could use a connection here too, but this is simpler.
	me.WorkResponse.Stdout = stdout.String()
	me.WorkResponse.Stderr = stderr.String()

	me.clock("worker.runCommand")
	return err
}

// Sorts FileAttr such deletions come reversed before additions.

func (me *Mirror) fillReply(ufs *unionfs.MemUnionFs) *FileSet {
	yield := ufs.Reap()
	wrRoot := strings.TrimLeft(me.writableRoot, "/")
	cache := me.daemon.contentCache

	files := []*FileAttr{}
	reapedHashes := map[string]string{}
	for path, v := range yield {
		f := &FileAttr{
			Path: filepath.Join(wrRoot, path),
		}

		f.FileInfo = v.FileInfo
		f.Link = v.Link
		if f.FileInfo != nil && f.FileInfo.IsRegular() {
			contentPath := filepath.Join(wrRoot, v.Original)
			if v.Original != "" && v.Original != contentPath {
				fa := me.rpcFs.attr.Get(contentPath)
				if fa.Hash == "" {
					log.Panicf("Contents for %q disappeared.", contentPath)
				}
				f.Hash = fa.Hash
			}
			if v.Backing != "" {
				f.Hash = reapedHashes[v.Backing]
				var err os.Error
				if f.Hash == "" {
					f.Hash, err = cache.DestructiveSavePath(v.Backing)
				}
				if err != nil {
					log.Fatalf("DestructiveSavePath fail %q: %v", v.Backing, err)
				} else {
					reapedHashes[v.Backing] = f.Hash
				}
			}
		}

		files = append(files, f)
	}
	fs := FileSet{files}
	ufs.Clear()
	fs.Sort()

	return &fs
}
<|endoftext|>


<commit_before>package presence

import (
	"errors"
	"fmt"
	"testing"
)

func TestErrorLen(t *testing.T) {
	e := Error{}
	id1 := <-nextID
	id2 := <-nextID
	err1 := errors.New(id1)
	err2 := errors.New(id2)

	e.Append(id1, err1)
	e.Append(id2, err2)

	if e.Len() != 2 {
		t.Fatalf("err len should be 2, but got:", e.Len())
	}
}

func TestErrorEach(t *testing.T) {
	e := Error{}
	id := <-nextID
	err := errors.New(id)

	e.Append(id, err)
	e.Each(func(idx string, errx error) {
		if idx != id {
			t.Fatalf("error id should be: %s, but got: %s", id, idx)
		}
		if err.Error() != errx.Error() {
			t.Fatalf("error message should be: %s, but got: %s", err.Error(), errx.Error())
		}
	})
}

func TestErrorString(t *testing.T) {
	e := Error{}
	id1 := <-nextID
	id2 := <-nextID
	err1 := errors.New(id1)
	err2 := errors.New(id2)

	e.Append(id1, err1)
	e.Append(id2, err2)

	errMessage := fmt.Sprintf("Presence Error:{id: %[1]s, err:%[1]s}{id: %[2]s, err:%[2]s}", id1, id2)
	if e.Error() != errMessage {
		t.Fatalf("err message should be \"%s\", but got: \"%s\"", errMessage, e.Error())
	}
}

func TestErrorHas(t *testing.T) {
	e := Error{}
	id1 := <-nextID
	id2 := <-nextID
	err1 := errors.New(id1)

	e.Append(id1, err1)

	if !e.Has(id1) {
		t.Fatalf("multi err should have %s, but it doesnt", id1)
	}
	if e.Has(id2) {
		t.Fatalf("multi err should not have %s, but it does", id2)
	}
}
<commit_msg>Presence: fix formatting<commit_after>package presence

import (
	"errors"
	"fmt"
	"testing"
)

func TestErrorLen(t *testing.T) {
	e := Error{}
	id1 := <-nextID
	id2 := <-nextID
	err1 := errors.New(id1)
	err2 := errors.New(id2)

	e.Append(id1, err1)
	e.Append(id2, err2)

	if e.Len() != 2 {
		t.Fatalf("err len should be 2, but got: %d", e.Len())
	}
}

func TestErrorEach(t *testing.T) {
	e := Error{}
	id := <-nextID
	err := errors.New(id)

	e.Append(id, err)
	e.Each(func(idx string, errx error) {
		if idx != id {
			t.Fatalf("error id should be: %s, but got: %s", id, idx)
		}
		if err.Error() != errx.Error() {
			t.Fatalf("error message should be: %s, but got: %s", err.Error(), errx.Error())
		}
	})
}

func TestErrorString(t *testing.T) {
	e := Error{}
	id1 := <-nextID
	id2 := <-nextID
	err1 := errors.New(id1)
	err2 := errors.New(id2)

	e.Append(id1, err1)
	e.Append(id2, err2)

	errMessage := fmt.Sprintf("Presence Error:{id: %[1]s, err:%[1]s}{id: %[2]s, err:%[2]s}", id1, id2)
	if e.Error() != errMessage {
		t.Fatalf("err message should be \"%s\", but got: \"%s\"", errMessage, e.Error())
	}
}

func TestErrorHas(t *testing.T) {
	e := Error{}
	id1 := <-nextID
	id2 := <-nextID
	err1 := errors.New(id1)

	e.Append(id1, err1)

	if !e.Has(id1) {
		t.Fatalf("multi err should have %s, but it doesnt", id1)
	}
	if e.Has(id2) {
		t.Fatalf("multi err should not have %s, but it does", id2)
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package versioner

import (
	"errors"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"github.com/syncthing/syncthing/lib/fs"

	"github.com/kballard/go-shellquote"
)

func init() {
	// Register the constructor for this type of versioner with the name "external"
	Factories["external"] = NewExternal
}

type External struct {
	command    string
	filesystem fs.Filesystem
}

func NewExternal(folderID string, filesystem fs.Filesystem, params map[string]string) Versioner {
	command := params["command"]

	if runtime.GOOS == "windows" {
		command = strings.Replace(command, `\`, `\\`, -1)
	}

	s := External{
		command:    command,
		filesystem: filesystem,
	}

	l.Debugf("instantiated %#v", s)
	return s
}

// Archive moves the named file away to a version archive. If this function
// returns nil, the named file does not exist any more (has been archived).
func (v External) Archive(filePath string) error {
	info, err := v.filesystem.Lstat(filePath)
	if fs.IsNotExist(err) {
		l.Debugln("not archiving nonexistent file", filePath)
		return nil
	} else if err != nil {
		return err
	}
	if info.IsSymlink() {
		panic("bug: attempting to version a symlink")
	}

	l.Debugln("archiving", filePath)

	if v.command == "" {
		return errors.New("Versioner: command is empty, please enter a valid command")
	}

	words, err := shellquote.Split(v.command)
	if err != nil {
		return errors.New("Versioner: command is invalid: " + err.Error())
	}

	context := map[string]string{
		"%FOLDER_FILESYSTEM%": v.filesystem.Type().String(),
		"%FOLDER_PATH%":       v.filesystem.URI(),
		"%FILE_PATH%":         filePath,
	}

	for i, word := range words {
		if replacement, ok := context[word]; ok {
			words[i] = replacement
		}
	}

	cmd := exec.Command(words[0], words[1:]...)
	env := os.Environ()
	// filter STGUIAUTH and STGUIAPIKEY from environment variables
	filteredEnv := []string{}
	for _, x := range env {
		if !strings.HasPrefix(x, "STGUIAUTH=") && !strings.HasPrefix(x, "STGUIAPIKEY=") {
			filteredEnv = append(filteredEnv, x)
		}
	}
	cmd.Env = filteredEnv
	combinedOutput, err := cmd.CombinedOutput()
	l.Debugln("external command output:", string(combinedOutput))
	if err != nil {
		return err
	}

	// return error if the file was not removed
	if _, err = v.filesystem.Lstat(filePath); fs.IsNotExist(err) {
		return nil
	}
	return errors.New("Versioner: file was not removed by external script")
}

func (v External) GetVersions() (map[string][]FileVersion, error) {
	return nil, ErrRestorationNotSupported
}

func (v External) Restore(filePath string, versionTime time.Time) error {
	return ErrRestorationNotSupported
}
<commit_msg>lib/versioner: Replace multiple placeholders in a single token in external command (fixes #5849)<commit_after>// Copyright (C) 2015 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package versioner

import (
	"errors"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

	"github.com/syncthing/syncthing/lib/fs"

	"github.com/kballard/go-shellquote"
)

func init() {
	// Register the constructor for this type of versioner with the name "external"
	Factories["external"] = NewExternal
}

type External struct {
	command    string
	filesystem fs.Filesystem
}

func NewExternal(folderID string, filesystem fs.Filesystem, params map[string]string) Versioner {
	command := params["command"]

	if runtime.GOOS == "windows" {
		command = strings.Replace(command, `\`, `\\`, -1)
	}

	s := External{
		command:    command,
		filesystem: filesystem,
	}

	l.Debugf("instantiated %#v", s)
	return s
}

// Archive moves the named file away to a version archive. If this function
// returns nil, the named file does not exist any more (has been archived).
func (v External) Archive(filePath string) error {
	info, err := v.filesystem.Lstat(filePath)
	if fs.IsNotExist(err) {
		l.Debugln("not archiving nonexistent file", filePath)
		return nil
	} else if err != nil {
		return err
	}
	if info.IsSymlink() {
		panic("bug: attempting to version a symlink")
	}

	l.Debugln("archiving", filePath)

	if v.command == "" {
		return errors.New("Versioner: command is empty, please enter a valid command")
	}

	words, err := shellquote.Split(v.command)
	if err != nil {
		return errors.New("Versioner: command is invalid: " + err.Error())
	}

	context := map[string]string{
		"%FOLDER_FILESYSTEM%": v.filesystem.Type().String(),
		"%FOLDER_PATH%":       v.filesystem.URI(),
		"%FILE_PATH%":         filePath,
	}

	for i, word := range words {
		for key, val := range context {
			word = strings.Replace(word, key, val, -1)
		}

		words[i] = word
	}

	cmd := exec.Command(words[0], words[1:]...)
	env := os.Environ()
	// filter STGUIAUTH and STGUIAPIKEY from environment variables
	filteredEnv := []string{}
	for _, x := range env {
		if !strings.HasPrefix(x, "STGUIAUTH=") && !strings.HasPrefix(x, "STGUIAPIKEY=") {
			filteredEnv = append(filteredEnv, x)
		}
	}
	cmd.Env = filteredEnv
	combinedOutput, err := cmd.CombinedOutput()
	l.Debugln("external command output:", string(combinedOutput))
	if err != nil {
		return err
	}

	// return error if the file was not removed
	if _, err = v.filesystem.Lstat(filePath); fs.IsNotExist(err) {
		return nil
	}
	return errors.New("Versioner: file was not removed by external script")
}

func (v External) GetVersions() (map[string][]FileVersion, error) {
	return nil, ErrRestorationNotSupported
}

func (v External) Restore(filePath string, versionTime time.Time) error {
	return ErrRestorationNotSupported
}
<|endoftext|>


<commit_before>package main

import (
	. "./quadedge"
	"fmt"
	"github.com/ajstarks/svgo/float"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"os"
	//	"text/template"
	"bytes"
)

var fileno int = 0

func nextfile() string {
	fileno++
	return fmt.Sprintf("hello%02d.svg", fileno)
}

var debug bool = false

func debugDraw(e0 *Edge, e1 *Edge) {
	if !debug {
		return
	}
	file, err := os.Create(nextfile())
	if err != nil {
		panic("can't create file")
	}
	s := svg.New(file)
	s.Start(1000, 1000)
	ox, oy := 100.0, 100.0 // put origin at (100,100)
	//	small, _ := BoundingBox(e0)
	//	dx, dy := ox-small.X, oy-small.Y
	dx, dy := ox, oy
	s.Circle(ox, oy, 5, "fill:black;stroke:black")
	for i, e := range e0.Edges() {
		if i == 0 {
			s.Circle(e.Org().X+dx, e.Org().Y+dy, 3, "fill:green;stroke:none")
			s.Line(e.Org().X+dx, e.Org().Y+dy,
				e.Dest().X+dx, e.Dest().Y+dy,
				"stroke:#f00;stroke-width:1")
		} else {
			s.Line(e.Org().X+dx, e.Org().Y+dy,
				e.Dest().X+dx, e.Dest().Y+dy,
				"stroke:#00f;stroke-width:1")
		}
	}
	if e1 != nil {
		for i, e := range e1.Edges() {
			if i == 0 {
				s.Circle(e.Org().X+dx, e.Org().Y+dy, 3, "fill:blue;stroke:none")
				s.Line(e.Org().X+dx, e.Org().Y+dy,
					e.Dest().X+dx, e.Dest().Y+dy,
					"stroke:#f00;stroke-width:1")
			} else {
				s.Line(e.Org().X+dx, e.Org().Y+dy,
					e.Dest().X+dx, e.Dest().Y+dy,
					"stroke:#00f;stroke-width:1")
			}
		}
	}
	s.End()
}

func edgeLength(e *Edge) float64 {
	if e == nil {
		return 0.0
	}
	dx := e.Dest().X - e.Org().X
	dy := e.Dest().Y - e.Org().Y
	return math.Sqrt(dx*dx + dy*dy)
}

func edgeRadians(e *Edge) float64 {
	dx := e.Dest().X - e.Org().X
	dy := e.Dest().Y - e.Org().Y
	return math.Atan2(dy, dx)
}

func scale(e0 *Edge, sf float64) {
	for _, e := range e0.Edges() {
		e.SetOrg(&Point2D{sf * e.Org().X, sf * e.Org().Y})
		e = e.Sym()
		e.SetOrg(&Point2D{sf * e.Org().X, sf * e.Org().Y})
	}
}

func rotate(e0 *Edge, rad float64) {
	rotatePoint := func(p *Point2D) *Point2D {
		angle := math.Atan2(p.Y, p.X)
		distance := math.Sqrt(p.X*p.X + p.Y*p.Y)
		angle2 := angle + rad
		y, x := math.Sincos(angle2)
		x, y = distance*x, distance*y
		return &Point2D{x, y}
	}
	for _, e := range e0.Edges() {
		e.SetOrg(rotatePoint(e.Org()))
		e = e.Sym()
		e.SetOrg(rotatePoint(e.Org()))
	}
}

func translate(e0 *Edge, dx, dy float64) {
	for _, e := range e0.Edges() {
		e.SetOrg(&Point2D{dx + e.Org().X, dy + e.Org().Y})
		e = e.Sym()
		e.SetOrg(&Point2D{dx + e.Org().X, dy + e.Org().Y})
	}
}

func tab() *Edge {
	pts := []*Point2D{{0.0, 0.0}, {40.0, 0.0}, {30.0, 10.0}, {10.0, 10.0}}
	return Polygon(pts)
}

func attach(e1, e2 *Edge) {
	debugDraw(e1, e2)
	l1 := edgeLength(e1)
	l2 := edgeLength(e2)
	if l1 == 0.0 || l2 == 0.0 {
		return
	}
	sf := l1 / l2
	translate(e2, -e2.Org().X, -e2.Org().Y) // bring origin of e2 to absolute origin (0,0)
	debugDraw(e1, e2)
	scale(e2, sf)
	debugDraw(e1, e2)
	rotate(e2, edgeRadians(e1)-edgeRadians(e2)+math.Pi)
	debugDraw(e1, e2)
	translate(e2, e1.Dest().X, e1.Dest().Y)
	debugDraw(e1, e2)
	Splice(e1.Oprev(), e2.Sym())
	Splice(e1.Sym(), e2.Oprev())
	DeleteEdge(e2)
	debugDraw(e1, nil)
}

func main() {
	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile", Compile)
	log.Printf("Listening on localhost:1999")
	log.Fatal(http.ListenAndServe("127.0.0.1:1999", nil))
}

func FrontPage(w http.ResponseWriter, req *http.Request) {
	w.Write(frontPageText)
	//	frontPage.Execute(w)
}

//var frontPage = template.Must(template.New("frontPage").Parse(frontPageText)) // HTML template
var frontPageText = []byte(`<!doctype html>

<title>Man, I Fold</title>

3-9: polygon, f: forward, b: back, r: reverse, s: save, t: tab, z: zero

<input autofocus="true" id="edit" onkeydown="keyHandler(event);"></input>

`)

var e0 *Edge
var outright = true
var internal = make(map[*QuadEdge]bool)

func attachAndMove(e1 *Edge) {
	if e0 == nil {
		e0 = e1
	} else {
		internal[e0.Q] = true
		if outright {
			attach(e0, e1)
			e0 = e0.Oprev()
		} else {
			attach(e0.Sym(), e1)
			e0 = e0.Onext()
		}
	}
}

func Compile(w http.ResponseWriter, req *http.Request) {
	cmd, err := ioutil.ReadAll(req.Body)
	if err != nil {
		w.WriteHeader(404)
		return
	}
	switch string(cmd) {
	case "3":
		attachAndMove(Ngon(3, 40))
	case "4":
		attachAndMove(Ngon(4, 40))
	case "5":
		attachAndMove(Ngon(5, 40))
	case "6":
		attachAndMove(Ngon(6, 40))
	case "7":
		attachAndMove(Ngon(7, 40))
	case "8":
		attachAndMove(Ngon(8, 40))
	case "9":
		attachAndMove(Ngon(9, 40))
	case "f":
		if outright {
			e0 = e0.Sym().Onext()
		} else {
			e0 = e0.Sym().Oprev()
		}
	case "b":
		if outright {
			e0 = e0.Oprev().Sym()
		} else {
			e0 = e0.Onext().Sym()
		}
	case "r":
		e0 = e0.Sym()
		outright = !outright
	case "s":
		file, err := os.Create("hello.svg")
		if err != nil {
			log.Fatal(err)
		}
		out := draw(&options{false,false})
		file.Write(out)
	case "t":
		attachAndMove(tab())
	case "z":
		e0 = nil
		internal = make(map[*QuadEdge]bool)
		outright = true
	default:
	}
	out := draw(nil)
	w.Write(out) // ignore err
}

type options struct {
	origin bool
	cursor bool
}

func draw(opt *options) []byte {
	printOrigin, printCursor := true, true
	if opt != nil {
		printOrigin = opt.origin
		printCursor = opt.cursor
	}
	buf := new(bytes.Buffer)
	s := svg.New(buf)
	s.Start(1000, 1000)
	// arrowhead
	s.Marker("Triangle", 0, 5, 20, 10, "viewBox='0 0 10 10' markerUnits='strokeWidth' orient='auto'")
	s.Path("M 0 0 L 10 5 L 0 10 z")
	s.MarkerEnd()
	ox, oy := 100.0, 100.0 // put origin at (100,100)
	//	small, _ := BoundingBox(e0)
	//	dx, dy := ox-small.X, oy-small.Y
	dx, dy := ox, oy
	if printOrigin {
		s.Circle(ox, oy, 5, "fill:black;stroke:black")
	}
	for i, e := range e0.Edges() {
		if i == 0 && printCursor {
			s.Line(e.Org().X+dx, e.Org().Y+dy,
				e.Dest().X+dx, e.Dest().Y+dy,
				"marker-end='url(#Triangle)' style='stroke:#f00;stroke-width:1'")
		} else if internal[e.Q] {
			s.Line(e.Org().X+dx, e.Org().Y+dy,
				e.Dest().X+dx, e.Dest().Y+dy,
				"stroke:#000;stroke-width:1;stroke-dasharray:4")
		} else {
			s.Line(e.Org().X+dx, e.Org().Y+dy,
				e.Dest().X+dx, e.Dest().Y+dy,
				"stroke:#000;stroke-width:1")
		}
	}
	s.End()
	return buf.Bytes()
}
<commit_msg>Add scaling etc<commit_after>package main

import (
	. "./quadedge"
	"fmt"
	"github.com/ajstarks/svgo/float"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"os"
	//	"text/template"
	"bytes"
)

var fileno int = 0

func nextfile() string {
	fileno++
	return fmt.Sprintf("hello%02d.svg", fileno)
}

var debug bool = false

func debugDraw(e0 *Edge, e1 *Edge) {
	if !debug {
		return
	}
	file, err := os.Create(nextfile())
	if err != nil {
		panic("can't create file")
	}
	s := svg.New(file)
	s.Start(1000, 1000)
	ox, oy := 100.0, 100.0 // put origin at (100,100)
	//	small, _ := BoundingBox(e0)
	//	dx, dy := ox-small.X, oy-small.Y
	dx, dy := ox, oy
	s.Circle(ox, oy, 5, "fill:black;stroke:black")
	for i, e := range e0.Edges() {
		if i == 0 {
			s.Circle(e.Org().X+dx, e.Org().Y+dy, 3, "fill:green;stroke:none")
			s.Line(e.Org().X+dx, e.Org().Y+dy,
				e.Dest().X+dx, e.Dest().Y+dy,
				"stroke:#f00;stroke-width:1")
		} else {
			s.Line(e.Org().X+dx, e.Org().Y+dy,
				e.Dest().X+dx, e.Dest().Y+dy,
				"stroke:#00f;stroke-width:1")
		}
	}
	if e1 != nil {
		for i, e := range e1.Edges() {
			if i == 0 {
				s.Circle(e.Org().X+dx, e.Org().Y+dy, 3, "fill:blue;stroke:none")
				s.Line(e.Org().X+dx, e.Org().Y+dy,
					e.Dest().X+dx, e.Dest().Y+dy,
					"stroke:#f00;stroke-width:1")
			} else {
				s.Line(e.Org().X+dx, e.Org().Y+dy,
					e.Dest().X+dx, e.Dest().Y+dy,
					"stroke:#00f;stroke-width:1")
			}
		}
	}
	s.End()
}

func edgeLength(e *Edge) float64 {
	if e == nil {
		return 0.0
	}
	dx := e.Dest().X - e.Org().X
	dy := e.Dest().Y - e.Org().Y
	return math.Sqrt(dx*dx + dy*dy)
}

func edgeRadians(e *Edge) float64 {
	dx := e.Dest().X - e.Org().X
	dy := e.Dest().Y - e.Org().Y
	return math.Atan2(dy, dx)
}

func scale(e0 *Edge, sf float64) {
	for _, e := range e0.Edges() {
		e.SetOrg(&Point2D{sf * e.Org().X, sf * e.Org().Y})
		e = e.Sym()
		e.SetOrg(&Point2D{sf * e.Org().X, sf * e.Org().Y})
	}
}

func rotate(e0 *Edge, rad float64) {
	rotatePoint := func(p *Point2D) *Point2D {
		angle := math.Atan2(p.Y, p.X)
		distance := math.Sqrt(p.X*p.X + p.Y*p.Y)
		angle2 := angle + rad
		y, x := math.Sincos(angle2)
		x, y = distance*x, distance*y
		return &Point2D{x, y}
	}
	for _, e := range e0.Edges() {
		e.SetOrg(rotatePoint(e.Org()))
		e = e.Sym()
		e.SetOrg(rotatePoint(e.Org()))
	}
}

func translate(e0 *Edge, dx, dy float64) {
	for _, e := range e0.Edges() {
		e.SetOrg(&Point2D{dx + e.Org().X, dy + e.Org().Y})
		e = e.Sym()
		e.SetOrg(&Point2D{dx + e.Org().X, dy + e.Org().Y})
	}
}

func tab() *Edge {
	pts := []*Point2D{{0.0, 0.0}, {40.0, 0.0}, {30.0, 10.0}, {10.0, 10.0}}
	return Polygon(pts)
}

func attach(e1, e2 *Edge) {
	debugDraw(e1, e2)
	l1 := edgeLength(e1)
	l2 := edgeLength(e2)
	if l1 == 0.0 || l2 == 0.0 {
		return
	}
	sf := l1 / l2
	translate(e2, -e2.Org().X, -e2.Org().Y) // bring origin of e2 to absolute origin (0,0)
	debugDraw(e1, e2)
	scale(e2, sf)
	debugDraw(e1, e2)
	rotate(e2, edgeRadians(e1)-edgeRadians(e2)+math.Pi)
	debugDraw(e1, e2)
	translate(e2, e1.Dest().X, e1.Dest().Y)
	debugDraw(e1, e2)
	Splice(e1.Oprev(), e2.Sym())
	Splice(e1.Sym(), e2.Oprev())
	DeleteEdge(e2)
	debugDraw(e1, nil)
}

func main() {
	http.HandleFunc("/", FrontPage)
	http.HandleFunc("/compile", Compile)
	log.Printf("Listening on localhost:1999")
	log.Fatal(http.ListenAndServe("127.0.0.1:1999", nil))
}

func FrontPage(w http.ResponseWriter, req *http.Request) {
	w.Write(frontPageText)
	//	frontPage.Execute(w)
}

//var frontPage = template.Must(template.New("frontPage").Parse(frontPageText)) // HTML template
var frontPageText = []byte(`<!doctype html>

<title>Man, I Fold</title>

3-9: polygon, f: forward, b: back, r: reverse, s: save, t: tab, z: zero, m: maximize toggle

<input autofocus="true" id="edit" onkeydown="keyHandler(event);"></input>

`)

var e0 *Edge
var outright = true
var internal = make(map[*QuadEdge]bool)

func attachAndMove(e1 *Edge) {
	if e0 == nil {
		e0 = e1
	} else {
		internal[e0.Q] = true
		if outright {
			attach(e0, e1)
			e0 = e0.Oprev()
		} else {
			attach(e0.Sym(), e1)
			e0 = e0.Onext()
		}
	}
}

func Compile(w http.ResponseWriter, req *http.Request) {
	cmd, err := ioutil.ReadAll(req.Body)
	if err != nil {
		w.WriteHeader(404)
		return
	}
	switch string(cmd) {
	case "3":
		attachAndMove(Ngon(3, 40))
	case "4":
		attachAndMove(Ngon(4, 40))
	case "5":
		attachAndMove(Ngon(5, 40))
	case "6":
		attachAndMove(Ngon(6, 40))
	case "7":
		attachAndMove(Ngon(7, 40))
	case "8":
		attachAndMove(Ngon(8, 40))
	case "9":
		attachAndMove(Ngon(9, 40))
	case "b":
		if outright {
			e0 = e0.Oprev().Sym()
		} else {
			e0 = e0.Onext().Sym()
		}
	case "f":
		if outright {
			e0 = e0.Sym().Onext()
		} else {
			e0 = e0.Sym().Oprev()
		}
	case "m":
		maximize = !maximize
	case "r":
		e0 = e0.Sym()
		outright = !outright
	case "s":
		file, err := os.Create("hello.svg")
		if err != nil {
			log.Fatal(err)
		}
		out := draw(&options{false, false})
		file.Write(out)
	case "t":
		attachAndMove(tab())
	case "z":
		e0 = nil
		internal = make(map[*QuadEdge]bool)
		outright = true
	default:
	}
	out := draw(nil)
	w.Write(out) // ignore err
}

type options struct {
	border bool
	cursor bool
}

var maximize = false

func draw(opt *options) []byte {
	printBorder, printCursor := true, true
	if opt != nil {
		printBorder = opt.border
		printCursor = opt.cursor
	}
	buf := new(bytes.Buffer)
	s := svg.New(buf)
	s.Startunit(11.0, 8.5, "in", "viewBox='0 0 1100 850'")
	if printBorder {
		s.Rect(0, 0, 1100, 850, "stroke:black; fill:none")
	}
	if e0 == nil {
		s.End()
		return buf.Bytes()
	}
	// arrowhead
	s.Marker("Triangle", 0, 5, 20, 10, "viewBox='0 0 10 10' markerUnits='strokeWidth' orient='auto'")
	s.Path("M 0 0 L 10 5 L 0 10 z")
	s.MarkerEnd()
	small, big := BoundingBox(e0)

	// margin
	s.Gtransform(fmt.Sprintf("scale(%f) translate(25,25)", 1050.0/1100.0))

	scale := 1.0
	width := big.X - small.X
	height := big.Y - small.Y
	scaleX := 1100.0 / width
	scaleY := 850 / height
	if scaleX < 1 || scaleY < 1 || maximize { // must scale down to fit or up to maximize
		scale = math.Min(scaleX, scaleY)
	}
	if scale != 1 {
		s.Gtransform(fmt.Sprintf("scale(%f)", scale))
	}

	shift := small.X < 0 || small.Y < 0 || maximize
	if shift {
		dx, dy := -small.X, -small.Y
		s.Gtransform(fmt.Sprintf("translate(%f,%f)", dx, dy))
	}

	//	dx, dy := ox-small.X, oy-small.Y
	for i, e := range e0.Edges() {
		if i == 0 && printCursor {
			s.Line(e.Org().X, e.Org().Y,
				e.Dest().X, e.Dest().Y,
				"marker-end='url(#Triangle)' style='stroke:#f00;stroke-width:1'")
		} else if internal[e.Q] {
			s.Line(e.Org().X, e.Org().Y,
				e.Dest().X, e.Dest().Y,
				"stroke:#000;stroke-width:1;stroke-dasharray:4")
		} else {
			s.Line(e.Org().X, e.Org().Y,
				e.Dest().X, e.Dest().Y,
				"stroke:#000;stroke-width:1")
		}
	}
	if shift {
		s.Gend()
	}
	if scale != 1 {
		s.Gend()
	}
	s.Gend()
	s.End()
	return buf.Bytes()
}
<|endoftext|>


<commit_before>package udp

import (
	"expvar"
	"fmt"
	"github.com/davyxu/cellnet"
	"github.com/davyxu/cellnet/peer"
	"github.com/davyxu/cellnet/util"
	"net"
	"time"
)

const MaxUDPRecvBuffer = 2048

type udpAcceptor struct {
	peer.CoreSessionManager
	peer.CorePeerProperty
	peer.CoreContextSet
	peer.CoreRunningTag
	peer.CoreProcBundle
	peer.CoreCaptureIOPanic

	conn *net.UDPConn

	sesQueue *util.Queue

	sesTimeout time.Duration

	mtSesQueueCount      *expvar.Int
	mtTotalRecvUDPPacket *expvar.Int
}

func (self *udpAcceptor) IsReady() bool {

	return self.IsRunning()
}

func (self *udpAcceptor) Port() int {
	if self.conn == nil {
		return 0
	}

	return self.conn.LocalAddr().(*net.UDPAddr).Port
}

func (self *udpAcceptor) Start() cellnet.Peer {

	if self.mtSesQueueCount == nil {
		self.mtSesQueueCount = expvar.NewInt(fmt.Sprintf("cellnet.Peer(%s).SessionQueueCount", self.Name()))
	}

	if self.mtTotalRecvUDPPacket == nil {
		self.mtTotalRecvUDPPacket = expvar.NewInt(fmt.Sprintf("cellnet.Peer(%s).TotalRecvUDPPacket", self.Name()))
	}

	ln, err := util.DetectPort(self.Address(), func(a *util.Address) (interface{}, error) {

		addr, err := net.ResolveUDPAddr("udp", a.HostPort())
		if err != nil {
			return nil, err
		}

		return net.ListenUDP("udp", addr)
	})

	if err != nil {

		log.Errorf("#udp.resolve failed(%s) %v", self.Name(), err.Error())
		return self
	}

	self.conn = ln.(*net.UDPConn)

	if err != nil {
		log.Errorf("#udp.listen failed(%s) %s", self.Name(), err.Error())
		self.SetRunning(false)
		return self
	}

	log.Infof("#udp.listen(%s) %s", self.Name(), self.Address())

	go self.accept()

	return self
}

func (self *udpAcceptor) protectedRecvPacket(ses *udpSession, data []byte) {
	defer func() {

		if err := recover(); err != nil {
			log.Errorf("IO panic: %s", err)
			self.conn.Close()
		}

	}()

	ses.Recv(data)
}

func (self *udpAcceptor) accept() {

	self.SetRunning(true)

	recvBuff := make([]byte, MaxUDPRecvBuffer)

	for {

		n, remoteAddr, err := self.conn.ReadFromUDP(recvBuff)
		if err != nil {
			break
		}

		if n > 0 {
			self.mtTotalRecvUDPPacket.Add(1)

			ses := self.allocSession(remoteAddr)

			if self.CaptureIOPanic() {
				self.protectedRecvPacket(ses, recvBuff[:n])
			} else {
				ses.Recv(recvBuff[:n])
			}

		}

	}

	self.SetRunning(false)

}

func (self *udpAcceptor) allocSession(addr *net.UDPAddr) *udpSession {

	var ses *udpSession

	if self.sesQueue.Count() > 0 {
		ses = self.sesQueue.Peek().(*udpSession)

		// 这个session还能用，需要重新new
		if ses.IsAlive() {
			ses = nil
		} else {
			// 可以复用
			ses = self.sesQueue.Dequeue().(*udpSession)
		}

	}

	if ses == nil {
		ses = &udpSession{}
		self.sesQueue.Enqueue(ses)
	}

	self.mtSesQueueCount.Set(int64(self.sesQueue.Count()))

	ses.timeOutTick = time.Now().Add(self.sesTimeout)
	ses.conn = self.conn
	ses.remote = addr
	ses.pInterface = self
	ses.CoreProcBundle = &self.CoreProcBundle

	return ses
}

func (self *udpAcceptor) SetSessionTTL(dur time.Duration) {
	self.sesTimeout = dur
}

func (self *udpAcceptor) Stop() {

	if self.conn != nil {
		self.conn.Close()
	}

	// TODO 等待accept线程结束
	self.SetRunning(false)
}

func (self *udpAcceptor) TypeName() string {
	return "udp.Acceptor"
}

func init() {

	peer.RegisterPeerCreator(func() cellnet.Peer {
		p := &udpAcceptor{
			sesQueue:   util.NewQueue(64),
			sesTimeout: time.Second,
		}

		return p
	})
}
<commit_msg>udp侦听器不在依赖expvar<commit_after>package udp

import (
	"github.com/davyxu/cellnet"
	"github.com/davyxu/cellnet/peer"
	"github.com/davyxu/cellnet/util"
	"net"
	"time"
)

const MaxUDPRecvBuffer = 2048

type udpAcceptor struct {
	peer.CoreSessionManager
	peer.CorePeerProperty
	peer.CoreContextSet
	peer.CoreRunningTag
	peer.CoreProcBundle
	peer.CoreCaptureIOPanic

	conn *net.UDPConn

	sesQueue *util.Queue

	sesTimeout time.Duration
}

func (self *udpAcceptor) IsReady() bool {

	return self.IsRunning()
}

func (self *udpAcceptor) Port() int {
	if self.conn == nil {
		return 0
	}

	return self.conn.LocalAddr().(*net.UDPAddr).Port
}

func (self *udpAcceptor) Start() cellnet.Peer {

	var finalAddr *util.Address
	ln, err := util.DetectPort(self.Address(), func(a *util.Address) (interface{}, error) {

		addr, err := net.ResolveUDPAddr("udp", a.HostPort())
		if err != nil {
			return nil, err
		}

		finalAddr = a

		return net.ListenUDP("udp", addr)
	})

	if err != nil {

		log.Errorf("#udp.resolve failed(%s) %v", self.Name(), err.Error())
		return self
	}

	self.conn = ln.(*net.UDPConn)

	if err != nil {
		log.Errorf("#udp.listen failed(%s) %s", self.Name(), err.Error())
		self.SetRunning(false)
		return self
	}

	log.Infof("#udp.listen(%s) %s", self.Name(), finalAddr.String())

	go self.accept()

	return self
}

func (self *udpAcceptor) protectedRecvPacket(ses *udpSession, data []byte) {
	defer func() {

		if err := recover(); err != nil {
			log.Errorf("IO panic: %s", err)
			self.conn.Close()
		}

	}()

	ses.Recv(data)
}

func (self *udpAcceptor) accept() {

	self.SetRunning(true)

	recvBuff := make([]byte, MaxUDPRecvBuffer)

	for {

		n, remoteAddr, err := self.conn.ReadFromUDP(recvBuff)
		if err != nil {
			break
		}

		if n > 0 {

			ses := self.allocSession(remoteAddr)

			if self.CaptureIOPanic() {
				self.protectedRecvPacket(ses, recvBuff[:n])
			} else {
				ses.Recv(recvBuff[:n])
			}

		}

	}

	self.SetRunning(false)

}

func (self *udpAcceptor) allocSession(addr *net.UDPAddr) *udpSession {

	var ses *udpSession

	if self.sesQueue.Count() > 0 {
		ses = self.sesQueue.Peek().(*udpSession)

		// 这个session还能用，需要重新new
		if ses.IsAlive() {
			ses = nil
		} else {
			// 可以复用
			ses = self.sesQueue.Dequeue().(*udpSession)
		}

	}

	if ses == nil {
		ses = &udpSession{}
		self.sesQueue.Enqueue(ses)
	}

	ses.timeOutTick = time.Now().Add(self.sesTimeout)
	ses.conn = self.conn
	ses.remote = addr
	ses.pInterface = self
	ses.CoreProcBundle = &self.CoreProcBundle

	return ses
}

func (self *udpAcceptor) SetSessionTTL(dur time.Duration) {
	self.sesTimeout = dur
}

func (self *udpAcceptor) Stop() {

	if self.conn != nil {
		self.conn.Close()
	}

	// TODO 等待accept线程结束
	self.SetRunning(false)
}

func (self *udpAcceptor) TypeName() string {
	return "udp.Acceptor"
}

func init() {

	peer.RegisterPeerCreator(func() cellnet.Peer {
		p := &udpAcceptor{
			sesQueue:   util.NewQueue(64),
			sesTimeout: time.Second,
		}

		return p
	})
}
<|endoftext|>