<commit_before>package client

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

	"../api"
	"../job"

	"github.com/gorilla/mux"
	"github.com/stretchr/testify/assert"
	"testing"
)

func getNewTestServer() *httptest.Server {
	r := mux.NewRouter()
	api.SetupApiRoutes(r)
	return httptest.NewServer(r)
}

func cleanUp() {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	jobs, err := kc.GetAllJobs()
	if len(jobs) == 0 {
		return
	}
	if err != nil {
		fmt.Printf("wtf")
	}
	for _, job := range jobs {
		kc.DeleteJob(job.Id)
	}
}

func generateNewJobMap() map[string]string {
	scheduleTime := time.Now().Add(time.Minute * 5)
	repeat := 1
	delay := "P1DT10M10S"
	parsedTime := scheduleTime.Format(time.RFC3339)
	scheduleStr := fmt.Sprintf("R%d/%s/%s", repeat, parsedTime, delay)

	return map[string]string{
		"schedule": scheduleStr,
		"name":     "mock_job",
		"command":  "bash -c 'date'",
		"owner":    "aj@ajvb.me",
	}
}

func TestCreateGetDeleteJob(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	id, err := kc.CreateJob(j)
	assert.NoError(t, err)
	assert.NotEqual(t, id, "")

	respJob, err := kc.GetJob(id)
	assert.NoError(t, err)
	assert.Equal(t, j["schedule"], respJob.Schedule)
	assert.Equal(t, j["name"], respJob.Name)
	assert.Equal(t, j["command"], respJob.Command)
	assert.Equal(t, j["owner"], respJob.Owner)

	ok, err := kc.DeleteJob(id)
	assert.NoError(t, err)
	assert.True(t, ok)

	cleanUp()
}

func TestCreateJobError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	j["schedule"] = "bbbbbbbbbbbbbbb"

	id, err := kc.CreateJob(j)
	assert.Error(t, err)
	assert.Equal(t, id, "")

	cleanUp()
}

func TestGetJobError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	respJob, err := kc.GetJob("id-that-doesnt-exist")
	assert.Error(t, err)
	assert.Nil(t, respJob)

	cleanUp()
}

func TestDeleteJobError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	ok, err := kc.DeleteJob("id-that-doesnt-exist")
	assert.Error(t, err)
	assert.False(t, ok)

	cleanUp()
}

func TestGetAllJobs(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()
	job.AllJobs.Jobs = make(map[string]*job.Job, 0)

	id, err := kc.CreateJob(j)
	assert.NoError(t, err)

	jobs, err := kc.GetAllJobs()
	assert.NoError(t, err)
	assert.Equal(t, 1, len(jobs))
	assert.Equal(t, j["schedule"], jobs[id].Schedule)
	assert.Equal(t, j["name"], jobs[id].Name)
	assert.Equal(t, j["command"], jobs[id].Command)
	assert.Equal(t, j["owner"], jobs[id].Owner)

	cleanUp()
}

func TestGetAllJobsNoJobsExist(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	jobs, err := kc.GetAllJobs()
	fmt.Printf("JOBS: %#v", jobs)
	assert.NoError(t, err)
	assert.Equal(t, 0, len(jobs))

	cleanUp()
}

func TestDeleteJob(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	id, err := kc.CreateJob(j)
	assert.NoError(t, err)

	ok, err := kc.DeleteJob(id)
	assert.NoError(t, err)
	assert.True(t, ok)

	respJob, err := kc.GetJob(id)
	assert.Nil(t, respJob)

	cleanUp()
}

func TestGetJobStats(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	// Create the job
	id, err := kc.CreateJob(j)
	assert.NoError(t, err)
	// Start the job
	ok, err := kc.StartJob(id)
	now := time.Now()
	assert.NoError(t, err)
	assert.True(t, ok)
	// Wait let the job run
	time.Sleep(time.Second * 1)

	stats, err := kc.GetJobStats(id)
	assert.NoError(t, err)
	assert.Equal(t, id, stats[0].JobId)
	assert.Equal(t, uint(0), stats[0].NumberOfRetries)
	assert.True(t, stats[0].Success)
	assert.True(t, stats[0].ExecutionDuration != time.Duration(0))
	assert.WithinDuration(t, now, stats[0].RanAt, time.Second)

	cleanUp()
}

func TestGetJobStatsError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	stats, err := kc.GetJobStats("not-an-actual-id")
	assert.Error(t, err)
	assert.Nil(t, stats)
}

func TestStartJob(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	id, err := kc.CreateJob(j)
	assert.NoError(t, err)
	assert.NotEqual(t, id, "")

	now := time.Now()
	ok, err := kc.StartJob(id)
	assert.NoError(t, err)
	assert.True(t, ok)

	// Wait let the job run
	time.Sleep(time.Second * 1)

	respJob, err := kc.GetJob(id)
	assert.NoError(t, err)
	assert.Equal(t, uint(1), respJob.SuccessCount)
	assert.WithinDuration(t, now, respJob.LastSuccess, time.Second*2)
	assert.WithinDuration(t, now, respJob.LastAttemptedRun, time.Second*2)

	cleanUp()
}

func TestStartJobError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	ok, err := kc.StartJob("not-an-actual-id")
	assert.Error(t, err)
	assert.False(t, ok)
}

func TestGetKalaStats(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	job.AllJobs.Jobs = make(map[string]*job.Job, 0)

	for i := 0; i < 5; i++ {
		// Generate new job
		j := generateNewJobMap()

		id, err := kc.CreateJob(j)
		assert.NoError(t, err)
		assert.NotEqual(t, id, "")

		ok, err := kc.StartJob(id)
		assert.NoError(t, err)
		assert.True(t, ok)
	}
	time.Sleep(time.Second * 3)

	stats, err := kc.GetKalaStats()
	assert.NoError(t, err)

	assert.Equal(t, 5, stats.ActiveJobs)
	assert.Equal(t, 0, stats.DisabledJobs)
	assert.Equal(t, 5, stats.Jobs)

	assert.Equal(t, uint(0), stats.ErrorCount)
	assert.Equal(t, uint(5), stats.SuccessCount)

	cleanUp()
}
<commit_msg>Fixed cleanup func in client_test.go<commit_after>package client

import (
	"fmt"
	"net/http/httptest"
	"os"
	"time"

	"../api"
	"../job"

	"github.com/gorilla/mux"
	"github.com/stretchr/testify/assert"
	"testing"
)

func getNewTestServer() *httptest.Server {
	r := mux.NewRouter()
	api.SetupApiRoutes(r)
	return httptest.NewServer(r)
}

func cleanUp() {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	jobs, err := kc.GetAllJobs()
	if len(jobs) == 0 {
		return
	}
	if err != nil {
		fmt.Printf("Problem running clean up (can't get all jobs from the server)")
		os.Exit(1)
	}
	for _, job := range jobs {
		kc.DeleteJob(job.Id)
	}
}

func generateNewJobMap() map[string]string {
	scheduleTime := time.Now().Add(time.Minute * 5)
	repeat := 1
	delay := "P1DT10M10S"
	parsedTime := scheduleTime.Format(time.RFC3339)
	scheduleStr := fmt.Sprintf("R%d/%s/%s", repeat, parsedTime, delay)

	return map[string]string{
		"schedule": scheduleStr,
		"name":     "mock_job",
		"command":  "bash -c 'date'",
		"owner":    "aj@ajvb.me",
	}
}

func TestCreateGetDeleteJob(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	id, err := kc.CreateJob(j)
	assert.NoError(t, err)
	assert.NotEqual(t, id, "")

	respJob, err := kc.GetJob(id)
	assert.NoError(t, err)
	assert.Equal(t, j["schedule"], respJob.Schedule)
	assert.Equal(t, j["name"], respJob.Name)
	assert.Equal(t, j["command"], respJob.Command)
	assert.Equal(t, j["owner"], respJob.Owner)

	ok, err := kc.DeleteJob(id)
	assert.NoError(t, err)
	assert.True(t, ok)

	cleanUp()
}

func TestCreateJobError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	j["schedule"] = "bbbbbbbbbbbbbbb"

	id, err := kc.CreateJob(j)
	assert.Error(t, err)
	assert.Equal(t, id, "")

	cleanUp()
}

func TestGetJobError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	respJob, err := kc.GetJob("id-that-doesnt-exist")
	assert.Error(t, err)
	assert.Nil(t, respJob)

	cleanUp()
}

func TestDeleteJobError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	ok, err := kc.DeleteJob("id-that-doesnt-exist")
	assert.Error(t, err)
	assert.False(t, ok)

	cleanUp()
}

func TestGetAllJobs(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()
	job.AllJobs.Jobs = make(map[string]*job.Job, 0)

	id, err := kc.CreateJob(j)
	assert.NoError(t, err)

	jobs, err := kc.GetAllJobs()
	assert.NoError(t, err)
	assert.Equal(t, 1, len(jobs))
	assert.Equal(t, j["schedule"], jobs[id].Schedule)
	assert.Equal(t, j["name"], jobs[id].Name)
	assert.Equal(t, j["command"], jobs[id].Command)
	assert.Equal(t, j["owner"], jobs[id].Owner)

	cleanUp()
}

func TestGetAllJobsNoJobsExist(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	jobs, err := kc.GetAllJobs()
	fmt.Printf("JOBS: %#v", jobs)
	assert.NoError(t, err)
	assert.Equal(t, 0, len(jobs))

	cleanUp()
}

func TestDeleteJob(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	id, err := kc.CreateJob(j)
	assert.NoError(t, err)

	ok, err := kc.DeleteJob(id)
	assert.NoError(t, err)
	assert.True(t, ok)

	respJob, err := kc.GetJob(id)
	assert.Nil(t, respJob)

	cleanUp()
}

func TestGetJobStats(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	// Create the job
	id, err := kc.CreateJob(j)
	assert.NoError(t, err)
	// Start the job
	ok, err := kc.StartJob(id)
	now := time.Now()
	assert.NoError(t, err)
	assert.True(t, ok)
	// Wait let the job run
	time.Sleep(time.Second * 1)

	stats, err := kc.GetJobStats(id)
	assert.NoError(t, err)
	assert.Equal(t, id, stats[0].JobId)
	assert.Equal(t, uint(0), stats[0].NumberOfRetries)
	assert.True(t, stats[0].Success)
	assert.True(t, stats[0].ExecutionDuration != time.Duration(0))
	assert.WithinDuration(t, now, stats[0].RanAt, time.Second)

	cleanUp()
}

func TestGetJobStatsError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	stats, err := kc.GetJobStats("not-an-actual-id")
	assert.Error(t, err)
	assert.Nil(t, stats)
}

func TestStartJob(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	j := generateNewJobMap()

	id, err := kc.CreateJob(j)
	assert.NoError(t, err)
	assert.NotEqual(t, id, "")

	now := time.Now()
	ok, err := kc.StartJob(id)
	assert.NoError(t, err)
	assert.True(t, ok)

	// Wait let the job run
	time.Sleep(time.Second * 1)

	respJob, err := kc.GetJob(id)
	assert.NoError(t, err)
	assert.Equal(t, uint(1), respJob.SuccessCount)
	assert.WithinDuration(t, now, respJob.LastSuccess, time.Second*2)
	assert.WithinDuration(t, now, respJob.LastAttemptedRun, time.Second*2)

	cleanUp()
}

func TestStartJobError(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)

	ok, err := kc.StartJob("not-an-actual-id")
	assert.Error(t, err)
	assert.False(t, ok)
}

func TestGetKalaStats(t *testing.T) {
	ts := getNewTestServer()
	defer ts.Close()
	kc := NewKalaClient(ts.URL)
	job.AllJobs.Jobs = make(map[string]*job.Job, 0)

	for i := 0; i < 5; i++ {
		// Generate new job
		j := generateNewJobMap()

		id, err := kc.CreateJob(j)
		assert.NoError(t, err)
		assert.NotEqual(t, id, "")

		ok, err := kc.StartJob(id)
		assert.NoError(t, err)
		assert.True(t, ok)
	}
	time.Sleep(time.Second * 3)

	stats, err := kc.GetKalaStats()
	assert.NoError(t, err)

	assert.Equal(t, 5, stats.ActiveJobs)
	assert.Equal(t, 0, stats.DisabledJobs)
	assert.Equal(t, 5, stats.Jobs)

	assert.Equal(t, uint(0), stats.ErrorCount)
	assert.Equal(t, uint(5), stats.SuccessCount)

	cleanUp()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	. "github.com/ying32/govcl/vcl"
	. "github.com/ying32/govcl/vcl/rtl"
	. "github.com/ying32/govcl/vcl/types"
	"github.com/ying32/govcl/vcl/types/colors"
)

type TPlayListItem struct {
	Caption  string // 歌曲名
	Singer   string // 歌手
	Length   int32  // 歌曲长度
	Lyric    string // 歌词文件
	FileName string // 歌曲文件名
}

type TPlayControl struct {
	*TDrawGrid
	datas          []TPlayListItem
	focusedColor   TColor
	playColor      TColor
	mouseMoveColor TColor
	mouseMoveIndex int32
	playingIndex   int32

	// 导出，测试用
	SingerPic *TBitmap

	OnSelect func(sender IObject, item TPlayListItem)
}

func NewPlayControl(owner IComponent) *TPlayControl {
	m := new(TPlayControl)
	m.TDrawGrid = NewDrawGrid(owner)

	m.TDrawGrid.SetDefaultDrawing(false)
	m.TDrawGrid.SetDefaultRowHeight(24)
	if LcLLoaded() {
		m.TDrawGrid.SetOptions(Include(0, GoLzRangeSelect, GoLzRowSelect))
	} else {
		m.TDrawGrid.SetOptions(Include(0, GoRangeSelect, GoRowSelect))
	}
	m.TDrawGrid.SetRowCount(0)
	m.TDrawGrid.SetColCount(4)
	m.TDrawGrid.SetFixedRows(0)
	m.TDrawGrid.SetFixedCols(0)
	m.TDrawGrid.SetGridLineWidth(0)
	m.TDrawGrid.SetBorderStyle(BsNone)
	m.TDrawGrid.SetScrollBars(SsVertical)

	m.TDrawGrid.SetWidth(536)
	m.TDrawGrid.SetHeight(397)

	m.TDrawGrid.SetDrawingStyle(GdsThemed)
	// 加载时取消第一行永远被选中
	m.TDrawGrid.SetSelection(TGridRect{-24, -24, -24, -24})

	m.TDrawGrid.SetColWidths(0, 60)
	m.TDrawGrid.SetColWidths(1, 230)
	m.TDrawGrid.SetColWidths(2, 100)
	m.TDrawGrid.SetColWidths(3, 80)

	//m.TDrawGrid.SetColor(0x39302c) //0x00EDEEF9)
	m.TDrawGrid.SetDoubleBuffered(true)

	m.focusedColor = colors.ClMoneyGreen //0x20302c //0x00C8CBEB
	m.playColor = m.focusedColor + 12
	m.mouseMoveColor = m.focusedColor - 12

	m.mouseMoveIndex = -1
	m.playingIndex = -1

	m.TDrawGrid.SetOnDblClick(m.onDblClick)
	m.TDrawGrid.SetOnMouseMove(m.onMouseMove)
	//m.TDrawGrid.SetOnMouseDown(m.onMouseDown)
	m.TDrawGrid.SetOnDrawCell(m.onDrawCell)
	//m.TDrawGrid.SetOnMouseEnter(m.onMouseEnter)
	m.TDrawGrid.SetOnMouseLeave(m.onMouseLeave)

	return m
}

func (p *TPlayControl) Add(item TPlayListItem) int32 {
	p.datas = append(p.datas, item)
	p.SetRowCount(int32(len(p.datas)))
	return int32(len(p.datas)) - 1
}

func (p *TPlayControl) Next() {
	if int(p.playingIndex) < len(p.datas)-1 {
		p.playingIndex++
		p.SetRow(p.playingIndex)
		p.Invalidate()
		if p.OnSelect != nil {
			p.OnSelect(p, p.datas[int(p.playingIndex)])
		}
	}
}

func (p *TPlayControl) Prev() {
	if int(p.playingIndex) > 0 {
		p.playingIndex--
		p.SetRow(p.playingIndex)
		p.Invalidate()
		if p.OnSelect != nil {
			p.OnSelect(p, p.datas[int(p.playingIndex)])
		}
	}
}

func (p *TPlayControl) onDrawCell(sender IObject, aCol, aRow int32, rect TRect, state TGridDrawState) {

	if len(p.datas) > 0 {
		canvas := p.Canvas()
		if aRow < int32(len(p.datas)) {
			drawFlags := Include(0, TfVerticalCenter, TfSingleLine, TfEndEllipsis)
			item := p.datas[int(aRow)]
			if p.mouseMoveIndex == aRow && p.playingIndex != aRow && !InSets(state, GdFocused) && !InSets(state, GdSelected) {
				canvas.Brush().SetColor(p.focusedColor - 12)
			} else if InSets(state, GdFocused) || InSets(state, GdSelected) {
				canvas.Brush().SetColor(p.focusedColor)
			} else {
				canvas.Brush().SetColor(p.Color())
			}

			if p.playingIndex == aRow {
				canvas.Brush().SetColor(p.focusedColor + 12)
				p.SetRowHeights(aRow, 50)
				if p.RowHeights(aRow) < 50 {
					p.ScrollBy(0, aRow+2)
				}
			} else {
				p.SetRowHeights(aRow, 24)
			}

			canvas.FillRect(rect)

			r := p.CellRect(aCol, aRow)
			switch aCol {
			case 0:

				if aRow == p.playingIndex {
					if !p.SingerPic.Empty() {
						r.Left += 1
						r.Top += +1
						r.Bottom -= -1
						canvas.StretchDraw(r, p.SingerPic)
					}
				} else {
					r.Inflate(-10, 0)
					s := fmt.Sprintf("%d.", aRow+1)
					canvas.TextRect3(&r, s, Include(drawFlags, TfRight))
				}

			case 1:
				if aRow == p.playingIndex {
					r.Inflate(-10, 0)
					canvas.Font().SetSize(11)
					canvas.Font().SetStyle(Include(0, FsBold))
					canvas.TextRect3(&r, item.Caption, drawFlags)
				} else {
					r.Inflate(-5, 0)
					canvas.TextRect3(&r, item.Caption, drawFlags)
				}
				canvas.Font().SetSize(8)
				canvas.Font().SetStyle(0)
			case 2:
				r.Inflate(-5, 0)
				canvas.TextRect3(&r, item.Singer, drawFlags)
			case 3:
				r.Inflate(-5, 0)
				canvas.TextRect3(&r, p.mediaLengthToTimeStr(item.Length), Include(drawFlags, TfRight))
			}
		}

	} else {
		p.Canvas().Brush().SetColor(p.Color())
		p.Canvas().FillRect(rect)
	}
}

func (p *TPlayControl) onMouseMove(sender IObject, shift TShiftState, x, y int32) {
	if !p.Enabled() {
		return
	}
	var col, row int32
	p.MouseToCell(x, y, &col, &row)
	p.mouseMoveIndex = row
	if p.mouseMoveIndex == -1 {
		return
	}
	p.Invalidate()
}

func (p *TPlayControl) onMouseDown(sender IObject, button TMouseButton, shift TShiftState, x, y int32) {

}

func (p *TPlayControl) onDblClick(sender IObject) {
	if !p.Enabled() {
		return
	}
	row := p.Row()
	if row == -1 {
		return
	}
	p.playingIndex = row
	p.Invalidate()
	if p.OnSelect != nil {
		p.OnSelect(p, p.datas[row])
	}
}

func (p *TPlayControl) onMouseEnter(sender IObject) {

}

func (p *TPlayControl) onMouseLeave(sender IObject) {
	if !p.Enabled() {
		return
	}
	p.mouseMoveIndex = -1
	p.Invalidate()
}

func (p *TPlayControl) mediaLengthToTimeStr(alen int32) string {
	return fmt.Sprintf("%.2d:%.2d", int(float32(alen)/1000.0)/60, int(float32(alen)/1000.0)%60)
}
<commit_msg>Update<commit_after>package main

import (
	"fmt"

	. "github.com/ying32/govcl/vcl"
	. "github.com/ying32/govcl/vcl/rtl"
	. "github.com/ying32/govcl/vcl/types"
	"github.com/ying32/govcl/vcl/types/colors"
)

type TPlayListItem struct {
	Caption  string // 歌曲名
	Singer   string // 歌手
	Length   int32  // 歌曲长度
	Lyric    string // 歌词文件
	FileName string // 歌曲文件名
}

type TPlayControl struct {
	*TDrawGrid
	datas          []TPlayListItem
	focusedColor   TColor
	playColor      TColor
	mouseMoveColor TColor
	mouseMoveIndex int32
	playingIndex   int32

	// 导出，测试用
	SingerPic *TBitmap

	OnSelect func(sender IObject, item TPlayListItem)
}

func NewPlayControl(owner IComponent) *TPlayControl {
	m := new(TPlayControl)
	m.TDrawGrid = NewDrawGrid(owner)

	m.TDrawGrid.SetDefaultDrawing(false)
	m.TDrawGrid.SetDefaultRowHeight(24)
	if LcLLoaded() {
		m.TDrawGrid.SetOptions(Include(0, GoLzRangeSelect, GoLzRowSelect))
	} else {
		m.TDrawGrid.SetOptions(Include(0, GoRangeSelect, GoRowSelect))
	}
	m.TDrawGrid.SetRowCount(0)
	m.TDrawGrid.SetColCount(4)
	m.TDrawGrid.SetFixedRows(0)
	m.TDrawGrid.SetFixedCols(0)
	m.TDrawGrid.SetGridLineWidth(0)
	m.TDrawGrid.SetBorderStyle(BsNone)
	m.TDrawGrid.SetScrollBars(SsVertical)

	m.TDrawGrid.SetWidth(536)
	m.TDrawGrid.SetHeight(397)

	m.TDrawGrid.SetDrawingStyle(GdsThemed)
	// 加载时取消第一行永远被选中
	m.TDrawGrid.SetSelection(TGridRect{-24, -24, -24, -24})

	m.TDrawGrid.SetColWidths(0, 60)
	if LcLLoaded() {
		m.TDrawGrid.SetColWidths(1, 215)
	} else {
		m.TDrawGrid.SetColWidths(1, 230)
	}

	m.TDrawGrid.SetColWidths(2, 100)
	m.TDrawGrid.SetColWidths(3, 80)

	//m.TDrawGrid.SetColor(0x39302c) //0x00EDEEF9)
	m.TDrawGrid.SetDoubleBuffered(true)

	m.focusedColor = colors.ClMoneyGreen //0x20302c //0x00C8CBEB
	m.playColor = m.focusedColor + 12
	m.mouseMoveColor = m.focusedColor - 12

	m.mouseMoveIndex = -1
	m.playingIndex = -1

	m.TDrawGrid.SetOnDblClick(m.onDblClick)
	m.TDrawGrid.SetOnMouseMove(m.onMouseMove)
	//m.TDrawGrid.SetOnMouseDown(m.onMouseDown)
	m.TDrawGrid.SetOnDrawCell(m.onDrawCell)
	//m.TDrawGrid.SetOnMouseEnter(m.onMouseEnter)
	m.TDrawGrid.SetOnMouseLeave(m.onMouseLeave)

	return m
}

func (p *TPlayControl) Add(item TPlayListItem) int32 {
	p.datas = append(p.datas, item)
	p.SetRowCount(int32(len(p.datas)))
	return int32(len(p.datas)) - 1
}

func (p *TPlayControl) Next() {
	if int(p.playingIndex) < len(p.datas)-1 {
		p.playingIndex++
		p.SetRow(p.playingIndex)
		p.Invalidate()
		if p.OnSelect != nil {
			p.OnSelect(p, p.datas[int(p.playingIndex)])
		}
	}
}

func (p *TPlayControl) Prev() {
	if int(p.playingIndex) > 0 {
		p.playingIndex--
		p.SetRow(p.playingIndex)
		p.Invalidate()
		if p.OnSelect != nil {
			p.OnSelect(p, p.datas[int(p.playingIndex)])
		}
	}
}

func (p *TPlayControl) onDrawCell(sender IObject, aCol, aRow int32, rect TRect, state TGridDrawState) {

	if len(p.datas) > 0 {
		canvas := p.Canvas()
		if aRow < int32(len(p.datas)) {
			drawFlags := Include(0, TfVerticalCenter, TfSingleLine, TfEndEllipsis)
			item := p.datas[int(aRow)]
			if p.mouseMoveIndex == aRow && p.playingIndex != aRow && !InSets(state, GdFocused) && !InSets(state, GdSelected) {
				canvas.Brush().SetColor(p.focusedColor - 12)
			} else if InSets(state, GdFocused) || InSets(state, GdSelected) {
				canvas.Brush().SetColor(p.focusedColor)
			} else {
				canvas.Brush().SetColor(p.Color())
			}

			if p.playingIndex == aRow {
				canvas.Brush().SetColor(p.focusedColor + 12)
				p.SetRowHeights(aRow, 50)
				if p.RowHeights(aRow) < 50 {
					p.ScrollBy(0, aRow+2)
				}
			} else {
				p.SetRowHeights(aRow, 24)
			}

			canvas.FillRect(rect)

			r := p.CellRect(aCol, aRow)
			switch aCol {
			case 0:

				if aRow == p.playingIndex {
					if !p.SingerPic.Empty() {
						r.Left += 1
						r.Top += +1
						r.Bottom -= -1
						canvas.StretchDraw(r, p.SingerPic)
					}
				} else {
					r.Inflate(-10, 0)
					s := fmt.Sprintf("%d.", aRow+1)
					canvas.TextRect3(&r, s, Include(drawFlags, TfRight))
				}

			case 1:
				if aRow == p.playingIndex {
					r.Inflate(-10, 0)
					canvas.Font().SetSize(11)
					canvas.Font().SetStyle(Include(0, FsBold))
					canvas.TextRect3(&r, item.Caption, drawFlags)
				} else {
					r.Inflate(-5, 0)
					canvas.TextRect3(&r, item.Caption, drawFlags)
				}
				canvas.Font().SetSize(8)
				canvas.Font().SetStyle(0)
			case 2:
				r.Inflate(-5, 0)
				canvas.TextRect3(&r, item.Singer, drawFlags)
			case 3:
				r.Inflate(-5, 0)
				canvas.TextRect3(&r, p.mediaLengthToTimeStr(item.Length), Include(drawFlags, TfRight))
			}
		}

	} else {
		p.Canvas().Brush().SetColor(p.Color())
		p.Canvas().FillRect(rect)
	}
}

func (p *TPlayControl) onMouseMove(sender IObject, shift TShiftState, x, y int32) {
	if !p.Enabled() {
		return
	}
	var col, row int32
	p.MouseToCell(x, y, &col, &row)
	p.mouseMoveIndex = row
	if p.mouseMoveIndex == -1 {
		return
	}
	p.Invalidate()
}

func (p *TPlayControl) onMouseDown(sender IObject, button TMouseButton, shift TShiftState, x, y int32) {

}

func (p *TPlayControl) onDblClick(sender IObject) {
	if !p.Enabled() {
		return
	}
	row := p.Row()
	if row == -1 {
		return
	}
	p.playingIndex = row
	p.Invalidate()
	if p.OnSelect != nil {
		p.OnSelect(p, p.datas[row])
	}
}

func (p *TPlayControl) onMouseEnter(sender IObject) {

}

func (p *TPlayControl) onMouseLeave(sender IObject) {
	if !p.Enabled() {
		return
	}
	p.mouseMoveIndex = -1
	p.Invalidate()
}

func (p *TPlayControl) mediaLengthToTimeStr(alen int32) string {
	return fmt.Sprintf("%.2d:%.2d", int(float32(alen)/1000.0)/60, int(float32(alen)/1000.0)%60)
}
<|endoftext|>


<commit_before>/*
 Copyright 2016 Padduck, LLC

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

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

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

package install

import (
	"io/ioutil"
	"os/exec"
	"github.com/pufferpanel/pufferd/logging"
	"syscall"
	"github.com/pufferpanel/pufferd/utils"
)

const SYSTEMD = `
[Unit]
Description=pufferd daemon service

[Service]
Type=simple
WorkingDirectory=/srv/pufferd
ExecStart=/srv/pufferd/pufferd --config ${configpath}
User=pufferd
Group=pufferd

[Install]
WantedBy=multi-user.target
`

func InstallService(configPath string) {
	cmd := exec.Command("useradd", "--system", "--no-create-home", "--user-group", "--home", "/srv/pufferd/data", "pufferd")

	err := cmd.Run()
	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
				if status.ExitStatus() != 9 {
					logging.Error("Error creating pufferd user", err)
					return
				}
			} else {
				logging.Error("Error creating pufferd user", err)
				return
			}
		} else {
			logging.Error("Error creating pufferd user", err)
			return
		}
	}

	updates := make(map[string]interface{})
	updates["configpath"] = configPath

	err = ioutil.WriteFile("/etc/systemd/system/pufferd.service", []byte(utils.ReplaceTokens(SYSTEMD, updates)), 0664)
	if err != nil {
		logging.Error("Cannot write systemd file, will not install service", err)
		return
	}
	cmd = exec.Command("systemctl", "enable", "pufferd")
	output, err := cmd.CombinedOutput()
	if err != nil {
		logging.Error("Error enabling pufferd service", err)
		logging.Error(string(output))
		return
	}
	logging.Info(string(output))

	logging.Info("Service may be started with: systemctl start pufferd")
}
<commit_msg>Remove home folder specification<commit_after>/*
 Copyright 2016 Padduck, LLC

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

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

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

package install

import (
	"io/ioutil"
	"os/exec"
	"github.com/pufferpanel/pufferd/logging"
	"syscall"
	"github.com/pufferpanel/pufferd/utils"
)

const SYSTEMD = `
[Unit]
Description=pufferd daemon service

[Service]
Type=simple
WorkingDirectory=/srv/pufferd
ExecStart=/srv/pufferd/pufferd --config ${configpath}
User=pufferd
Group=pufferd

[Install]
WantedBy=multi-user.target
`

func InstallService(configPath string) {
	cmd := exec.Command("useradd", "--system", "--no-create-home", "--user-group", "pufferd")

	err := cmd.Run()
	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
				if status.ExitStatus() != 9 {
					logging.Error("Error creating pufferd user", err)
					return
				}
			} else {
				logging.Error("Error creating pufferd user", err)
				return
			}
		} else {
			logging.Error("Error creating pufferd user", err)
			return
		}
	}

	updates := make(map[string]interface{})
	updates["configpath"] = configPath

	err = ioutil.WriteFile("/etc/systemd/system/pufferd.service", []byte(utils.ReplaceTokens(SYSTEMD, updates)), 0664)
	if err != nil {
		logging.Error("Cannot write systemd file, will not install service", err)
		return
	}

	cmd = exec.Command("systemctl", "enable", "pufferd")
	output, err := cmd.CombinedOutput()
	if err != nil {
		logging.Error("Error enabling pufferd service", err)
		logging.Error(string(output))
		return
	}
	logging.Info(string(output))

	logging.Info("Service may be started with: systemctl start pufferd")
}
<|endoftext|>


<commit_before>package jsgettext

import (
	"encoding/json"

	"github.com/gopherjs/gopherjs/js"
)

type msgIdStrPairs map[string]string
type localesMsg map[string]msgIdStrPairs

var msg = localesMsg{}
var document = js.Global.Get("document")

func SetupTranslationMapping(jsonBlob string) (err error) {
	err := json.Unmarshal([]byte(jsonBlob), &msg)
	return
}

// Gettext translates the given string to the language specified by locale.
func Gettext(locale, str string) string {
	if val, ok := msg[locale]; ok {
		if val2, ok2 := val[str]; ok2 {
			return val2
		}
	}
	return str
}

// TranslateDocument searches all element with [data-default-string] attribute
// in the document, translate the string with given locale, and set the
// textContent of the element to the translated string.
func TranslateDocument(locale string) {
	nodeList := document.Call("querySelectorAll", "[data-default-string]")
	length := nodeList.Get("length").Int()
	for i := 0; i < length; i++ {
		element := nodeList.Call("item", i)
		str := element.Get("dataset").Get("defaultString").String()
		element.Set("textContent", Gettext(locale, str))
	}
}
<commit_msg>add godoc<commit_after>// Package jsgettext provides frontend methods to translate html document in the
// browser on the fly, similar to GNU gettext function. This package is written
// in Go and compiled to JavaScript via GopherJS.
package jsgettext

import (
	"encoding/json"

	"github.com/gopherjs/gopherjs/js"
)

type msgIdStrPairs map[string]string
type localesMsg map[string]msgIdStrPairs

var msg = localesMsg{}
var document = js.Global.Get("document")

func SetupTranslationMapping(jsonBlob string) (err error) {
	err := json.Unmarshal([]byte(jsonBlob), &msg)
	return
}

// Gettext translates the given string to the language specified by locale.
func Gettext(locale, str string) string {
	if val, ok := msg[locale]; ok {
		if val2, ok2 := val[str]; ok2 {
			return val2
		}
	}
	return str
}

// TranslateDocument searches all element with [data-default-string] attribute
// in the document, translate the string with given locale, and set the
// textContent of the element to the translated string.
func TranslateDocument(locale string) {
	nodeList := document.Call("querySelectorAll", "[data-default-string]")
	length := nodeList.Get("length").Int()
	for i := 0; i < length; i++ {
		element := nodeList.Call("item", i)
		str := element.Get("dataset").Get("defaultString").String()
		element.Set("textContent", Gettext(locale, str))
	}
}
<|endoftext|>


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

package user

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/Unknwon/com"

	"github.com/go-gitea/gitea/models"
	"github.com/go-gitea/gitea/modules/base"
	"github.com/go-gitea/gitea/modules/log"
	"github.com/go-gitea/gitea/modules/middleware"
	"github.com/go-gitea/gitea/modules/setting"
)

const (
	DASHBOARD base.TplName = "user/dashboard/dashboard"
	PULLS     base.TplName = "user/dashboard/pulls"
	ISSUES    base.TplName = "user/issues"
	STARS     base.TplName = "user/stars"
	PROFILE   base.TplName = "user/profile"
)

func Dashboard(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("dashboard")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsNews"] = true

	var ctxUser *models.User
	// Check context type.
	orgName := ctx.Params(":org")
	if len(orgName) > 0 {
		// Organization.
		org, err := models.GetUserByName(orgName)
		if err != nil {
			if err == models.ErrUserNotExist {
				ctx.Handle(404, "GetUserByName", err)
			} else {
				ctx.Handle(500, "GetUserByName", err)
			}
			return
		}
		ctxUser = org
	} else {
		// Normal user.
		ctxUser = ctx.User
		collaborates, err := ctx.User.GetAccessibleRepositories()
		if err != nil {
			ctx.Handle(500, "GetAccessibleRepositories", err)
			return
		}

		repositories := make([]*models.Repository, 0, len(collaborates))
		for repo := range collaborates {
			repositories = append(repositories, repo)
		}

		ctx.Data["CollaborateCount"] = len(repositories)
		ctx.Data["CollaborativeRepos"] = repositories
	}
	ctx.Data["ContextUser"] = ctxUser

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["Orgs"] = ctx.User.Orgs

	repos, err := models.GetRepositories(ctxUser.Id, true)
	if err != nil {
		ctx.Handle(500, "GetRepositories", err)
		return
	}
	ctx.Data["Repos"] = repos

	// Get mirror repositories.
	mirrors := make([]*models.Repository, 0, len(repos)/2)
	for _, repo := range repos {
		if repo.IsMirror {
			if err = repo.GetMirror(); err != nil {
				ctx.Handle(500, "GetMirror: "+repo.Name, err)
				return
			}
			mirrors = append(mirrors, repo)
		}
	}
	ctx.Data["MirrorCount"] = len(mirrors)
	ctx.Data["Mirrors"] = mirrors

	// Get feeds.
	actions, err := models.GetFeeds(ctxUser.Id, 0, false)
	if err != nil {
		ctx.Handle(500, "GetFeeds", err)
		return
	}

	// Check access of private repositories.
	feeds := make([]*models.Action, 0, len(actions))
	for _, act := range actions {
		if act.IsPrivate {
			// This prevents having to retrieve the repository for each action
			repo := &models.Repository{Id: act.RepoID, IsPrivate: true}
			if act.RepoUserName != ctx.User.LowerName {
				if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
					continue
				}
			}

		}
		// FIXME: cache results?
		u, err := models.GetUserByName(act.ActUserName)
		if err != nil {
			if err == models.ErrUserNotExist {
				continue
			}
			ctx.Handle(500, "GetUserByName", err)
			return
		}
		act.ActAvatar = u.AvatarLink()
		feeds = append(feeds, act)
	}
	ctx.Data["Feeds"] = feeds
	ctx.HTML(200, DASHBOARD)
}

func Pulls(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("pull_requests")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsPulls"] = true

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["ContextUser"] = ctx.User

	ctx.HTML(200, PULLS)
}

func ShowSSHKeys(ctx *middleware.Context, uid int64) {
	keys, err := models.ListPublicKeys(uid)
	if err != nil {
		ctx.Handle(500, "ListPublicKeys", err)
		return
	}

	var buf bytes.Buffer
	for i := range keys {
		buf.WriteString(keys[i].OmitEmail())
	}
	ctx.RenderData(200, buf.Bytes())
}

func Profile(ctx *middleware.Context) {
	ctx.Data["Title"] = "Profile"
	ctx.Data["PageIsUserProfile"] = true

	uname := ctx.Params(":username")
	// Special handle for FireFox requests favicon.ico.
	if uname == "favicon.ico" {
		ctx.Redirect(setting.AppSubUrl + "/img/favicon.png")
		return
	}

	isShowKeys := false
	if strings.HasSuffix(uname, ".keys") {
		isShowKeys = true
		uname = strings.TrimSuffix(uname, ".keys")
	}

	u, err := models.GetUserByName(uname)
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "GetUserByName", err)
		} else {
			ctx.Handle(500, "GetUserByName", err)
		}
		return
	}

	// Show SSH keys.
	if isShowKeys {
		ShowSSHKeys(ctx, u.Id)
		return
	}

	if u.IsOrganization() {
		ctx.Redirect(setting.AppSubUrl + "/org/" + u.Name)
		return
	}

	// For security reason, hide e-mail address for anonymous visitors.
	if !ctx.IsSigned {
		u.Email = ""
	}
	ctx.Data["Owner"] = u

	tab := ctx.Query("tab")
	ctx.Data["TabName"] = tab
	switch tab {
	case "activity":
		actions, err := models.GetFeeds(u.Id, 0, false)
		if err != nil {
			ctx.Handle(500, "GetFeeds", err)
			return
		}
		feeds := make([]*models.Action, 0, len(actions))
		for _, act := range actions {
			if act.IsPrivate {
				if !ctx.IsSigned {
					continue
				}
				// This prevents having to retrieve the repository for each action
				repo := &models.Repository{Id: act.RepoID, IsPrivate: true}
				if act.RepoUserName != ctx.User.LowerName {
					if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
						continue
					}
				}

			}
			// FIXME: cache results?
			u, err := models.GetUserByName(act.ActUserName)
			if err != nil {
				if err == models.ErrUserNotExist {
					continue
				}
				ctx.Handle(500, "GetUserByName", err)
				return
			}
			act.ActAvatar = u.AvatarLink()
			feeds = append(feeds, act)
		}
		ctx.Data["Feeds"] = feeds
	default:
		ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
		if err != nil {
			ctx.Handle(500, "GetRepositories", err)
			return
		}
	}

	ctx.HTML(200, PROFILE)
}

func Email2User(ctx *middleware.Context) {
	u, err := models.GetUserByEmail(ctx.Query("email"))
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "user.Email2User(GetUserByEmail)", err)
		} else {
			ctx.Handle(500, "user.Email2User(GetUserByEmail)", err)
		}
		return
	}
	ctx.Redirect(setting.AppSubUrl + "/user/" + u.Name)
}

func Issues(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("issues")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsIssues"] = true

	viewType := ctx.Query("type")
	types := []string{"assigned", "created_by"}
	if !com.IsSliceContainsStr(types, viewType) {
		viewType = "all"
	}

	isShowClosed := ctx.Query("state") == "closed"

	var filterMode int
	switch viewType {
	case "assigned":
		filterMode = models.FM_ASSIGN
	case "created_by":
		filterMode = models.FM_CREATE
	}

	repoId, _ := com.StrTo(ctx.Query("repoid")).Int64()
	issueStats := models.GetUserIssueStats(ctx.User.Id, filterMode)

	// Get all repositories.
	repos, err := models.GetRepositories(ctx.User.Id, true)
	if err != nil {
		ctx.Handle(500, "user.Issues(GetRepositories)", err)
		return
	}

	repoIds := make([]int64, 0, len(repos))
	showRepos := make([]*models.Repository, 0, len(repos))
	for _, repo := range repos {
		if repo.NumIssues == 0 {
			continue
		}

		repoIds = append(repoIds, repo.Id)
		repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
		issueStats.AllCount += int64(repo.NumOpenIssues)

		if isShowClosed {
			if repo.NumClosedIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumClosedIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		} else {
			if repo.NumOpenIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumOpenIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		}
	}

	if repoId > 0 {
		repoIds = []int64{repoId}
	}

	page, _ := com.StrTo(ctx.Query("page")).Int()

	// Get all issues.
	var ius []*models.IssueUser
	switch viewType {
	case "assigned":
		fallthrough
	case "created_by":
		ius, err = models.GetIssueUserPairsByMode(ctx.User.Id, repoId, isShowClosed, page, filterMode)
	default:
		ius, err = models.GetIssueUserPairsByRepoIds(repoIds, isShowClosed, page)
	}
	if err != nil {
		ctx.Handle(500, "user.Issues(GetAllIssueUserPairs)", err)
		return
	}

	issues := make([]*models.Issue, len(ius))
	for i := range ius {
		issues[i], err = models.GetIssueById(ius[i].IssueId)
		if err != nil {
			if err == models.ErrIssueNotExist {
				log.Warn("user.Issues(GetIssueById #%d): issue not exist", ius[i].IssueId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetIssueById #%d)", ius[i].IssueId), err)
				return
			}
		}

		issues[i].Repo, err = models.GetRepositoryById(issues[i].RepoId)
		if err != nil {
			if models.IsErrRepoNotExist(err) {
				log.Warn("user.Issues(GetRepositoryById #%d): repository not exist", issues[i].RepoId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetRepositoryById #%d)", issues[i].RepoId), err)
				return
			}
		}

		if err = issues[i].Repo.GetOwner(); err != nil {
			ctx.Handle(500, "user.Issues(GetOwner)", err)
			return
		}

		if err = issues[i].GetPoster(); err != nil {
			ctx.Handle(500, "user.Issues(GetUserById)", err)
			return
		}
	}

	ctx.Data["RepoId"] = repoId
	ctx.Data["Repos"] = showRepos
	ctx.Data["Issues"] = issues
	ctx.Data["ViewType"] = viewType
	ctx.Data["IssueStats"] = issueStats
	ctx.Data["IsShowClosed"] = isShowClosed
	if isShowClosed {
		ctx.Data["State"] = "closed"
		ctx.Data["ShowCount"] = issueStats.ClosedCount
	} else {
		ctx.Data["ShowCount"] = issueStats.OpenCount
	}

	ctx.Data["ContextUser"] = ctx.User

	ctx.HTML(200, ISSUES)
}
<commit_msg>fix .keys route<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package user

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/Unknwon/com"

	"github.com/go-gitea/gitea/models"
	"github.com/go-gitea/gitea/modules/base"
	"github.com/go-gitea/gitea/modules/log"
	"github.com/go-gitea/gitea/modules/middleware"
	"github.com/go-gitea/gitea/modules/setting"
)

const (
	DASHBOARD base.TplName = "user/dashboard/dashboard"
	PULLS     base.TplName = "user/dashboard/pulls"
	ISSUES    base.TplName = "user/issues"
	STARS     base.TplName = "user/stars"
	PROFILE   base.TplName = "user/profile"
)

func Dashboard(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("dashboard")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsNews"] = true

	var ctxUser *models.User
	// Check context type.
	orgName := ctx.Params(":org")
	if len(orgName) > 0 {
		// Organization.
		org, err := models.GetUserByName(orgName)
		if err != nil {
			if err == models.ErrUserNotExist {
				ctx.Handle(404, "GetUserByName", err)
			} else {
				ctx.Handle(500, "GetUserByName", err)
			}
			return
		}
		ctxUser = org
	} else {
		// Normal user.
		ctxUser = ctx.User
		collaborates, err := ctx.User.GetAccessibleRepositories()
		if err != nil {
			ctx.Handle(500, "GetAccessibleRepositories", err)
			return
		}

		repositories := make([]*models.Repository, 0, len(collaborates))
		for repo := range collaborates {
			repositories = append(repositories, repo)
		}

		ctx.Data["CollaborateCount"] = len(repositories)
		ctx.Data["CollaborativeRepos"] = repositories
	}
	ctx.Data["ContextUser"] = ctxUser

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["Orgs"] = ctx.User.Orgs

	repos, err := models.GetRepositories(ctxUser.Id, true)
	if err != nil {
		ctx.Handle(500, "GetRepositories", err)
		return
	}
	ctx.Data["Repos"] = repos

	// Get mirror repositories.
	mirrors := make([]*models.Repository, 0, len(repos)/2)
	for _, repo := range repos {
		if repo.IsMirror {
			if err = repo.GetMirror(); err != nil {
				ctx.Handle(500, "GetMirror: "+repo.Name, err)
				return
			}
			mirrors = append(mirrors, repo)
		}
	}
	ctx.Data["MirrorCount"] = len(mirrors)
	ctx.Data["Mirrors"] = mirrors

	// Get feeds.
	actions, err := models.GetFeeds(ctxUser.Id, 0, false)
	if err != nil {
		ctx.Handle(500, "GetFeeds", err)
		return
	}

	// Check access of private repositories.
	feeds := make([]*models.Action, 0, len(actions))
	for _, act := range actions {
		if act.IsPrivate {
			// This prevents having to retrieve the repository for each action
			repo := &models.Repository{Id: act.RepoID, IsPrivate: true}
			if act.RepoUserName != ctx.User.LowerName {
				if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
					continue
				}
			}

		}
		// FIXME: cache results?
		u, err := models.GetUserByName(act.ActUserName)
		if err != nil {
			if err == models.ErrUserNotExist {
				continue
			}
			ctx.Handle(500, "GetUserByName", err)
			return
		}
		act.ActAvatar = u.AvatarLink()
		feeds = append(feeds, act)
	}
	ctx.Data["Feeds"] = feeds
	ctx.HTML(200, DASHBOARD)
}

func Pulls(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("pull_requests")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsPulls"] = true

	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return
	}
	ctx.Data["ContextUser"] = ctx.User

	ctx.HTML(200, PULLS)
}

func ShowSSHKeys(ctx *middleware.Context, uid int64) {
	keys, err := models.ListPublicKeys(uid)
	if err != nil {
		ctx.Handle(500, "ListPublicKeys", err)
		return
	}

	var buf bytes.Buffer
	for i := range keys {
		buf.WriteString(keys[i].OmitEmail())
		buf.WriteString("\n")
	}
	ctx.RenderData(200, buf.Bytes())
}

func Profile(ctx *middleware.Context) {
	ctx.Data["Title"] = "Profile"
	ctx.Data["PageIsUserProfile"] = true

	uname := ctx.Params(":username")
	// Special handle for FireFox requests favicon.ico.
	if uname == "favicon.ico" {
		ctx.Redirect(setting.AppSubUrl + "/img/favicon.png")
		return
	}

	isShowKeys := false
	if strings.HasSuffix(uname, ".keys") {
		isShowKeys = true
		uname = strings.TrimSuffix(uname, ".keys")
	}

	u, err := models.GetUserByName(uname)
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "GetUserByName", err)
		} else {
			ctx.Handle(500, "GetUserByName", err)
		}
		return
	}

	// Show SSH keys.
	if isShowKeys {
		ShowSSHKeys(ctx, u.Id)
		return
	}

	if u.IsOrganization() {
		ctx.Redirect(setting.AppSubUrl + "/org/" + u.Name)
		return
	}

	// For security reason, hide e-mail address for anonymous visitors.
	if !ctx.IsSigned {
		u.Email = ""
	}
	ctx.Data["Owner"] = u

	tab := ctx.Query("tab")
	ctx.Data["TabName"] = tab
	switch tab {
	case "activity":
		actions, err := models.GetFeeds(u.Id, 0, false)
		if err != nil {
			ctx.Handle(500, "GetFeeds", err)
			return
		}
		feeds := make([]*models.Action, 0, len(actions))
		for _, act := range actions {
			if act.IsPrivate {
				if !ctx.IsSigned {
					continue
				}
				// This prevents having to retrieve the repository for each action
				repo := &models.Repository{Id: act.RepoID, IsPrivate: true}
				if act.RepoUserName != ctx.User.LowerName {
					if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
						continue
					}
				}

			}
			// FIXME: cache results?
			u, err := models.GetUserByName(act.ActUserName)
			if err != nil {
				if err == models.ErrUserNotExist {
					continue
				}
				ctx.Handle(500, "GetUserByName", err)
				return
			}
			act.ActAvatar = u.AvatarLink()
			feeds = append(feeds, act)
		}
		ctx.Data["Feeds"] = feeds
	default:
		ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
		if err != nil {
			ctx.Handle(500, "GetRepositories", err)
			return
		}
	}

	ctx.HTML(200, PROFILE)
}

func Email2User(ctx *middleware.Context) {
	u, err := models.GetUserByEmail(ctx.Query("email"))
	if err != nil {
		if err == models.ErrUserNotExist {
			ctx.Handle(404, "user.Email2User(GetUserByEmail)", err)
		} else {
			ctx.Handle(500, "user.Email2User(GetUserByEmail)", err)
		}
		return
	}
	ctx.Redirect(setting.AppSubUrl + "/user/" + u.Name)
}

func Issues(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("issues")
	ctx.Data["PageIsDashboard"] = true
	ctx.Data["PageIsIssues"] = true

	viewType := ctx.Query("type")
	types := []string{"assigned", "created_by"}
	if !com.IsSliceContainsStr(types, viewType) {
		viewType = "all"
	}

	isShowClosed := ctx.Query("state") == "closed"

	var filterMode int
	switch viewType {
	case "assigned":
		filterMode = models.FM_ASSIGN
	case "created_by":
		filterMode = models.FM_CREATE
	}

	repoId, _ := com.StrTo(ctx.Query("repoid")).Int64()
	issueStats := models.GetUserIssueStats(ctx.User.Id, filterMode)

	// Get all repositories.
	repos, err := models.GetRepositories(ctx.User.Id, true)
	if err != nil {
		ctx.Handle(500, "user.Issues(GetRepositories)", err)
		return
	}

	repoIds := make([]int64, 0, len(repos))
	showRepos := make([]*models.Repository, 0, len(repos))
	for _, repo := range repos {
		if repo.NumIssues == 0 {
			continue
		}

		repoIds = append(repoIds, repo.Id)
		repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
		issueStats.AllCount += int64(repo.NumOpenIssues)

		if isShowClosed {
			if repo.NumClosedIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumClosedIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		} else {
			if repo.NumOpenIssues > 0 {
				if filterMode == models.FM_CREATE {
					repo.NumOpenIssues = int(models.GetIssueCountByPoster(ctx.User.Id, repo.Id, isShowClosed))
				}
				showRepos = append(showRepos, repo)
			}
		}
	}

	if repoId > 0 {
		repoIds = []int64{repoId}
	}

	page, _ := com.StrTo(ctx.Query("page")).Int()

	// Get all issues.
	var ius []*models.IssueUser
	switch viewType {
	case "assigned":
		fallthrough
	case "created_by":
		ius, err = models.GetIssueUserPairsByMode(ctx.User.Id, repoId, isShowClosed, page, filterMode)
	default:
		ius, err = models.GetIssueUserPairsByRepoIds(repoIds, isShowClosed, page)
	}
	if err != nil {
		ctx.Handle(500, "user.Issues(GetAllIssueUserPairs)", err)
		return
	}

	issues := make([]*models.Issue, len(ius))
	for i := range ius {
		issues[i], err = models.GetIssueById(ius[i].IssueId)
		if err != nil {
			if err == models.ErrIssueNotExist {
				log.Warn("user.Issues(GetIssueById #%d): issue not exist", ius[i].IssueId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetIssueById #%d)", ius[i].IssueId), err)
				return
			}
		}

		issues[i].Repo, err = models.GetRepositoryById(issues[i].RepoId)
		if err != nil {
			if models.IsErrRepoNotExist(err) {
				log.Warn("user.Issues(GetRepositoryById #%d): repository not exist", issues[i].RepoId)
				continue
			} else {
				ctx.Handle(500, fmt.Sprintf("user.Issues(GetRepositoryById #%d)", issues[i].RepoId), err)
				return
			}
		}

		if err = issues[i].Repo.GetOwner(); err != nil {
			ctx.Handle(500, "user.Issues(GetOwner)", err)
			return
		}

		if err = issues[i].GetPoster(); err != nil {
			ctx.Handle(500, "user.Issues(GetUserById)", err)
			return
		}
	}

	ctx.Data["RepoId"] = repoId
	ctx.Data["Repos"] = showRepos
	ctx.Data["Issues"] = issues
	ctx.Data["ViewType"] = viewType
	ctx.Data["IssueStats"] = issueStats
	ctx.Data["IsShowClosed"] = isShowClosed
	if isShowClosed {
		ctx.Data["State"] = "closed"
		ctx.Data["ShowCount"] = issueStats.ClosedCount
	} else {
		ctx.Data["ShowCount"] = issueStats.OpenCount
	}

	ctx.Data["ContextUser"] = ctx.User

	ctx.HTML(200, ISSUES)
}
<|endoftext|>


<commit_before>// continuserv proactively re-generates the spec on filesystem changes, and serves it over HTTP.
// It will always serve the most recent version of the spec, and may block an HTTP request until regeneration is finished.
// It does not currently pre-empt stale generations, but will block until they are complete.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"

	fsnotify "gopkg.in/fsnotify.v1"
)

var (
	port = flag.Int("port", 8000, "Port on which to serve HTTP")

	toServe atomic.Value   // Always contains valid []byte to serve. May be stale unless wg is zero.
	wg      sync.WaitGroup // Indicates how many updates are pending.
	mu      sync.Mutex     // Prevent multiple updates in parallel.
)

func main() {
	flag.Parse()

	w, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatalf("Error making watcher: %v", err)
	}

	dir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Error getting wd: %v", err)
	}
	for ; !exists(path.Join(dir, ".git")); dir = path.Dir(dir) {
		if dir == "/" {
			log.Fatalf("Could not find git root")
		}
	}

	filepath.Walk(dir, makeWalker(w))

	wg.Add(1)
	populateOnce(dir)

	ch := make(chan struct{}, 100) // Buffered to ensure we can multiple-increment wg for pending writes
	go doPopulate(ch, dir)

	go watchFS(ch, w)
    fmt.Printf("Listening on port %d\n", *port)
	http.HandleFunc("/", serve)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))

}

func watchFS(ch chan struct{}, w *fsnotify.Watcher) {
	for {
		select {
		case e := <-w.Events:
			if filter(e) {
				wg.Add(1)
				fmt.Printf("Noticed change to %s, re-generating spec\n", e.Name)
				ch <- struct{}{}
			}
		}
	}
}

func makeWalker(w *fsnotify.Watcher) filepath.WalkFunc {
	return func(path string, _ os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("Error walking: %v", err)
		}
		if err := w.Add(path); err != nil {
			log.Fatalf("Failed to add watch: %v", err)
		}
		return nil
	}
}

// Return true if event should trigger re-population
func filter(e fsnotify.Event) bool {
	// vim is *really* noisy about how it writes files
	if e.Op != fsnotify.Write {
		return false
	}
	// Avoid some temp files that vim writes
	if strings.HasSuffix(e.Name, "~") || strings.HasSuffix(e.Name, ".swp") || strings.HasPrefix(e.Name, ".") {
		return false
	}

	// Avoid infinite cycles being caused by writing actual output
	if strings.Contains(e.Name, "/tmp/") || strings.Contains(e.Name, "/gen/") {
		return false
	}
	return true
}

func serve(w http.ResponseWriter, req *http.Request) {
	wg.Wait()
	b := toServe.Load().([]byte)
	w.Write(b)
}

func populateOnce(dir string) {
	defer wg.Done()
	mu.Lock()
	defer mu.Unlock()
	cmd := exec.Command("python", "gendoc.py")
	cmd.Dir = path.Join(dir, "scripts")
	var b bytes.Buffer
	cmd.Stderr = &b
	err := cmd.Run()
	if err != nil {
		toServe.Store([]byte(fmt.Errorf("error generating spec: %v\nOutput from gendoc:\n%v", err, b.String()).Error()))
		return
	}
	specBytes, err := ioutil.ReadFile(path.Join(dir, "scripts", "gen", "specification.html"))
	if err != nil {
		toServe.Store([]byte(fmt.Errorf("error reading spec: %v", err).Error()))
		return
	}
	toServe.Store(specBytes)
}

func doPopulate(ch chan struct{}, dir string) {
	for _ = range ch {
		populateOnce(dir)
	}
}

func exists(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}
<commit_msg>Apparently tabs are A Thing<commit_after>// continuserv proactively re-generates the spec on filesystem changes, and serves it over HTTP.
// It will always serve the most recent version of the spec, and may block an HTTP request until regeneration is finished.
// It does not currently pre-empt stale generations, but will block until they are complete.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"

	fsnotify "gopkg.in/fsnotify.v1"
)

var (
	port = flag.Int("port", 8000, "Port on which to serve HTTP")

	toServe atomic.Value   // Always contains valid []byte to serve. May be stale unless wg is zero.
	wg      sync.WaitGroup // Indicates how many updates are pending.
	mu      sync.Mutex     // Prevent multiple updates in parallel.
)

func main() {
	flag.Parse()

	w, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatalf("Error making watcher: %v", err)
	}

	dir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Error getting wd: %v", err)
	}
	for ; !exists(path.Join(dir, ".git")); dir = path.Dir(dir) {
		if dir == "/" {
			log.Fatalf("Could not find git root")
		}
	}

	filepath.Walk(dir, makeWalker(w))

	wg.Add(1)
	populateOnce(dir)

	ch := make(chan struct{}, 100) // Buffered to ensure we can multiple-increment wg for pending writes
	go doPopulate(ch, dir)

	go watchFS(ch, w)
	fmt.Printf("Listening on port %d\n", *port)
	http.HandleFunc("/", serve)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))

}

func watchFS(ch chan struct{}, w *fsnotify.Watcher) {
	for {
		select {
		case e := <-w.Events:
			if filter(e) {
				wg.Add(1)
				fmt.Printf("Noticed change to %s, re-generating spec\n", e.Name)
				ch <- struct{}{}
			}
		}
	}
}

func makeWalker(w *fsnotify.Watcher) filepath.WalkFunc {
	return func(path string, _ os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("Error walking: %v", err)
		}
		if err := w.Add(path); err != nil {
			log.Fatalf("Failed to add watch: %v", err)
		}
		return nil
	}
}

// Return true if event should trigger re-population
func filter(e fsnotify.Event) bool {
	// vim is *really* noisy about how it writes files
	if e.Op != fsnotify.Write {
		return false
	}
	// Avoid some temp files that vim writes
	if strings.HasSuffix(e.Name, "~") || strings.HasSuffix(e.Name, ".swp") || strings.HasPrefix(e.Name, ".") {
		return false
	}

	// Avoid infinite cycles being caused by writing actual output
	if strings.Contains(e.Name, "/tmp/") || strings.Contains(e.Name, "/gen/") {
		return false
	}
	return true
}

func serve(w http.ResponseWriter, req *http.Request) {
	wg.Wait()
	b := toServe.Load().([]byte)
	w.Write(b)
}

func populateOnce(dir string) {
	defer wg.Done()
	mu.Lock()
	defer mu.Unlock()
	cmd := exec.Command("python", "gendoc.py")
	cmd.Dir = path.Join(dir, "scripts")
	var b bytes.Buffer
	cmd.Stderr = &b
	err := cmd.Run()
	if err != nil {
		toServe.Store([]byte(fmt.Errorf("error generating spec: %v\nOutput from gendoc:\n%v", err, b.String()).Error()))
		return
	}
	specBytes, err := ioutil.ReadFile(path.Join(dir, "scripts", "gen", "specification.html"))
	if err != nil {
		toServe.Store([]byte(fmt.Errorf("error reading spec: %v", err).Error()))
		return
	}
	toServe.Store(specBytes)
}

func doPopulate(ch chan struct{}, dir string) {
	for _ = range ch {
		populateOnce(dir)
	}
}

func exists(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

// githubToSlack maps (lowercase!) GitHub usernames to Slack user IDs.
// Add yourself when you join the oncall rotation.
// You can mostly easily find your user ID by visiting your Slack profile, clicking "...",
// and clicking "Copy user ID".
var githubToSlack = map[string]string{
	"amwat":          "U9B1P2UGP",
	"bentheelder":    "U1P7T516X",
	"chases2":        "UJ9R0FWD6",
	"cjwagner":       "U4QFZFMCM",
	"fejta":          "U0E2KHQ13",
	"katharine":      "UBTBNJ6GL",
	"michelle192837": "U3TRY5WV7",
	"mushuee":        "U013TPFJWC8",
	"spiffxp":        "U09R2FL93",
}

// rotationToSlack group maps the rotation in go.k8s.io/oncall to the slack
// group ID for the oncall alias group for this rotation
var rotationToSlackGroup = map[string]string{
	"test-infra":         "SGLF0GUQH",   // @test-infra-oncall
	"google-build-admin": "S017N31TLNN", // @google-build-admin
	// TODO: create this slack group?
	// "scalability": "",
}

func getJSON(url string, v interface{}) error {
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("failed to fetch %q: %v", url, err)
	}
	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(v)
	if err != nil {
		return fmt.Errorf("failed to decode json from %q: %v", url, err)
	}
	return nil
}

func getCurrentOncallers() (map[string]string, error) {
	oncallBlob := struct {
		Oncall map[string]string `json:"Oncall"`
	}{}

	err := getJSON("https://storage.googleapis.com/kubernetes-jenkins/oncall.json", &oncallBlob)
	if err != nil {
		return nil, err
	}

	return oncallBlob.Oncall, nil
}

func updateGroupMembership(token, groupID, userID string) error {
	result := struct {
		Ok    bool   `json:"ok"`
		Error string `json:"error,omitempty"`
	}{}

	err := getJSON("https://slack.com/api/usergroups.users.update?token="+token+"&users="+userID+"&usergroup="+groupID, &result)
	if err != nil {
		return fmt.Errorf("couldn't make membership request: %v", err)
	}

	if !result.Ok {
		return fmt.Errorf("couldn't update membership: %s", result.Error)
	}

	return nil
}

func getTokenFromPath(path string) (string, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return "", fmt.Errorf("couldn't open file: %v", err)
	}
	return strings.TrimSpace(string(content)), nil
}

type options struct {
	tokenPath string
}

func parseFlags() options {
	o := options{}
	flag.StringVar(&o.tokenPath, "token-path", "/etc/slack-token", "Path to a file containing the slack token")
	flag.Parse()
	return o
}

func main() {
	o := parseFlags()

	token, err := getTokenFromPath(o.tokenPath)
	if err != nil {
		log.Fatalf("Failed to get Slack token: %v", err)
	}

	oncallers, err := getCurrentOncallers()
	if err != nil {
		log.Fatalf("Failed to find current oncallers: %v", err)
	}
	log.Printf("Current oncallers: %s\n", oncallers)

	failed := false
	for rotation, user := range oncallers {
		// skip rotations that have not yet added their slack ID
		// this tool is not required
		groupID, exists := rotationToSlackGroup[rotation]
		if !exists {
			log.Printf("Rotation %q does not yet have a Group ID, skipping ...", rotation)
			continue
		}

		// get the slack ID for the current oncall in this rotation
		userID, exists := githubToSlack[strings.ToLower(user)]
		if !exists {
			log.Printf("Failed to get Slack ID for GitHub user %q", user)
			// continue with other rotations, but exit error afterwards
			failed = true
			continue
		}
		log.Printf("%s's slack ID: %s\n", user, userID)

		// update this rotation's slack instance
		log.Printf("Adding slack user %s to slack usergroup %s", userID, groupID)
		if err := updateGroupMembership(token, groupID, userID); err != nil {
			log.Fatalf("Failed to update usergroup membership: %v", err)
		}
	}
	// if one of the group updates failed, fail the run now
	if failed {
		log.Fatal("Failed to update some rotation(s)")
	}

	log.Printf("Done!")
}
<commit_msg>correct test-infra rotation key<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

// githubToSlack maps (lowercase!) GitHub usernames to Slack user IDs.
// Add yourself when you join the oncall rotation.
// You can mostly easily find your user ID by visiting your Slack profile, clicking "...",
// and clicking "Copy user ID".
var githubToSlack = map[string]string{
	"amwat":          "U9B1P2UGP",
	"bentheelder":    "U1P7T516X",
	"chases2":        "UJ9R0FWD6",
	"cjwagner":       "U4QFZFMCM",
	"fejta":          "U0E2KHQ13",
	"katharine":      "UBTBNJ6GL",
	"michelle192837": "U3TRY5WV7",
	"mushuee":        "U013TPFJWC8",
	"spiffxp":        "U09R2FL93",
}

// rotationToSlack group maps the rotation in go.k8s.io/oncall to the slack
// group ID for the oncall alias group for this rotation
var rotationToSlackGroup = map[string]string{
	"testinfra":          "SGLF0GUQH",   // @test-infra-oncall
	"google-build-admin": "S017N31TLNN", // @google-build-admin
	// TODO: create this slack group?
	// "scalability": "",
}

func getJSON(url string, v interface{}) error {
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("failed to fetch %q: %v", url, err)
	}
	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(v)
	if err != nil {
		return fmt.Errorf("failed to decode json from %q: %v", url, err)
	}
	return nil
}

func getCurrentOncallers() (map[string]string, error) {
	oncallBlob := struct {
		Oncall map[string]string `json:"Oncall"`
	}{}

	err := getJSON("https://storage.googleapis.com/kubernetes-jenkins/oncall.json", &oncallBlob)
	if err != nil {
		return nil, err
	}

	return oncallBlob.Oncall, nil
}

func updateGroupMembership(token, groupID, userID string) error {
	result := struct {
		Ok    bool   `json:"ok"`
		Error string `json:"error,omitempty"`
	}{}

	err := getJSON("https://slack.com/api/usergroups.users.update?token="+token+"&users="+userID+"&usergroup="+groupID, &result)
	if err != nil {
		return fmt.Errorf("couldn't make membership request: %v", err)
	}

	if !result.Ok {
		return fmt.Errorf("couldn't update membership: %s", result.Error)
	}

	return nil
}

func getTokenFromPath(path string) (string, error) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		return "", fmt.Errorf("couldn't open file: %v", err)
	}
	return strings.TrimSpace(string(content)), nil
}

type options struct {
	tokenPath string
}

func parseFlags() options {
	o := options{}
	flag.StringVar(&o.tokenPath, "token-path", "/etc/slack-token", "Path to a file containing the slack token")
	flag.Parse()
	return o
}

func main() {
	o := parseFlags()

	token, err := getTokenFromPath(o.tokenPath)
	if err != nil {
		log.Fatalf("Failed to get Slack token: %v", err)
	}

	oncallers, err := getCurrentOncallers()
	if err != nil {
		log.Fatalf("Failed to find current oncallers: %v", err)
	}
	log.Printf("Current oncallers: %s\n", oncallers)

	failed := false
	for rotation, user := range oncallers {
		// skip rotations that have not yet added their slack ID
		// this tool is not required
		groupID, exists := rotationToSlackGroup[rotation]
		if !exists {
			log.Printf("Rotation %q does not yet have a Group ID, skipping ...", rotation)
			continue
		}

		// get the slack ID for the current oncall in this rotation
		userID, exists := githubToSlack[strings.ToLower(user)]
		if !exists {
			log.Printf("Failed to get Slack ID for GitHub user %q", user)
			// continue with other rotations, but exit error afterwards
			failed = true
			continue
		}
		log.Printf("%s's slack ID: %s\n", user, userID)

		// update this rotation's slack instance
		log.Printf("Adding slack user %s to slack usergroup %s", userID, groupID)
		if err := updateGroupMembership(token, groupID, userID); err != nil {
			log.Fatalf("Failed to update usergroup membership: %v", err)
		}
	}
	// if one of the group updates failed, fail the run now
	if failed {
		log.Fatal("Failed to update some rotation(s)")
	}

	log.Printf("Done!")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"html"
	"net/http"
	"net/url"
	"strconv"
	"time"
	"unicode/utf8"

	"github.com/gin-gonic/gin"
	"gopkg.in/flosch/pongo2.v3"

	grn "github.com/hnakamur/cgoroonga"
)

var ctx *grn.Ctx
var dbFilename string
var listenAddress string

func init() {
	flag.StringVar(&dbFilename, "d", "wikipedia_ja.db", "database filename")
	flag.StringVar(&listenAddress, "l", ":8080", "listen address (address:port)")
}

func formIntValue(c *gin.Context, key string, defaultValue int) (int, error) {
	strValue := c.Request.FormValue(key)
	intValue := defaultValue
	if strValue != "" {
		var err error
		intValue, err = strconv.Atoi(strValue)
		if err != nil {
			return defaultValue,
				fmt.Errorf("int parameter expected, but got \"%s\" for parameter \"%s\"",
					strValue, key)
		}
	}
	return intValue, nil
}

func formDateValue(c *gin.Context, key string, defaultValue time.Time) (time.Time, error) {
	strValue := c.Request.FormValue(key)
	timeValue := defaultValue
	if strValue != "" {
		var err error
		timeValue, err = time.Parse("2006-1-2", strValue)
		if err != nil {
			return defaultValue,
				fmt.Errorf("date parameter expected, but got \"%s\" for parameter \"%s\"",
					strValue, key)
		}
	}
	return timeValue, nil
}

type Result struct {
	URL       string
	Title     string
	Content   string
	UpdatedAt string
}

func titleToURL(title string) string {
	return fmt.Sprintf("https://ja.wikipedia.org/wiki/%s",
		url.QueryEscape(title))
}

func calcStartDate(timespan string) time.Time {
	var duration time.Duration
	switch timespan {
	case "week":
		duration = 7 * 24 * time.Hour
	case "month":
		duration = 31 * 24 * time.Hour
	case "year":
		duration = 365 * 24 * time.Hour
	default:
		return time.Unix(0, 0)
	}
	return time.Now().Add(-duration)
}

const viewablePageCount = 9

func getIndex(c *gin.Context) {
	c.Request.ParseForm()
	q := c.Request.Form.Get("q")
	timespan := c.Request.Form.Get("timespan")
	var err error
	var limitCount int = 10
	var page int = 1
	var numPages int = 1
	var matchedCount uint
	viewablePages := []int{}
	results := []Result{}
	var cond *grn.Obj
	if q != "" {
		page, err = formIntValue(c, "page", 1)
		if err != nil {
			c.String(http.StatusBadRequest, err.Error())
			return
		}
		if page < 1 {
			page = 1
		}

		limitCount, err = formIntValue(c, "limit", 10)
		if err != nil {
			c.String(http.StatusBadRequest, err.Error())
			return
		}
		offset := (page - 1) * limitCount

		startDate := calcStartDate(timespan)

		table := ctx.Get("Articles")
		defer ctx.ObjUnlinkDefer(&err, table)

		var res *grn.Obj
		var v *grn.Obj
		cond, v, err = ctx.ExprCreateForQuery(table)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, cond)
		defer ctx.ObjUnlinkDefer(&err, v)

		query := fmt.Sprintf("_key:@%s OR text:@%s", q, q)
		flags := grn.EXPR_SYNTAX_QUERY | grn.EXPR_ALLOW_PRAGMA | grn.EXPR_ALLOW_COLUMN
		err = ctx.ExprParse(cond, query, nil, grn.OP_MATCH, grn.OP_AND, flags)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		if !startDate.IsZero() {
			usecStartTime := startDate.UnixNano() / 1000
			filter := "updated_at >= " + strconv.FormatInt(usecStartTime, 10)
			err = ctx.ExprParse(cond, filter, nil, grn.OP_MATCH, grn.OP_AND,
				grn.EXPR_SYNTAX_SCRIPT)
			if err != nil {
				c.String(http.StatusInternalServerError, err.Error())
				return
			}

			if q != "" {
				err = ctx.ExprAppendOp(cond, grn.OP_AND, 2)
				if err != nil {
					c.String(http.StatusInternalServerError, err.Error())
					return
				}
			}
		}

		res, err = ctx.TableSelect(table, cond, nil, grn.OP_OR)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, res)

		matchedCount, err = ctx.TableSize(res)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		numPages = ((int(matchedCount) - 1) / limitCount) + 1
		if page > numPages {
			page = numPages
		}
		startPage := page - (viewablePageCount / 2)
		if startPage < 1 {
			startPage = 1
		}
		endPage := startPage + viewablePageCount
		if endPage > numPages+1 {
			endPage = numPages + 1
			if endPage-startPage < viewablePageCount {
				startPage = endPage - viewablePageCount
				if startPage < 1 {
					startPage = 1
				}
			}
		}
		for i := startPage; i < endPage; i++ {
			viewablePages = append(viewablePages, i)
		}

		var keyColumn *grn.Obj
		keyColumn, err = ctx.ObjColumn(res, "_key")
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, keyColumn)

		var textColumn *grn.Obj
		textColumn, err = ctx.ObjColumn(res, "text")
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, textColumn)

		var updatedAtColumn *grn.Obj
		updatedAtColumn, err = ctx.ObjColumn(res, "updated_at")
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, updatedAtColumn)

		tc, err := ctx.TableCursorOpen(res, "", "", offset, limitCount, grn.CURSOR_ASCENDING)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.TableCursorClose(tc)

		var buf grn.Obj
		defer ctx.ObjUnlinkDefer(&err, &buf)
		for {
			id := ctx.TableCursorNext(tc)
			if id == grn.ID_NIL {
				break
			}
			grn.TextInit(&buf, 0)
			grn.BulkRewind(&buf)
			ctx.ObjGetValue(keyColumn, id, &buf)
			title := grn.BulkHead(&buf)

			grn.TextInit(&buf, 0)
			grn.BulkRewind(&buf)
			ctx.ObjGetValue(textColumn, id, &buf)
			text := grn.BulkHead(&buf)

			flags := grn.SNIP_COPY_TAG | grn.SNIP_SKIP_LEADING_SPACES
			textManLen := 200
			snippet := ctx.ExprSnippet(cond, flags, textManLen, 1, true,
				[][]string{
					[]string{"", ""},
				})
			snipResults, err := ctx.SnipExec(snippet, text)
			if err != nil {
				c.String(http.StatusInternalServerError, err.Error())
				return
			}
			if len(snipResults) > 0 {
				text = "..." + snipResults[0] + "..."

			} else {
				if utf8.RuneCountInString(text) >= textManLen {
					r := []rune(text)
					text = string(r[:textManLen]) + "..."
				}
				text = html.EscapeString(text)
			}

			grn.TimeInit(&buf, 0)
			ctx.ObjGetValue(updatedAtColumn, id, &buf)
			updatedAt := grn.TimeValue(&buf)

			url := titleToURL(title)

			results = append(results, Result{
				URL:       url,
				Title:     title,
				Content:   text,
				UpdatedAt: updatedAt.Format("2006/01/02"),
			})
		}
	}

	url_ := fmt.Sprintf("%s?q=%s&timespan=%s&limit=%d",
		c.Request.URL.Path, url.QueryEscape(q), timespan, limitCount)
	tpl, err := pongo2.FromFile("templates/index.html")
	if err != nil {
		c.String(500, "Internal Server Error")
	}
	err = tpl.ExecuteWriter(pongo2.Context{
		"url":           url_,
		"q":             q,
		"timespan":      timespan,
		"matchedCount":  matchedCount,
		"results":       results,
		"page":          page,
		"numPages":      numPages,
		"viewablePages": viewablePages,
	}, c.Writer)
	if err != nil {
		c.String(500, "Internal Server Error")
	}
}

func main() {
	flag.Parse()

	var err error
	err = grn.Init()
	if err != nil {
		panic(err)
	}
	defer grn.FinDefer(&err)

	ctx, err = grn.CtxOpen(0)
	if err != nil {
		panic(err)
	}
	defer ctx.CloseDefer(&err)

	var db *grn.Obj
	db, err = ctx.DBOpenOrCreate(dbFilename, nil)
	if err != nil {
		panic(err)
	}
	defer ctx.ObjUnlinkDefer(&err, db)

	r := gin.Default()
	r.Static("/static", "./static")
	r.GET("/", getIndex)
	r.Run(listenAddress)
}
<commit_msg>Fix a typo: textManLen -> textMaxLen<commit_after>package main

import (
	"flag"
	"fmt"
	"html"
	"net/http"
	"net/url"
	"strconv"
	"time"
	"unicode/utf8"

	"github.com/gin-gonic/gin"
	"gopkg.in/flosch/pongo2.v3"

	grn "github.com/hnakamur/cgoroonga"
)

var ctx *grn.Ctx
var dbFilename string
var listenAddress string

func init() {
	flag.StringVar(&dbFilename, "d", "wikipedia_ja.db", "database filename")
	flag.StringVar(&listenAddress, "l", ":8080", "listen address (address:port)")
}

func formIntValue(c *gin.Context, key string, defaultValue int) (int, error) {
	strValue := c.Request.FormValue(key)
	intValue := defaultValue
	if strValue != "" {
		var err error
		intValue, err = strconv.Atoi(strValue)
		if err != nil {
			return defaultValue,
				fmt.Errorf("int parameter expected, but got \"%s\" for parameter \"%s\"",
					strValue, key)
		}
	}
	return intValue, nil
}

func formDateValue(c *gin.Context, key string, defaultValue time.Time) (time.Time, error) {
	strValue := c.Request.FormValue(key)
	timeValue := defaultValue
	if strValue != "" {
		var err error
		timeValue, err = time.Parse("2006-1-2", strValue)
		if err != nil {
			return defaultValue,
				fmt.Errorf("date parameter expected, but got \"%s\" for parameter \"%s\"",
					strValue, key)
		}
	}
	return timeValue, nil
}

type Result struct {
	URL       string
	Title     string
	Content   string
	UpdatedAt string
}

func titleToURL(title string) string {
	return fmt.Sprintf("https://ja.wikipedia.org/wiki/%s",
		url.QueryEscape(title))
}

func calcStartDate(timespan string) time.Time {
	var duration time.Duration
	switch timespan {
	case "week":
		duration = 7 * 24 * time.Hour
	case "month":
		duration = 31 * 24 * time.Hour
	case "year":
		duration = 365 * 24 * time.Hour
	default:
		return time.Unix(0, 0)
	}
	return time.Now().Add(-duration)
}

const viewablePageCount = 9

func getIndex(c *gin.Context) {
	c.Request.ParseForm()
	q := c.Request.Form.Get("q")
	timespan := c.Request.Form.Get("timespan")
	var err error
	var limitCount int = 10
	var page int = 1
	var numPages int = 1
	var matchedCount uint
	viewablePages := []int{}
	results := []Result{}
	var cond *grn.Obj
	if q != "" {
		page, err = formIntValue(c, "page", 1)
		if err != nil {
			c.String(http.StatusBadRequest, err.Error())
			return
		}
		if page < 1 {
			page = 1
		}

		limitCount, err = formIntValue(c, "limit", 10)
		if err != nil {
			c.String(http.StatusBadRequest, err.Error())
			return
		}
		offset := (page - 1) * limitCount

		startDate := calcStartDate(timespan)

		table := ctx.Get("Articles")
		defer ctx.ObjUnlinkDefer(&err, table)

		var res *grn.Obj
		var v *grn.Obj
		cond, v, err = ctx.ExprCreateForQuery(table)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, cond)
		defer ctx.ObjUnlinkDefer(&err, v)

		query := fmt.Sprintf("_key:@%s OR text:@%s", q, q)
		flags := grn.EXPR_SYNTAX_QUERY | grn.EXPR_ALLOW_PRAGMA | grn.EXPR_ALLOW_COLUMN
		err = ctx.ExprParse(cond, query, nil, grn.OP_MATCH, grn.OP_AND, flags)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		if !startDate.IsZero() {
			usecStartTime := startDate.UnixNano() / 1000
			filter := "updated_at >= " + strconv.FormatInt(usecStartTime, 10)
			err = ctx.ExprParse(cond, filter, nil, grn.OP_MATCH, grn.OP_AND,
				grn.EXPR_SYNTAX_SCRIPT)
			if err != nil {
				c.String(http.StatusInternalServerError, err.Error())
				return
			}

			if q != "" {
				err = ctx.ExprAppendOp(cond, grn.OP_AND, 2)
				if err != nil {
					c.String(http.StatusInternalServerError, err.Error())
					return
				}
			}
		}

		res, err = ctx.TableSelect(table, cond, nil, grn.OP_OR)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, res)

		matchedCount, err = ctx.TableSize(res)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		numPages = ((int(matchedCount) - 1) / limitCount) + 1
		if page > numPages {
			page = numPages
		}
		startPage := page - (viewablePageCount / 2)
		if startPage < 1 {
			startPage = 1
		}
		endPage := startPage + viewablePageCount
		if endPage > numPages+1 {
			endPage = numPages + 1
			if endPage-startPage < viewablePageCount {
				startPage = endPage - viewablePageCount
				if startPage < 1 {
					startPage = 1
				}
			}
		}
		for i := startPage; i < endPage; i++ {
			viewablePages = append(viewablePages, i)
		}

		var keyColumn *grn.Obj
		keyColumn, err = ctx.ObjColumn(res, "_key")
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, keyColumn)

		var textColumn *grn.Obj
		textColumn, err = ctx.ObjColumn(res, "text")
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, textColumn)

		var updatedAtColumn *grn.Obj
		updatedAtColumn, err = ctx.ObjColumn(res, "updated_at")
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.ObjUnlinkDefer(&err, updatedAtColumn)

		tc, err := ctx.TableCursorOpen(res, "", "", offset, limitCount, grn.CURSOR_ASCENDING)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		defer ctx.TableCursorClose(tc)

		var buf grn.Obj
		defer ctx.ObjUnlinkDefer(&err, &buf)
		for {
			id := ctx.TableCursorNext(tc)
			if id == grn.ID_NIL {
				break
			}
			grn.TextInit(&buf, 0)
			grn.BulkRewind(&buf)
			ctx.ObjGetValue(keyColumn, id, &buf)
			title := grn.BulkHead(&buf)

			grn.TextInit(&buf, 0)
			grn.BulkRewind(&buf)
			ctx.ObjGetValue(textColumn, id, &buf)
			text := grn.BulkHead(&buf)

			flags := grn.SNIP_COPY_TAG | grn.SNIP_SKIP_LEADING_SPACES
			textMaxLen := 200
			snippet := ctx.ExprSnippet(cond, flags, textMaxLen, 1, true,
				[][]string{
					[]string{"", ""},
				})
			snipResults, err := ctx.SnipExec(snippet, text)
			if err != nil {
				c.String(http.StatusInternalServerError, err.Error())
				return
			}
			if len(snipResults) > 0 {
				text = "..." + snipResults[0] + "..."

			} else {
				if utf8.RuneCountInString(text) >= textMaxLen {
					r := []rune(text)
					text = string(r[:textMaxLen]) + "..."
				}
				text = html.EscapeString(text)
			}

			grn.TimeInit(&buf, 0)
			ctx.ObjGetValue(updatedAtColumn, id, &buf)
			updatedAt := grn.TimeValue(&buf)

			url := titleToURL(title)

			results = append(results, Result{
				URL:       url,
				Title:     title,
				Content:   text,
				UpdatedAt: updatedAt.Format("2006/01/02"),
			})
		}
	}

	url_ := fmt.Sprintf("%s?q=%s&timespan=%s&limit=%d",
		c.Request.URL.Path, url.QueryEscape(q), timespan, limitCount)
	tpl, err := pongo2.FromFile("templates/index.html")
	if err != nil {
		c.String(500, "Internal Server Error")
	}
	err = tpl.ExecuteWriter(pongo2.Context{
		"url":           url_,
		"q":             q,
		"timespan":      timespan,
		"matchedCount":  matchedCount,
		"results":       results,
		"page":          page,
		"numPages":      numPages,
		"viewablePages": viewablePages,
	}, c.Writer)
	if err != nil {
		c.String(500, "Internal Server Error")
	}
}

func main() {
	flag.Parse()

	var err error
	err = grn.Init()
	if err != nil {
		panic(err)
	}
	defer grn.FinDefer(&err)

	ctx, err = grn.CtxOpen(0)
	if err != nil {
		panic(err)
	}
	defer ctx.CloseDefer(&err)

	var db *grn.Obj
	db, err = ctx.DBOpenOrCreate(dbFilename, nil)
	if err != nil {
		panic(err)
	}
	defer ctx.ObjUnlinkDefer(&err, db)

	r := gin.Default()
	r.Static("/static", "./static")
	r.GET("/", getIndex)
	r.Run(listenAddress)
}
<|endoftext|>


<commit_before>package minesweeper

import (
	"bytes"
	"testing"
)

const targetTestVersion = 1

var validBoards = []string{
	// zero size board
	`
++
++`,

	// empty board
	`
+---+
|   |
|   |
|   |
+---+`,

	// board full of mines
	`
+---+
|***|
|***|
|***|
+---+`,

	// surrounded
	`
+---+
|***|
|*8*|
|***|
+---+`,

	// horizontal line
	`
+-----+
|1*2*1|
+-----+`,

	// vertical line
	`
+-+
|1|
|*|
|2|
|*|
|1|
+-+`,

	// cross
	`
+-----+
| 2*2 |
|25*52|
|*****|
|25*52|
| 2*2 |
+-----+`,

	// Large 6x6 board with 8 bombs
	`
+------+
|1*22*1|
|12*322|
| 123*2|
|112*4*|
|1*22*2|
|111111|
+------+`,

	// Large 5x5 board with 7 bombs
	`
+-----+
|1*2*1|
|11322|
| 12*2|
|12*4*|
|1*3*2|
+-----+`,

	// Small 1x5 board with 2 bombs
	`
+-+
|*|
|2|
|*|
|1|
| |
+-+`,

	// 1x1 sqaure with 1 bomb
	`
+-+
|*|
+-+`,

	// 2x2 square with 4 bombs
	`
+--+
|**|
|**|
+--+`,

	// 5x5 square with 2 bombs
	`
+-----+
|  111|
|  1*1|
|  111|
|111  |
|1*1  |
+-----+`,
}

var badBoards = []string{
	// Unaligned
	`
+-+
| |
|*  |
|  |
+-+`,

	// incomplete border
	`
+-----+
*   * |
+-- --+`,

	// Unknown characters
	`
+-----+
|X  * |
+-----+`,
}

// constructor.  take board as a string, return Board with digits cleared.
func clear(s string) Board {
	b := []byte(s)
	for i, c := range b {
		if c >= '0' && c <= '9' {
			b[i] = ' '
		}
	}
	return bytes.Split(b, []byte{'\n'})[1:]
}

func TestValid(t *testing.T) {
	for _, ref := range validBoards {
		b := clear(ref)
		t.Log(b)
		if err := b.Count(); err != nil {
			var _ error = err
			t.Fatalf("Count() returned %q, want nil.", err)
		}
		if res := b.String(); res != ref {
			t.Fatalf("Count() result: %s,\n  want:%s.", res, ref)
		}
	}
}

func TestBad(t *testing.T) {
	for _, ref := range badBoards {
		b := clear(ref)
		t.Log(b)
		if b.Count() == nil {
			t.Fatal("Count() returned nil, want error.")
		}
	}
}

func TestTestVersion(t *testing.T) {
	if testVersion != targetTestVersion {
		t.Errorf("Found testVersion = %v, want %v.", testVersion, targetTestVersion)
	}
}

func BenchmarkCount(b *testing.B) {
	m := clear(`
+------+
|1*22*1|
|12*322|
| 123*2|
|112*4*|
|1*22*2|
|111111|
+------+`)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m.Count()
	}
}
<commit_msg>minesweeper: Ensure test versioning consistency with other exercises (#527)<commit_after>package minesweeper

import (
	"bytes"
	"testing"
)

const targetTestVersion = 1

var validBoards = []string{
	// zero size board
	`
++
++`,

	// empty board
	`
+---+
|   |
|   |
|   |
+---+`,

	// board full of mines
	`
+---+
|***|
|***|
|***|
+---+`,

	// surrounded
	`
+---+
|***|
|*8*|
|***|
+---+`,

	// horizontal line
	`
+-----+
|1*2*1|
+-----+`,

	// vertical line
	`
+-+
|1|
|*|
|2|
|*|
|1|
+-+`,

	// cross
	`
+-----+
| 2*2 |
|25*52|
|*****|
|25*52|
| 2*2 |
+-----+`,

	// Large 6x6 board with 8 bombs
	`
+------+
|1*22*1|
|12*322|
| 123*2|
|112*4*|
|1*22*2|
|111111|
+------+`,

	// Large 5x5 board with 7 bombs
	`
+-----+
|1*2*1|
|11322|
| 12*2|
|12*4*|
|1*3*2|
+-----+`,

	// Small 1x5 board with 2 bombs
	`
+-+
|*|
|2|
|*|
|1|
| |
+-+`,

	// 1x1 sqaure with 1 bomb
	`
+-+
|*|
+-+`,

	// 2x2 square with 4 bombs
	`
+--+
|**|
|**|
+--+`,

	// 5x5 square with 2 bombs
	`
+-----+
|  111|
|  1*1|
|  111|
|111  |
|1*1  |
+-----+`,
}

var badBoards = []string{
	// Unaligned
	`
+-+
| |
|*  |
|  |
+-+`,

	// incomplete border
	`
+-----+
*   * |
+-- --+`,

	// Unknown characters
	`
+-----+
|X  * |
+-----+`,
}

// constructor.  take board as a string, return Board with digits cleared.
func clear(s string) Board {
	b := []byte(s)
	for i, c := range b {
		if c >= '0' && c <= '9' {
			b[i] = ' '
		}
	}
	return bytes.Split(b, []byte{'\n'})[1:]
}

func TestTestVersion(t *testing.T) {
	if testVersion != targetTestVersion {
		t.Fatalf("Found testVersion = %v, want %v.", testVersion, targetTestVersion)
	}
}

func TestValid(t *testing.T) {
	for _, ref := range validBoards {
		b := clear(ref)
		t.Log(b)
		if err := b.Count(); err != nil {
			var _ error = err
			t.Fatalf("Count() returned %q, want nil.", err)
		}
		if res := b.String(); res != ref {
			t.Fatalf("Count() result: %s,\n  want:%s.", res, ref)
		}
	}
}

func TestBad(t *testing.T) {
	for _, ref := range badBoards {
		b := clear(ref)
		t.Log(b)
		if b.Count() == nil {
			t.Fatal("Count() returned nil, want error.")
		}
	}
}

func BenchmarkCount(b *testing.B) {
	m := clear(`
+------+
|1*22*1|
|12*322|
| 123*2|
|112*4*|
|1*22*2|
|111111|
+------+`)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m.Count()
	}
}
<|endoftext|>


<commit_before>package docker

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"github.com/dotcloud/docker/registry"
	"os"
	"strings"
	"testing"
)

// FIXME: these tests have an external dependency on a staging index hosted
// on the docker.io infrastructure. That dependency should be removed.
// - Unit tests should have no side-effect dependencies.
// - Integration tests should have side-effects limited to the host environment being tested.

func TestLogin(t *testing.T) {
	os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com")
	defer os.Setenv("DOCKER_INDEX_URL", "")
	authConfig := &registry.AuthConfig{
		Username:      "unittester",
		Password:      "surlautrerivejetattendrai",
		Email:         "noise+unittester@docker.com",
		ServerAddress: "https://indexstaging-docker.dotcloud.com/v1/",
	}
	status, err := registry.Login(authConfig, nil)
	if err != nil {
		t.Fatal(err)
	}
	if status != "Login Succeeded" {
		t.Fatalf("Expected status \"Login Succeeded\", found \"%s\" instead", status)
	}
}

func TestCreateAccount(t *testing.T) {
	tokenBuffer := make([]byte, 16)
	_, err := rand.Read(tokenBuffer)
	if err != nil {
		t.Fatal(err)
	}
	token := hex.EncodeToString(tokenBuffer)[:12]
	username := "ut" + token
	authConfig := &registry.AuthConfig{
		Username:      username,
		Password:      "test42",
		Email:         fmt.Sprintf("docker-ut+%s@example.com", token),
		ServerAddress: "https://indexstaging-docker.dotcloud.com/v1/",
	}
	status, err := registry.Login(authConfig, nil)
	if err != nil {
		t.Fatal(err)
	}
	expectedStatus := fmt.Sprintf(
		"Account created. Please see the documentation of the registry %s for instructions how to activate it.",
		authConfig.ServerAddress,
	)
	if status != expectedStatus {
		t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status)
	}

	status, err = registry.Login(authConfig, nil)
	if err == nil {
		t.Fatalf("Expected error but found nil instead")
	}

	expectedError := "Login: Account is not Active"

	if !strings.Contains(err.Error(), expectedError) {
		t.Fatalf("Expected message \"%s\" but found \"%s\" instead", expectedError, err)
	}
}
<commit_msg>Skip login tests because of external dependency to a hosted service.<commit_after>package docker

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"github.com/dotcloud/docker/registry"
	"os"
	"strings"
	"testing"
)

// FIXME: these tests have an external dependency on a staging index hosted
// on the docker.io infrastructure. That dependency should be removed.
// - Unit tests should have no side-effect dependencies.
// - Integration tests should have side-effects limited to the host environment being tested.

func TestLogin(t *testing.T) {
	t.Skip("FIXME: please remove dependency on external services")
	os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com")
	defer os.Setenv("DOCKER_INDEX_URL", "")
	authConfig := &registry.AuthConfig{
		Username:      "unittester",
		Password:      "surlautrerivejetattendrai",
		Email:         "noise+unittester@docker.com",
		ServerAddress: "https://indexstaging-docker.dotcloud.com/v1/",
	}
	status, err := registry.Login(authConfig, nil)
	if err != nil {
		t.Fatal(err)
	}
	if status != "Login Succeeded" {
		t.Fatalf("Expected status \"Login Succeeded\", found \"%s\" instead", status)
	}
}

func TestCreateAccount(t *testing.T) {
	t.Skip("FIXME: please remove dependency on external services")
	tokenBuffer := make([]byte, 16)
	_, err := rand.Read(tokenBuffer)
	if err != nil {
		t.Fatal(err)
	}
	token := hex.EncodeToString(tokenBuffer)[:12]
	username := "ut" + token
	authConfig := &registry.AuthConfig{
		Username:      username,
		Password:      "test42",
		Email:         fmt.Sprintf("docker-ut+%s@example.com", token),
		ServerAddress: "https://indexstaging-docker.dotcloud.com/v1/",
	}
	status, err := registry.Login(authConfig, nil)
	if err != nil {
		t.Fatal(err)
	}
	expectedStatus := fmt.Sprintf(
		"Account created. Please see the documentation of the registry %s for instructions how to activate it.",
		authConfig.ServerAddress,
	)
	if status != expectedStatus {
		t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status)
	}

	status, err = registry.Login(authConfig, nil)
	if err == nil {
		t.Fatalf("Expected error but found nil instead")
	}

	expectedError := "Login: Account is not Active"

	if !strings.Contains(err.Error(), expectedError) {
		t.Fatalf("Expected message \"%s\" but found \"%s\" instead", expectedError, err)
	}
}
<|endoftext|>


<commit_before>package flotilla

import (
	"fmt"
	"io"
	"reflect"
	"time"

	"github.com/simulatedsimian/flotilla/dock"
	"github.com/tarm/serial"
)

// Event is a wrapper around a dock.Event that contains an additional Dock index
type Event struct {
	dock.Event
	dockIndex int
}

type Client struct {
	ports            []io.ReadWriteCloser
	docks            []*dock.Dock
	connectedModules map[ModuleAddress]Updateable
	requestedModules []Updateable
	eventChan        chan Event
	ticker           *time.Ticker
}

func structMembersToInterfaces(moduleStructPtr interface{}) (res []interface{}) {

	typeof := reflect.TypeOf(moduleStructPtr)

	if typeof.Kind() != reflect.Ptr || typeof.Elem().Kind() != reflect.Struct {
		panic("modules supplied to Client.AquireModules not a struct pointer")
	}

	fields := typeof.Elem().NumField()
	for i := 0; i < fields; i++ {
		iface := reflect.ValueOf(moduleStructPtr).Elem().Field(i).Addr().Interface()
		res = append(res, iface)
	}
	return
}

func (c *Client) AquireModules(moduleStructPtr interface{}) {

	modules := structMembersToInterfaces(moduleStructPtr)

	for _, m := range modules {
		module := reflect.ValueOf(m).Elem().FieldByName("Module")
		if module.IsValid() {
			if mod, ok := module.Addr().Interface().(*Module); ok {
				mod.client = c
				if t, ok := reflect.ValueOf(m).Interface().(Typed); ok {
					mod.moduleType = t.Type()
					c.requestedModules = append(c.requestedModules, mod)
				}
			}
		}
	}
}

func (c *Client) Run() error {
	for {
		err := c.waitForEvent()
		if err != nil {
			return err
		}
	}
}

func (c *Client) waitForEvent() error {
	if c.ticker != nil {
		select {
		case ev := <-c.eventChan:
			return c.handleEvent(ev)
		case t := <-c.ticker.C:
			c.handleTick(t)
		}
	} else {
		return c.handleEvent(<-c.eventChan)
	}
	return nil
}

func (c *Client) handleTick(t time.Time) {
}

func (c *Client) handleEvent(ev Event) error {

	if ev.EventType == dock.EventError {
		return ev.Error
	}

	addr := ModuleAddress{dock: ev.dockIndex, channel: ev.Channel}

	if m, ok := c.connectedModules[addr]; ok {
		m.Update(ev)
		if !m.Connected() {
			delete(c.connectedModules, addr)
		}
		return nil
	}

	if ev.EventType == dock.EventConnected {
		for _, m := range c.requestedModules {
			if !m.Connected() {
				m.Update(ev)
				if m.Connected() {
					c.connectedModules[addr] = m
					break
				}
			}
		}
	}
	return nil
}

func (c *Client) Close() {
	for _, p := range c.ports {
		p.Close()
	}
	c.docks = nil
}

func makeClient() *Client {
	client := Client{}
	client.eventChan = make(chan Event, 100)
	client.connectedModules = make(map[ModuleAddress]Updateable)
	return &client
}

func ConnectToDock(serialport string) (*Client, error) {
	return ConnectToDocks(serialport)
}

func ConnectToDocks(serialports ...string) (*Client, error) {
	if len(serialports) == 0 {
		return nil, fmt.Errorf("ConnectToDocks: No Serial Ports supplied")
	}

	ports := []io.ReadWriteCloser{}

	for i, s := range serialports {
		serialcfg := serial.Config{Name: s, Baud: 115200}
		port, err := serial.OpenPort(&serialcfg)
		if err != nil {
			for _, p := range ports {
				p.Close()
			}
			return nil, fmt.Errorf("Failed to connect to Dock %d (%s): %v", i, s, err)
		}
		ports = append(ports, port)
	}

	return ConnectToDocksRaw(ports...)
}

func ConnectToDocksRaw(ports ...io.ReadWriteCloser) (*Client, error) {
	client := makeClient()

	for _, port := range ports {
		client.ports = append(client.ports, port)
		client.docks = append(client.docks, dock.ConnectDock(port))
	}

	// create a go routine for each dock that reads the event and gives it to the
	// common client event chan along with source dock index
	for i, d := range client.docks {
		go func(d *dock.Dock, dockIndex int) {
			for {
				ev := <-d.Events
				client.eventChan <- Event{ev, dockIndex}
				if ev.EventType == dock.EventError {
					return
				}
			}
		}(d, i)
	}

	return client, nil
}

func FindDocks() (*Client, error) {
	return nil, fmt.Errorf("Find Docks Not Implemented Yet")
}
<commit_msg>added waitForModules<commit_after>package flotilla

import (
	"fmt"
	"io"
	"reflect"
	"time"

	"github.com/simulatedsimian/flotilla/dock"
	"github.com/tarm/serial"
)

// Event is a wrapper around a dock.Event that contains an additional Dock index
type Event struct {
	dock.Event
	dockIndex int
}

type Client struct {
	ports            []io.ReadWriteCloser
	docks            []*dock.Dock
	connectedModules map[ModuleAddress]Updateable
	requestedModules []Updateable
	eventChan        chan Event
	ticker           *time.Ticker
}

func structMembersToInterfaces(moduleStructPtr interface{}) (res []interface{}) {

	typeof := reflect.TypeOf(moduleStructPtr)

	if typeof.Kind() != reflect.Ptr || typeof.Elem().Kind() != reflect.Struct {
		panic("modules supplied to Client.AquireModules not a struct pointer")
	}

	fields := typeof.Elem().NumField()
	for i := 0; i < fields; i++ {
		iface := reflect.ValueOf(moduleStructPtr).Elem().Field(i).Addr().Interface()
		res = append(res, iface)
	}
	return
}

func (c *Client) AquireModules(moduleStructPtr interface{}) {

	modules := structMembersToInterfaces(moduleStructPtr)

	for _, m := range modules {
		module := reflect.ValueOf(m).Elem().FieldByName("Module")
		if module.IsValid() {
			if mod, ok := module.Addr().Interface().(*Module); ok {
				mod.client = c
				if t, ok := reflect.ValueOf(m).Interface().(Typed); ok {
					mod.moduleType = t.Type()
					c.requestedModules = append(c.requestedModules, mod)
				}
			}
		}
	}
}

func (c *Client) WaitForModules(moduleStructPtr interface{}) {
	modules := structMembersToInterfaces(moduleStructPtr)

	for {
		for _, d := range c.docks {
			d.SendDockCommand('e')
		}
		time.Sleep(500 * time.Millisecond)
		unconnected := false
		for _, m := range modules {
			if u, ok := reflect.ValueOf(m).Interface().(Updateable); ok {
				if !u.Connected() {
					unconnected = true
				}
			}
		}
		if !unconnected {
			return
		}
	}
}

func (c *Client) Run() error {
	for {
		err := c.waitForEvent()
		if err != nil {
			return err
		}
	}
}

func (c *Client) waitForEvent() error {
	if c.ticker != nil {
		select {
		case ev := <-c.eventChan:
			return c.handleEvent(ev)
		case t := <-c.ticker.C:
			c.handleTick(t)
		}
	} else {
		return c.handleEvent(<-c.eventChan)
	}
	return nil
}

func (c *Client) handleTick(t time.Time) {
}

func (c *Client) handleEvent(ev Event) error {

	if ev.EventType == dock.EventError {
		return ev.Error
	}

	addr := ModuleAddress{dock: ev.dockIndex, channel: ev.Channel}

	if m, ok := c.connectedModules[addr]; ok {
		m.Update(ev)
		if !m.Connected() {
			delete(c.connectedModules, addr)
		}
		return nil
	}

	if ev.EventType == dock.EventConnected {
		for _, m := range c.requestedModules {
			if !m.Connected() {
				m.Update(ev)
				if m.Connected() {
					c.connectedModules[addr] = m
					break
				}
			}
		}
	}
	return nil
}

func (c *Client) Close() {
	for _, p := range c.ports {
		p.Close()
	}
	c.docks = nil
}

func makeClient() *Client {
	client := Client{}
	client.eventChan = make(chan Event, 100)
	client.connectedModules = make(map[ModuleAddress]Updateable)
	return &client
}

func ConnectToDock(serialport string) (*Client, error) {
	return ConnectToDocks(serialport)
}

func ConnectToDocks(serialports ...string) (*Client, error) {
	if len(serialports) == 0 {
		return nil, fmt.Errorf("ConnectToDocks: No Serial Ports supplied")
	}

	ports := []io.ReadWriteCloser{}

	for i, s := range serialports {
		serialcfg := serial.Config{Name: s, Baud: 115200}
		port, err := serial.OpenPort(&serialcfg)
		if err != nil {
			for _, p := range ports {
				p.Close()
			}
			return nil, fmt.Errorf("Failed to connect to Dock %d (%s): %v", i, s, err)
		}
		ports = append(ports, port)
	}

	return ConnectToDocksRaw(ports...)
}

func ConnectToDocksRaw(ports ...io.ReadWriteCloser) (*Client, error) {
	client := makeClient()

	for _, port := range ports {
		client.ports = append(client.ports, port)
		client.docks = append(client.docks, dock.ConnectDock(port))
	}

	// create a go routine for each dock that reads the event and gives it to the
	// common client event chan along with source dock index
	for i, d := range client.docks {
		go func(d *dock.Dock, dockIndex int) {
			for {
				ev := <-d.Events
				client.eventChan <- Event{ev, dockIndex}
				if ev.EventType == dock.EventError {
					return
				}
			}
		}(d, i)
	}

	return client, nil
}

func FindDocks() (*Client, error) {
	return nil, fmt.Errorf("Find Docks Not Implemented Yet")
}
<|endoftext|>


<commit_before>package json

import (
	"errors"

	"github.com/arschles/flexwork"
)

type Err error

func NewErr(str string) Err {
	return Err(errors.New(str))
}

var (
	ErrFormatting   = NewErr("json_formatting")
	ErrDB           = NewErr("database")
	ErrUnauthorized = NewErr("unauthorized")
	ErrBadRequest   = NewErr("bad request")
	ErrInternal     = NewErr("internal_error")
	ErrNotFound     = NewErr("not_found")
)

const (
	jsonEncodeErr = `{
    "error":"json",
    "msg":"couldn't encode JSON indicating the error"
  }`
)

type ErrorJSON struct {
	Type    string `json:"error"`
	Message string `json:"msg"`
}

// Error returns a flexwork.Response that encodes a JSON error response
func Error(code int, eType Err, msg string) flexwork.Response {
	err := ErrorJSON{Type: eType.Error(), Message: msg}
	return Encode(code, err)
}
<commit_msg>json cleanup<commit_after>package json

import "github.com/arschles/flexwork"

// ErrorJSON is an error to be encoded to JSON and returned. See constants in this package
// for some commonly used ones
type ErrorJSON struct {
	Code    int    `json:"-"`
	Type    string `json:"error"`
	Message string `json:"msg"`
}

const jsonEncodeErr = `{
  "error":"json",
  "msg":"couldn't encode JSON indicating the error"
}`

// Error returns a flexwork.Response that encodes a JSON error response
func Error(err ErrorJSON) flexwork.Response {
	return Encode(err.Code, err)
}
<|endoftext|>