<commit_before>package mgr

import (
	"errors"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"encoding/json"

	"github.com/qiniu/log"
	"github.com/qiniu/logkit/conf"
	"github.com/qiniu/logkit/metric"
	"github.com/qiniu/logkit/reader"
	"github.com/qiniu/logkit/sender"
	"github.com/qiniu/logkit/transforms"
	"github.com/qiniu/logkit/utils"
)

const (
	KeyMetricType = "type"
)

const (
	defaultCollectInterval = 3
)

type MetricConfig struct {
	MetricType string                 `json:"type"`
	Attributes map[string]bool        `json:"attributes"`
	Config     map[string]interface{} `json:"config"`
}

type MetricRunner struct {
	RunnerName string `json:"name"`

	collectors   []metric.Collector
	transformers []transforms.Transformer
	senders      []sender.Sender

	collectInterval time.Duration
	rs              RunnerStatus
	lastRs          RunnerStatus
	rsMutex         *sync.RWMutex
	meta            *reader.Meta
	lastSend        time.Time
	stopped         int32
	exitChan        chan struct{}
}

func NewMetric(tp string) (metric.Collector, error) {
	if c, ok := metric.Collectors[tp]; ok {
		return c(), nil
	}
	return nil, fmt.Errorf("Metric <%v> is not support now", tp)
}

func NewMetricRunner(rc RunnerConfig, sr *sender.SenderRegistry) (runner *MetricRunner, err error) {
	if rc.CollectInterval <= 0 {
		rc.CollectInterval = defaultCollectInterval
	}
	interval := time.Duration(rc.CollectInterval) * time.Second
	meta, err := reader.NewMetaWithConf(conf.MapConf{
		utils.GlobalKeyName:  rc.RunnerName,
		reader.KeyRunnerName: rc.RunnerName,
		reader.KeyMode:       reader.ModeMetrics,
	})
	if err != nil {
		return nil, fmt.Errorf("Runner "+rc.RunnerName+" add failed, err is %v", err)
	}
	for i := range rc.SenderConfig {
		rc.SenderConfig[i][sender.KeyRunnerName] = rc.RunnerName
	}
	collectors := make([]metric.Collector, 0)
	transformers := make([]transforms.Transformer, 0)
	if len(rc.MetricConfig) == 0 {
		return nil, fmt.Errorf("Runner " + rc.RunnerName + " has zero metric, ignore it")
	}
	for _, m := range rc.MetricConfig {
		tp := m.MetricType
		c, err := NewMetric(tp)
		if err != nil {
			log.Errorf("%v ignore it...", err)
			err = nil
			continue
		}
		configBytes, err := json.Marshal(m.Config)
		if err != nil {
			return nil, fmt.Errorf("metric %v marshal config error %v", tp, err)
		}
		err = json.Unmarshal(configBytes, c)
		if err != nil {
			return nil, fmt.Errorf("metric %v unmarshal config error %v", tp, err)
		}
		collectors = append(collectors, c)

		// 配置文件中明确标明 false 的 attr 加入 discard transformer
		config := c.Config()
		if attributes, ex := config[metric.AttributesString]; ex {
			if attrs, ok := attributes.([]utils.KeyValue); ok {
				for _, attr := range attrs {
					val, exist := m.Attributes[attr.Key]
					if exist && !val {
						trans, err := createDiscardTransformer(attr.Key)
						if err != nil {
							return nil, fmt.Errorf("metric %v key %v, transform add failed, %v", tp, attr.Key, err)
						}
						transformers = append(transformers, trans)
					}
				}
			}
		}
	}
	if len(collectors) < 1 {
		err = errors.New("no collectors were added")
		return
	}

	senders := make([]sender.Sender, 0)
	for _, c := range rc.SenderConfig {
		s, err := sr.NewSender(c, meta.FtSaveLogPath())
		if err != nil {
			return nil, err
		}
		senders = append(senders, s)
	}
	runner = &MetricRunner{
		RunnerName: rc.RunnerName,
		exitChan:   make(chan struct{}),
		lastSend:   time.Now(), // 上一次发送时间
		meta:       meta,
		rs: RunnerStatus{
			ReaderStats: utils.StatsInfo{},
			SenderStats: make(map[string]utils.StatsInfo),
			lastState:   time.Now(),
			Name:        rc.RunnerName,
		},
		lastRs: RunnerStatus{
			ReaderStats: utils.StatsInfo{},
			SenderStats: make(map[string]utils.StatsInfo),
			lastState:   time.Now(),
			Name:        rc.RunnerName,
		},
		rsMutex:         new(sync.RWMutex),
		collectInterval: interval,
		collectors:      collectors,
		transformers:    transformers,
		senders:         senders,
	}
	runner.StatusRestore()
	return
}

func (mr *MetricRunner) Name() string {
	return mr.RunnerName
}

func (r *MetricRunner) Run() {
	defer close(r.exitChan)
	for {
		if atomic.LoadInt32(&r.stopped) > 0 {
			log.Debugf("runner %v exited from run", r.RunnerName)
			r.exitChan <- struct{}{}
			return
		}
		dataCnt := 0
		datas := make([]sender.Data, 0)
		// collect data
		for _, c := range r.collectors {
			tmpdatas, err := c.Collect()
			if err != nil {
				log.Errorf("collecter <%v> collect data error: %v", c.Name(), err)
				continue
			}
			for i := range tmpdatas {
				if len(tmpdatas[i]) > 0 {
					dataCnt++
					datas = append(datas, tmpdatas[i])
				}
			}
			if len(tmpdatas) < 1 {
				log.Debugf("MetricRunner %v collect No data", c.Name())
			}
		}
		r.rsMutex.Lock()
		r.rs.ReadDataCount += int64(dataCnt)
		r.rsMutex.Unlock()
		r.lastSend = time.Now()
		if len(datas) <= 0 {
			log.Debug("MetricRunner collect No data")
			time.Sleep(r.collectInterval)
			continue
		}
		var err error
		for i := range r.transformers {
			datas, err = r.transformers[i].Transform(datas)
			if err != nil {
				log.Error(err)
			}
		}
		if len(datas) <= 0 {
			log.Warnf("data has been cleared by transformer")
			time.Sleep(r.collectInterval)
			continue
		}
		for _, s := range r.senders {
			if !r.trySend(s, datas, 3) {
				log.Errorf("failed to send metricData: << %v >>", datas)
				break
			}
		}
		time.Sleep(r.collectInterval)
	}
}

// trySend 尝试发送数据，如果此时runner退出返回false，其他情况无论是达到最大重试次数还是发送成功，都返回true
func (r *MetricRunner) trySend(s sender.Sender, datas []sender.Data, times int) bool {
	if len(datas) <= 0 {
		return true
	}
	if _, ok := r.rs.SenderStats[s.Name()]; !ok {
		r.rs.SenderStats[s.Name()] = utils.StatsInfo{}
	}
	r.rsMutex.RLock()
	info := r.rs.SenderStats[s.Name()]
	r.rsMutex.RUnlock()
	cnt := 1
	for {
		// 至少尝试一次。如果任务已经停止，那么只尝试一次
		if cnt > 1 && atomic.LoadInt32(&r.stopped) > 0 {
			return false
		}
		err := s.Send(datas)
		if se, ok := err.(*utils.StatsError); ok {
			err = se.ErrorDetail
			if se.Ft {
				info.Errors = se.Errors
				info.Success = se.Success
				r.rs.Lag.Ftlags = se.Ftlag
			} else {
				if cnt > 1 {
					info.Errors -= se.Success
				} else {
					info.Errors += se.Errors
				}
				info.Success += se.Success
			}
		} else if err != nil {
			if cnt <= 1 {
				info.Errors += int64(len(datas))
			}
		} else {
			info.Success += int64(len(datas))
		}
		if err != nil {
			log.Error(err)
			time.Sleep(time.Second)
			if times <= 0 || cnt < times {
				cnt++
				continue
			}
			log.Errorf("retry send %v times, but still error %v, discard datas %v ... total %v lines", cnt, err, datas[0], len(datas))
		}
		break
	}
	r.rsMutex.Lock()
	r.rs.SenderStats[s.Name()] = info
	r.rsMutex.Unlock()
	return true
}

func (mr *MetricRunner) Stop() {
	atomic.AddInt32(&mr.stopped, 1)

	log.Warnf("wait for MetricRunner " + mr.Name() + " stopped")
	timer := time.NewTimer(time.Second * 10)
	select {
	case <-mr.exitChan:
		log.Warnf("MetricRunner " + mr.Name() + " has been stopped ")
	case <-timer.C:
		log.Warnf("MetricRunner " + mr.Name() + " exited timeout ")
	}
	for _, s := range mr.senders {
		err := s.Close()
		if err != nil {
			log.Errorf("cannot close sender name: %s, err: %v", s.Name(), err)
		} else {
			log.Warnf("sender %v of MetricRunner %v closed", s.Name(), mr.Name())
		}
	}
}

func (mr *MetricRunner) Reset() error {
	var errMsg string
	err := mr.meta.Reset()
	if err != nil {
		errMsg += err.Error() + "\n"
	}
	for _, sd := range mr.senders {
		ssd, ok := sd.(Resetable)
		if ok {
			if nerr := ssd.Reset(); nerr != nil {
				errMsg += err.Error() + "\n"
			}
		}
	}
	return errors.New(errMsg)
}

func (_ *MetricRunner) Cleaner() CleanInfo {
	return CleanInfo{
		enable: false,
	}
}

func (mr *MetricRunner) Status() RunnerStatus {
	mr.rsMutex.Lock()
	defer mr.rsMutex.Unlock()
	durationTime := float64(mr.collectInterval.Seconds())
	mr.rs.ReadSpeed = float64(mr.rs.ReadDataCount-mr.lastRs.ReadDataCount) / durationTime
	mr.rs.ReadSpeedTrend = getTrend(mr.lastRs.ReadSpeed, mr.rs.ReadSpeed)

	for i := range mr.senders {
		sts, ok := mr.senders[i].(sender.StatsSender)
		if ok {
			mr.rs.SenderStats[mr.senders[i].Name()] = sts.Stats()
		}
	}

	for k, v := range mr.rs.SenderStats {
		if lv, ok := mr.lastRs.SenderStats[k]; ok {
			v.Speed, v.Trend = calcSpeedTrend(lv, v, durationTime)
		} else {
			v.Speed, v.Trend = calcSpeedTrend(utils.StatsInfo{}, v, durationTime)
		}
		mr.rs.SenderStats[k] = v
	}
	copyRunnerStatus(&mr.lastRs, &mr.rs)
	return mr.rs
}

func (mr *MetricRunner) StatusRestore() {
	rStat, err := mr.meta.ReadStatistic()

	if err != nil {
		log.Warnf("runner %v, restore status failed", mr.RunnerName)
		return
	}
	mr.rs.ReadDataCount = rStat.ReaderCnt
	mr.rs.ParserStats.Success = rStat.ParserCnt[0]
	mr.rs.ParserStats.Errors = rStat.ParserCnt[1]
	for _, s := range mr.senders {
		name := s.Name()
		info, exist := rStat.SenderCnt[name]
		if !exist {
			continue
		}
		sStatus, ok := s.(sender.StatsSender)
		if ok {
			sStatus.Restore(&utils.StatsInfo{
				Success: info[0],
				Errors:  info[1],
			})
		}
		status, ext := mr.rs.SenderStats[name]
		if !ext {
			status = utils.StatsInfo{}
		}
		status.Success = info[0]
		status.Errors = info[1]
		mr.rs.SenderStats[name] = status
	}
	copyRunnerStatus(&mr.lastRs, &mr.rs)
	log.Infof("runner %v restore status %v", mr.RunnerName, rStat)
}

func (mr *MetricRunner) StatusBackup() {
	status := mr.Status()
	bStart := &reader.Statistic{
		ReaderCnt: status.ReadDataCount,
		ParserCnt: [2]int64{
			status.ParserStats.Success,
			status.ParserStats.Errors,
		},
		SenderCnt: map[string][2]int64{},
	}
	for _, s := range mr.senders {
		name := s.Name()
		sStatus, ok := s.(sender.StatsSender)
		if ok {
			status.SenderStats[name] = sStatus.Stats()
		}
		if sta, exist := status.SenderStats[name]; exist {
			bStart.SenderCnt[name] = [2]int64{
				sta.Success,
				sta.Errors,
			}
		}
	}
	err := mr.meta.WriteStatistic(bStart)
	if err != nil {
		log.Warnf("runner %v, backup status failed", mr.RunnerName)
	} else {
		log.Infof("runner %v, backup status %v", mr.RunnerName, bStart)
	}
}

func createDiscardTransformer(key string) (transforms.Transformer, error) {
	strTP := "discard"
	creater, ok := transforms.Transformers[strTP]
	if !ok {
		return nil, fmt.Errorf("type %v of transformer not exist", strTP)
	}
	tConf := map[string]string{
		"key":   key,
		"type":  strTP,
		"stage": "after_parser",
	}
	trans := creater()
	bts, err := json.Marshal(tConf)
	if err != nil {
		return nil, fmt.Errorf("type %v of transformer marshal config error %v", strTP, err)
	}
	err = json.Unmarshal(bts, trans)
	if err != nil {
		return nil, fmt.Errorf("type %v of transformer unmarshal config error %v", strTP, err)
	}
	return trans, nil
}
<commit_msg>为 metrics 收集的信息增加时间字段<commit_after>package mgr

import (
	"errors"
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"encoding/json"

	"github.com/qiniu/log"
	"github.com/qiniu/logkit/conf"
	"github.com/qiniu/logkit/metric"
	"github.com/qiniu/logkit/reader"
	"github.com/qiniu/logkit/sender"
	"github.com/qiniu/logkit/transforms"
	"github.com/qiniu/logkit/utils"
)

const (
	KeyMetricType = "type"
	KeyMetricTime = "timestamp"
)

const (
	defaultCollectInterval = 3
)

type MetricConfig struct {
	MetricType string                 `json:"type"`
	Attributes map[string]bool        `json:"attributes"`
	Config     map[string]interface{} `json:"config"`
}

type MetricRunner struct {
	RunnerName string `json:"name"`

	collectors   []metric.Collector
	transformers []transforms.Transformer
	senders      []sender.Sender

	collectInterval time.Duration
	rs              RunnerStatus
	lastRs          RunnerStatus
	rsMutex         *sync.RWMutex
	meta            *reader.Meta
	lastSend        time.Time
	stopped         int32
	exitChan        chan struct{}
}

func NewMetric(tp string) (metric.Collector, error) {
	if c, ok := metric.Collectors[tp]; ok {
		return c(), nil
	}
	return nil, fmt.Errorf("Metric <%v> is not support now", tp)
}

func NewMetricRunner(rc RunnerConfig, sr *sender.SenderRegistry) (runner *MetricRunner, err error) {
	if rc.CollectInterval <= 0 {
		rc.CollectInterval = defaultCollectInterval
	}
	interval := time.Duration(rc.CollectInterval) * time.Second
	meta, err := reader.NewMetaWithConf(conf.MapConf{
		utils.GlobalKeyName:  rc.RunnerName,
		reader.KeyRunnerName: rc.RunnerName,
		reader.KeyMode:       reader.ModeMetrics,
	})
	if err != nil {
		return nil, fmt.Errorf("Runner "+rc.RunnerName+" add failed, err is %v", err)
	}
	for i := range rc.SenderConfig {
		rc.SenderConfig[i][sender.KeyRunnerName] = rc.RunnerName
	}
	collectors := make([]metric.Collector, 0)
	transformers := make([]transforms.Transformer, 0)
	if len(rc.MetricConfig) == 0 {
		return nil, fmt.Errorf("Runner " + rc.RunnerName + " has zero metric, ignore it")
	}
	for _, m := range rc.MetricConfig {
		tp := m.MetricType
		c, err := NewMetric(tp)
		if err != nil {
			log.Errorf("%v ignore it...", err)
			err = nil
			continue
		}
		configBytes, err := json.Marshal(m.Config)
		if err != nil {
			return nil, fmt.Errorf("metric %v marshal config error %v", tp, err)
		}
		err = json.Unmarshal(configBytes, c)
		if err != nil {
			return nil, fmt.Errorf("metric %v unmarshal config error %v", tp, err)
		}
		collectors = append(collectors, c)

		// 配置文件中明确标明 false 的 attr 加入 discard transformer
		config := c.Config()
		if attributes, ex := config[metric.AttributesString]; ex {
			if attrs, ok := attributes.([]utils.KeyValue); ok {
				for _, attr := range attrs {
					val, exist := m.Attributes[attr.Key]
					if exist && !val {
						trans, err := createDiscardTransformer(attr.Key)
						if err != nil {
							return nil, fmt.Errorf("metric %v key %v, transform add failed, %v", tp, attr.Key, err)
						}
						transformers = append(transformers, trans)
					}
				}
			}
		}
	}
	if len(collectors) < 1 {
		err = errors.New("no collectors were added")
		return
	}

	senders := make([]sender.Sender, 0)
	for _, c := range rc.SenderConfig {
		s, err := sr.NewSender(c, meta.FtSaveLogPath())
		if err != nil {
			return nil, err
		}
		senders = append(senders, s)
	}
	runner = &MetricRunner{
		RunnerName: rc.RunnerName,
		exitChan:   make(chan struct{}),
		lastSend:   time.Now(), // 上一次发送时间
		meta:       meta,
		rs: RunnerStatus{
			ReaderStats: utils.StatsInfo{},
			SenderStats: make(map[string]utils.StatsInfo),
			lastState:   time.Now(),
			Name:        rc.RunnerName,
		},
		lastRs: RunnerStatus{
			ReaderStats: utils.StatsInfo{},
			SenderStats: make(map[string]utils.StatsInfo),
			lastState:   time.Now(),
			Name:        rc.RunnerName,
		},
		rsMutex:         new(sync.RWMutex),
		collectInterval: interval,
		collectors:      collectors,
		transformers:    transformers,
		senders:         senders,
	}
	runner.StatusRestore()
	return
}

func (mr *MetricRunner) Name() string {
	return mr.RunnerName
}

func (r *MetricRunner) Run() {
	defer close(r.exitChan)
	for {
		if atomic.LoadInt32(&r.stopped) > 0 {
			log.Debugf("runner %v exited from run", r.RunnerName)
			r.exitChan <- struct{}{}
			return
		}
		dataCnt := 0
		datas := make([]sender.Data, 0)
		// collect data
		for _, c := range r.collectors {
			tmpdatas, err := c.Collect()
			now := time.Now().Format(time.RFC3339)
			if err != nil {
				log.Errorf("collecter <%v> collect data error: %v", c.Name(), err)
				continue
			}
			for i := range tmpdatas {
				if len(tmpdatas[i]) > 0 {
					dataCnt++
					tmpdatas[i][KeyMetricTime] = now
					datas = append(datas, tmpdatas[i])
				}
			}
			if len(tmpdatas) < 1 {
				log.Debugf("MetricRunner %v collect No data", c.Name())
			}
		}
		r.rsMutex.Lock()
		r.rs.ReadDataCount += int64(dataCnt)
		r.rsMutex.Unlock()
		r.lastSend = time.Now()
		if len(datas) <= 0 {
			log.Debug("MetricRunner collect No data")
			time.Sleep(r.collectInterval)
			continue
		}
		var err error
		for i := range r.transformers {
			datas, err = r.transformers[i].Transform(datas)
			if err != nil {
				log.Error(err)
			}
		}
		if len(datas) <= 0 {
			log.Warnf("data has been cleared by transformer")
			time.Sleep(r.collectInterval)
			continue
		}
		for _, s := range r.senders {
			if !r.trySend(s, datas, 3) {
				log.Errorf("failed to send metricData: << %v >>", datas)
				break
			}
		}
		time.Sleep(r.collectInterval)
	}
}

// trySend 尝试发送数据，如果此时runner退出返回false，其他情况无论是达到最大重试次数还是发送成功，都返回true
func (r *MetricRunner) trySend(s sender.Sender, datas []sender.Data, times int) bool {
	if len(datas) <= 0 {
		return true
	}
	if _, ok := r.rs.SenderStats[s.Name()]; !ok {
		r.rs.SenderStats[s.Name()] = utils.StatsInfo{}
	}
	r.rsMutex.RLock()
	info := r.rs.SenderStats[s.Name()]
	r.rsMutex.RUnlock()
	cnt := 1
	for {
		// 至少尝试一次。如果任务已经停止，那么只尝试一次
		if cnt > 1 && atomic.LoadInt32(&r.stopped) > 0 {
			return false
		}
		err := s.Send(datas)
		if se, ok := err.(*utils.StatsError); ok {
			err = se.ErrorDetail
			if se.Ft {
				info.Errors = se.Errors
				info.Success = se.Success
				r.rs.Lag.Ftlags = se.Ftlag
			} else {
				if cnt > 1 {
					info.Errors -= se.Success
				} else {
					info.Errors += se.Errors
				}
				info.Success += se.Success
			}
		} else if err != nil {
			if cnt <= 1 {
				info.Errors += int64(len(datas))
			}
		} else {
			info.Success += int64(len(datas))
		}
		if err != nil {
			log.Error(err)
			time.Sleep(time.Second)
			if times <= 0 || cnt < times {
				cnt++
				continue
			}
			log.Errorf("retry send %v times, but still error %v, discard datas %v ... total %v lines", cnt, err, datas[0], len(datas))
		}
		break
	}
	r.rsMutex.Lock()
	r.rs.SenderStats[s.Name()] = info
	r.rsMutex.Unlock()
	return true
}

func (mr *MetricRunner) Stop() {
	atomic.AddInt32(&mr.stopped, 1)

	log.Warnf("wait for MetricRunner " + mr.Name() + " stopped")
	timer := time.NewTimer(time.Second * 10)
	select {
	case <-mr.exitChan:
		log.Warnf("MetricRunner " + mr.Name() + " has been stopped ")
	case <-timer.C:
		log.Warnf("MetricRunner " + mr.Name() + " exited timeout ")
	}
	for _, s := range mr.senders {
		err := s.Close()
		if err != nil {
			log.Errorf("cannot close sender name: %s, err: %v", s.Name(), err)
		} else {
			log.Warnf("sender %v of MetricRunner %v closed", s.Name(), mr.Name())
		}
	}
}

func (mr *MetricRunner) Reset() error {
	var errMsg string
	err := mr.meta.Reset()
	if err != nil {
		errMsg += err.Error() + "\n"
	}
	for _, sd := range mr.senders {
		ssd, ok := sd.(Resetable)
		if ok {
			if nerr := ssd.Reset(); nerr != nil {
				errMsg += err.Error() + "\n"
			}
		}
	}
	return errors.New(errMsg)
}

func (_ *MetricRunner) Cleaner() CleanInfo {
	return CleanInfo{
		enable: false,
	}
}

func (mr *MetricRunner) Status() RunnerStatus {
	mr.rsMutex.Lock()
	defer mr.rsMutex.Unlock()
	durationTime := float64(mr.collectInterval.Seconds())
	mr.rs.ReadSpeed = float64(mr.rs.ReadDataCount-mr.lastRs.ReadDataCount) / durationTime
	mr.rs.ReadSpeedTrend = getTrend(mr.lastRs.ReadSpeed, mr.rs.ReadSpeed)

	for i := range mr.senders {
		sts, ok := mr.senders[i].(sender.StatsSender)
		if ok {
			mr.rs.SenderStats[mr.senders[i].Name()] = sts.Stats()
		}
	}

	for k, v := range mr.rs.SenderStats {
		if lv, ok := mr.lastRs.SenderStats[k]; ok {
			v.Speed, v.Trend = calcSpeedTrend(lv, v, durationTime)
		} else {
			v.Speed, v.Trend = calcSpeedTrend(utils.StatsInfo{}, v, durationTime)
		}
		mr.rs.SenderStats[k] = v
	}
	copyRunnerStatus(&mr.lastRs, &mr.rs)
	return mr.rs
}

func (mr *MetricRunner) StatusRestore() {
	rStat, err := mr.meta.ReadStatistic()

	if err != nil {
		log.Warnf("runner %v, restore status failed", mr.RunnerName)
		return
	}
	mr.rs.ReadDataCount = rStat.ReaderCnt
	mr.rs.ParserStats.Success = rStat.ParserCnt[0]
	mr.rs.ParserStats.Errors = rStat.ParserCnt[1]
	for _, s := range mr.senders {
		name := s.Name()
		info, exist := rStat.SenderCnt[name]
		if !exist {
			continue
		}
		sStatus, ok := s.(sender.StatsSender)
		if ok {
			sStatus.Restore(&utils.StatsInfo{
				Success: info[0],
				Errors:  info[1],
			})
		}
		status, ext := mr.rs.SenderStats[name]
		if !ext {
			status = utils.StatsInfo{}
		}
		status.Success = info[0]
		status.Errors = info[1]
		mr.rs.SenderStats[name] = status
	}
	copyRunnerStatus(&mr.lastRs, &mr.rs)
	log.Infof("runner %v restore status %v", mr.RunnerName, rStat)
}

func (mr *MetricRunner) StatusBackup() {
	status := mr.Status()
	bStart := &reader.Statistic{
		ReaderCnt: status.ReadDataCount,
		ParserCnt: [2]int64{
			status.ParserStats.Success,
			status.ParserStats.Errors,
		},
		SenderCnt: map[string][2]int64{},
	}
	for _, s := range mr.senders {
		name := s.Name()
		sStatus, ok := s.(sender.StatsSender)
		if ok {
			status.SenderStats[name] = sStatus.Stats()
		}
		if sta, exist := status.SenderStats[name]; exist {
			bStart.SenderCnt[name] = [2]int64{
				sta.Success,
				sta.Errors,
			}
		}
	}
	err := mr.meta.WriteStatistic(bStart)
	if err != nil {
		log.Warnf("runner %v, backup status failed", mr.RunnerName)
	} else {
		log.Infof("runner %v, backup status %v", mr.RunnerName, bStart)
	}
}

func createDiscardTransformer(key string) (transforms.Transformer, error) {
	strTP := "discard"
	creater, ok := transforms.Transformers[strTP]
	if !ok {
		return nil, fmt.Errorf("type %v of transformer not exist", strTP)
	}
	tConf := map[string]string{
		"key":   key,
		"type":  strTP,
		"stage": "after_parser",
	}
	trans := creater()
	bts, err := json.Marshal(tConf)
	if err != nil {
		return nil, fmt.Errorf("type %v of transformer marshal config error %v", strTP, err)
	}
	err = json.Unmarshal(bts, trans)
	if err != nil {
		return nil, fmt.Errorf("type %v of transformer unmarshal config error %v", strTP, err)
	}
	return trans, nil
}
<|endoftext|>


<commit_before>package imgscale

import (
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strings"
)

/*
	Prefix: use in the url to identify the format

	Height: the target height

	Ratio: zero for keeping ratio

	Thumbnail: true to use thumbnail feature in imagemagick, it is quick and optimized but you loose meta data

*/
type Format struct {
	Prefix    string
	Height    uint
	Ratio     float64
	Thumbnail bool
	Watermark bool
}

/*
	Path: path of the folder which contains images

	Prefix: use as image path indicator in url

	Separator: separator between format prefix and filename

	Formats: list of Format

	Exts: allow extensions, only jpg, jpeg and png available. Be aware that jpg and jpeg handled as different extension.

	Comment: will store in meta data if specified

	AutoRotate: Autorotate image according to orientation stored in the image meta data

*/
type Config struct {
	Path    string
	Prefix  string
	Separator string
	Formats []*Format
	Exts    []string
	Comment string
	AutoRotate bool
	Watermark *Watermark
}

func configure(config *Config) *handler {
	for _, ext := range config.Exts {
		if _, ok := supportedExts[ext]; !ok {
			panic(fmt.Sprintf("Extension '%s' not supported", ext))
		}
	}

	prefixes := make([]string, len(config.Formats))
	formats := make(map[string]*Format)
	for i, format := range config.Formats {
		if _, exists := formats[format.Prefix]; exists {
			panic(fmt.Sprintf("You can not have same prefix '%s' for 2 different formats", format.Prefix))
		}
		prefixes[i] = format.Prefix
		formats[format.Prefix] = format
	}
	var separator string

	if config.Separator != "" {
		separator = config.Separator
	} else {
		separator = "/"
	}
	path := fmt.Sprintf("^/%s/(?P<format>%s)%s(?P<filename>.+)\\.(?i)(?P<ext>%s)$", config.Prefix, strings.Join(prefixes, "|"), separator, strings.Join(config.Exts, "|"))
	h := handler{formats: formats, config: config, regexp: regexp.MustCompile(path), supportedExts: supportedExts}
	h.SetValidator(defaultValidator{})

	if config.Watermark != nil && config.Watermark.Filename != "" {
		config.Watermark.load()
	}
	return &h
}

/*
	LoadConfig parse configuration file. It will panic if any error
*/
func LoadConfig(filename string) *Config {
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	decoder := json.NewDecoder(file)
	conf := Config{}
	err = decoder.Decode(&conf)
	if err != nil {
		panic(err)
	}
	return &conf
}
<commit_msg>Adding docstring, documentation is hard to maintain<commit_after>package imgscale

import (
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"strings"
)

/*
	Prefix: use in the url to identify the format

	Height: the target height

	Ratio: zero for keeping ratio

	Thumbnail: true to use thumbnail feature in imagemagick, it is quick and optimized but you loose meta data

	Watermark: Indicate if watermark should apply on this format

*/
type Format struct {
	Prefix    string
	Height    uint
	Ratio     float64
	Thumbnail bool
	Watermark bool
}

/*
	Path: path of the folder which contains images

	Prefix: use as image path indicator in url

	Separator: separator between format prefix and filename

	Formats: list of Format

	Exts: allow extensions, only jpg, jpeg and png available. Be aware that jpg and jpeg handled as different extension.

	Comment: will store in meta data if specified

	AutoRotate: Autorotate image according to orientation stored in the image meta data

	Watermark: this watermark is used when watermark on a format is enabled

*/
type Config struct {
	Path    string
	Prefix  string
	Separator string
	Formats []*Format
	Exts    []string
	Comment string
	AutoRotate bool
	Watermark *Watermark
}

func configure(config *Config) *handler {
	for _, ext := range config.Exts {
		if _, ok := supportedExts[ext]; !ok {
			panic(fmt.Sprintf("Extension '%s' not supported", ext))
		}
	}

	prefixes := make([]string, len(config.Formats))
	formats := make(map[string]*Format)
	for i, format := range config.Formats {
		if _, exists := formats[format.Prefix]; exists {
			panic(fmt.Sprintf("You can not have same prefix '%s' for 2 different formats", format.Prefix))
		}
		prefixes[i] = format.Prefix
		formats[format.Prefix] = format
	}
	var separator string

	if config.Separator != "" {
		separator = config.Separator
	} else {
		separator = "/"
	}
	path := fmt.Sprintf("^/%s/(?P<format>%s)%s(?P<filename>.+)\\.(?i)(?P<ext>%s)$", config.Prefix, strings.Join(prefixes, "|"), separator, strings.Join(config.Exts, "|"))
	h := handler{formats: formats, config: config, regexp: regexp.MustCompile(path), supportedExts: supportedExts}
	h.SetValidator(defaultValidator{})

	if config.Watermark != nil && config.Watermark.Filename != "" {
		config.Watermark.load()
	}
	return &h
}

/*
	LoadConfig parse configuration file. It will panic if any error
*/
func LoadConfig(filename string) *Config {
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	decoder := json.NewDecoder(file)
	conf := Config{}
	err = decoder.Decode(&conf)
	if err != nil {
		panic(err)
	}
	return &conf
}
<|endoftext|>


<commit_before>package middleware

import (
	"bytes"
	"io"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/labstack/echo"
	"github.com/labstack/gommon/color"
	"github.com/valyala/fasttemplate"
)

type (
	// LoggerConfig defines the config for Logger middleware.
	LoggerConfig struct {
		// Skipper defines a function to skip middleware.
		Skipper Skipper

		// Tags to constructed the logger format.
		//
		// - time_unix
		// - time_unix_nano
		// - time_rfc3339
		// - time_rfc3339_nano
		// - time_custom
		// - id (Request ID)
		// - remote_ip
		// - uri
		// - host
		// - method
		// - path
		// - referer
		// - user_agent
		// - status
		// - error
		// - latency (In nanoseconds)
		// - latency_human (Human readable)
		// - bytes_in (Bytes received)
		// - bytes_out (Bytes sent)
		// - header:<NAME>
		// - query:<NAME>
		// - form:<NAME>
		//
		// Example "${remote_ip} ${status}"
		//
		// Optional. Default value DefaultLoggerConfig.Format.
		Format string `yaml:"format"`

		// Optional. Default value DefaultLoggerConfig.CustomTimeFormat.
		CustomTimeFormat string `yaml:"custom_time_format"`

		// Output is a writer where logs in JSON format are written.
		// Optional. Default value os.Stdout.
		Output io.Writer

		template *fasttemplate.Template
		colorer  *color.Color
		pool     *sync.Pool
	}
)

var (
	// DefaultLoggerConfig is the default Logger middleware config.
	DefaultLoggerConfig = LoggerConfig{
		Skipper: DefaultSkipper,
		Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` +
			`"method":"${method}","uri":"${uri}","status":${status},"error":"${error}","latency":${latency},` +
			`"latency_human":"${latency_human}","bytes_in":${bytes_in},` +
			`"bytes_out":${bytes_out}}` + "\n",
		CustomTimeFormat: "2006-01-02 15:04:05.00000",
		Output:           os.Stdout,
		colorer:          color.New(),
	}
)

// Logger returns a middleware that logs HTTP requests.
func Logger() echo.MiddlewareFunc {
	return LoggerWithConfig(DefaultLoggerConfig)
}

// LoggerWithConfig returns a Logger middleware with config.
// See: `Logger()`.
func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
	// Defaults
	if config.Skipper == nil {
		config.Skipper = DefaultLoggerConfig.Skipper
	}
	if config.Format == "" {
		config.Format = DefaultLoggerConfig.Format
	}
	if config.Output == nil {
		config.Output = DefaultLoggerConfig.Output
	}

	config.template = fasttemplate.New(config.Format, "${", "}")
	config.colorer = color.New()
	config.colorer.SetOutput(config.Output)
	config.pool = &sync.Pool{
		New: func() interface{} {
			return bytes.NewBuffer(make([]byte, 256))
		},
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) (err error) {
			if config.Skipper(c) {
				return next(c)
			}

			req := c.Request()
			res := c.Response()
			start := time.Now()
			if err = next(c); err != nil {
				c.Error(err)
			}
			stop := time.Now()
			buf := config.pool.Get().(*bytes.Buffer)
			buf.Reset()
			defer config.pool.Put(buf)

			if _, err = config.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) {
				switch tag {
				case "time_unix":
					return buf.WriteString(strconv.FormatInt(time.Now().Unix(), 10))
				case "time_unix_nano":
					return buf.WriteString(strconv.FormatInt(time.Now().UnixNano(), 10))
				case "time_rfc3339":
					return buf.WriteString(time.Now().Format(time.RFC3339))
				case "time_rfc3339_nano":
					return buf.WriteString(time.Now().Format(time.RFC3339Nano))
				case "time_custom":
					return buf.WriteString(time.Now().Format(config.CustomTimeFormat))
				case "id":
					id := req.Header.Get(echo.HeaderXRequestID)
					if id == "" {
						id = res.Header().Get(echo.HeaderXRequestID)
					}
					return buf.WriteString(id)
				case "remote_ip":
					return buf.WriteString(c.RealIP())
				case "host":
					return buf.WriteString(req.Host)
				case "uri":
					return buf.WriteString(req.RequestURI)
				case "method":
					return buf.WriteString(req.Method)
				case "path":
					p := req.URL.Path
					if p == "" {
						p = "/"
					}
					return buf.WriteString(p)
				case "referer":
					return buf.WriteString(req.Referer())
				case "user_agent":
					return buf.WriteString(req.UserAgent())
				case "status":
					n := res.Status
					s := config.colorer.Green(n)
					switch {
					case n >= 500:
						s = config.colorer.Red(n)
					case n >= 400:
						s = config.colorer.Yellow(n)
					case n >= 300:
						s = config.colorer.Cyan(n)
					}
					return buf.WriteString(s)
				case "error":
					if err != nil {
						return buf.WriteString(err.Error())
					}
				case "latency":
					l := stop.Sub(start)
					return buf.WriteString(strconv.FormatInt(int64(l), 10))
				case "latency_human":
					return buf.WriteString(stop.Sub(start).String())
				case "bytes_in":
					cl := req.Header.Get(echo.HeaderContentLength)
					if cl == "" {
						cl = "0"
					}
					return buf.WriteString(cl)
				case "bytes_out":
					return buf.WriteString(strconv.FormatInt(res.Size, 10))
				default:
					switch {
					case strings.HasPrefix(tag, "header:"):
						return buf.Write([]byte(c.Request().Header.Get(tag[7:])))
					case strings.HasPrefix(tag, "query:"):
						return buf.Write([]byte(c.QueryParam(tag[6:])))
					case strings.HasPrefix(tag, "form:"):
						return buf.Write([]byte(c.FormValue(tag[5:])))
					case strings.HasPrefix(tag, "cookie:"):
						cookie, err := c.Cookie(tag[7:])
						if err == nil {
							return buf.Write([]byte(cookie.Value))
						}
					}
				}
				return 0, nil
			}); err != nil {
				return
			}

			_, err = config.Output.Write(buf.Bytes())
			return
		}
	}
}
<commit_msg>add proto field for middleware<commit_after>package middleware

import (
	"bytes"
	"io"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/labstack/echo"
	"github.com/labstack/gommon/color"
	"github.com/valyala/fasttemplate"
)

type (
	// LoggerConfig defines the config for Logger middleware.
	LoggerConfig struct {
		// Skipper defines a function to skip middleware.
		Skipper Skipper

		// Tags to constructed the logger format.
		//
		// - time_unix
		// - time_unix_nano
		// - time_rfc3339
		// - time_rfc3339_nano
		// - time_custom
		// - id (Request ID)
		// - remote_ip
		// - uri
		// - host
		// - method
		// - path
		// - proto
		// - referer
		// - user_agent
		// - status
		// - error
		// - latency (In nanoseconds)
		// - latency_human (Human readable)
		// - bytes_in (Bytes received)
		// - bytes_out (Bytes sent)
		// - header:<NAME>
		// - query:<NAME>
		// - form:<NAME>
		//
		// Example "${remote_ip} ${status}"
		//
		// Optional. Default value DefaultLoggerConfig.Format.
		Format string `yaml:"format"`

		// Optional. Default value DefaultLoggerConfig.CustomTimeFormat.
		CustomTimeFormat string `yaml:"custom_time_format"`

		// Output is a writer where logs in JSON format are written.
		// Optional. Default value os.Stdout.
		Output io.Writer

		template *fasttemplate.Template
		colorer  *color.Color
		pool     *sync.Pool
	}
)

var (
	// DefaultLoggerConfig is the default Logger middleware config.
	DefaultLoggerConfig = LoggerConfig{
		Skipper: DefaultSkipper,
		Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` +
			`"method":"${method}","uri":"${uri}","status":${status},"error":"${error}","latency":${latency},` +
			`"latency_human":"${latency_human}","bytes_in":${bytes_in},` +
			`"bytes_out":${bytes_out}}` + "\n",
		CustomTimeFormat: "2006-01-02 15:04:05.00000",
		Output:           os.Stdout,
		colorer:          color.New(),
	}
)

// Logger returns a middleware that logs HTTP requests.
func Logger() echo.MiddlewareFunc {
	return LoggerWithConfig(DefaultLoggerConfig)
}

// LoggerWithConfig returns a Logger middleware with config.
// See: `Logger()`.
func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
	// Defaults
	if config.Skipper == nil {
		config.Skipper = DefaultLoggerConfig.Skipper
	}
	if config.Format == "" {
		config.Format = DefaultLoggerConfig.Format
	}
	if config.Output == nil {
		config.Output = DefaultLoggerConfig.Output
	}

	config.template = fasttemplate.New(config.Format, "${", "}")
	config.colorer = color.New()
	config.colorer.SetOutput(config.Output)
	config.pool = &sync.Pool{
		New: func() interface{} {
			return bytes.NewBuffer(make([]byte, 256))
		},
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) (err error) {
			if config.Skipper(c) {
				return next(c)
			}

			req := c.Request()
			res := c.Response()
			start := time.Now()
			if err = next(c); err != nil {
				c.Error(err)
			}
			stop := time.Now()
			buf := config.pool.Get().(*bytes.Buffer)
			buf.Reset()
			defer config.pool.Put(buf)

			if _, err = config.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) {
				switch tag {
				case "time_unix":
					return buf.WriteString(strconv.FormatInt(time.Now().Unix(), 10))
				case "time_unix_nano":
					return buf.WriteString(strconv.FormatInt(time.Now().UnixNano(), 10))
				case "time_rfc3339":
					return buf.WriteString(time.Now().Format(time.RFC3339))
				case "time_rfc3339_nano":
					return buf.WriteString(time.Now().Format(time.RFC3339Nano))
				case "time_custom":
					return buf.WriteString(time.Now().Format(config.CustomTimeFormat))
				case "id":
					id := req.Header.Get(echo.HeaderXRequestID)
					if id == "" {
						id = res.Header().Get(echo.HeaderXRequestID)
					}
					return buf.WriteString(id)
				case "remote_ip":
					return buf.WriteString(c.RealIP())
				case "host":
					return buf.WriteString(req.Host)
				case "uri":
					return buf.WriteString(req.RequestURI)
				case "method":
					return buf.WriteString(req.Method)
				case "path":
					p := req.URL.Path
					if p == "" {
						p = "/"
					}
					return buf.WriteString(p)
				case "proto":
					return buf.WriteString(req.Proto)
				case "referer":
					return buf.WriteString(req.Referer())
				case "user_agent":
					return buf.WriteString(req.UserAgent())
				case "status":
					n := res.Status
					s := config.colorer.Green(n)
					switch {
					case n >= 500:
						s = config.colorer.Red(n)
					case n >= 400:
						s = config.colorer.Yellow(n)
					case n >= 300:
						s = config.colorer.Cyan(n)
					}
					return buf.WriteString(s)
				case "error":
					if err != nil {
						return buf.WriteString(err.Error())
					}
				case "latency":
					l := stop.Sub(start)
					return buf.WriteString(strconv.FormatInt(int64(l), 10))
				case "latency_human":
					return buf.WriteString(stop.Sub(start).String())
				case "bytes_in":
					cl := req.Header.Get(echo.HeaderContentLength)
					if cl == "" {
						cl = "0"
					}
					return buf.WriteString(cl)
				case "bytes_out":
					return buf.WriteString(strconv.FormatInt(res.Size, 10))
				default:
					switch {
					case strings.HasPrefix(tag, "header:"):
						return buf.Write([]byte(c.Request().Header.Get(tag[7:])))
					case strings.HasPrefix(tag, "query:"):
						return buf.Write([]byte(c.QueryParam(tag[6:])))
					case strings.HasPrefix(tag, "form:"):
						return buf.Write([]byte(c.FormValue(tag[5:])))
					case strings.HasPrefix(tag, "cookie:"):
						cookie, err := c.Cookie(tag[7:])
						if err == nil {
							return buf.Write([]byte(cookie.Value))
						}
					}
				}
				return 0, nil
			}); err != nil {
				return
			}

			_, err = config.Output.Write(buf.Bytes())
			return
		}
	}
}
<|endoftext|>


<commit_before>package gps

import (
	"bytes"
	"fmt"
	"net/url"
	"path/filepath"

	"github.com/Masterminds/vcs"
)

// A maybeSource represents a set of information that, given some
// typically-expensive network effort, could be transformed into a proper source.
//
// Wrapping these up as their own type kills two birds with one stone:
//
// * Allows control over when deduction logic triggers network activity
// * Makes it easy to attempt multiple URLs for a given import path
type maybeSource interface {
	try(cachedir string, an ProjectAnalyzer) (source, string, error)
}

type maybeSources []maybeSource

func (mbs maybeSources) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	var e sourceFailures
	for _, mb := range mbs {
		src, ident, err := mb.try(cachedir, an)
		if err == nil {
			return src, ident, nil
		}
		e = append(e, sourceSetupFailure{
			ident: ident,
			err:   err,
		})
	}
	return nil, "", e
}

type sourceSetupFailure struct {
	ident string
	err   error
}

func (e sourceSetupFailure) Error() string {
	return fmt.Sprintf("failed to set up %q, error %s", e.ident, e.err.Error())
}

type sourceFailures []sourceSetupFailure

func (sf sourceFailures) Error() string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "No valid source could be created:\n")
	for _, e := range sf {
		fmt.Fprintf(&buf, "\t%s", e.Error())
	}

	return buf.String()
}

type maybeGitSource struct {
	url *url.URL
}

func (m maybeGitSource) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	ustr := m.url.String()
	path := filepath.Join(cachedir, "sources", sanitizer.Replace(ustr))
	r, err := vcs.NewGitRepo(ustr, path)
	if err != nil {
		return nil, "", err
	}

	src := &gitSource{
		baseVCSSource: baseVCSSource{
			an: an,
			dc: newMetaCache(),
			crepo: &repo{
				r:     r,
				rpath: path,
			},
		},
	}

	src.baseVCSSource.lvfunc = src.listVersions
	if !r.CheckLocal() {
		_, err = src.listVersions()
		if err != nil {
			return nil, "", err
		}
	}

	return src, ustr, nil
}

type maybeGopkginSource struct {
	// the original gopkg.in import path. this is used to create the on-disk
	// location to avoid duplicate resource management - e.g., if instances of
	// a gopkg.in project are accessed via different schemes, or if the
	// underlying github repository is accessed directly.
	opath string
	// the actual upstream URL - always github
	url *url.URL
	// the major version to apply for filtering
	major uint64
}

func (m maybeGopkginSource) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	// We don't actually need a fully consistent transform into the on-disk path
	// - just something that's unique to the particular gopkg.in domain context.
	// So, it's OK to just dumb-join the scheme with the path.
	path := filepath.Join(cachedir, "sources", sanitizer.Replace(m.url.Scheme+"/"+m.opath))
	ustr := m.url.String()
	r, err := vcs.NewGitRepo(ustr, path)
	if err != nil {
		return nil, "", err
	}

	src := &gopkginSource{
		gitSource: gitSource{
			baseVCSSource: baseVCSSource{
				an: an,
				dc: newMetaCache(),
				crepo: &repo{
					r:     r,
					rpath: path,
				},
			},
		},
		major: m.major,
	}

	src.baseVCSSource.lvfunc = src.listVersions
	if !r.CheckLocal() {
		_, err = src.listVersions()
		if err != nil {
			return nil, "", err
		}
	}

	return src, ustr, nil
}

type maybeBzrSource struct {
	url *url.URL
}

func (m maybeBzrSource) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	ustr := m.url.String()
	path := filepath.Join(cachedir, "sources", sanitizer.Replace(ustr))
	r, err := vcs.NewBzrRepo(ustr, path)
	if err != nil {
		return nil, "", err
	}
	if !r.Ping() {
		return nil, "", fmt.Errorf("Remote repository at %s does not exist, or is inaccessible", ustr)
	}

	src := &bzrSource{
		baseVCSSource: baseVCSSource{
			an: an,
			dc: newMetaCache(),
			ex: existence{
				s: existsUpstream,
				f: existsUpstream,
			},
			crepo: &repo{
				r:     r,
				rpath: path,
			},
		},
	}
	src.baseVCSSource.lvfunc = src.listVersions

	return src, ustr, nil
}

type maybeHgSource struct {
	url *url.URL
}

func (m maybeHgSource) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	ustr := m.url.String()
	path := filepath.Join(cachedir, "sources", sanitizer.Replace(ustr))
	r, err := vcs.NewHgRepo(ustr, path)
	if err != nil {
		return nil, "", err
	}
	if !r.Ping() {
		return nil, "", fmt.Errorf("Remote repository at %s does not exist, or is inaccessible", ustr)
	}

	src := &hgSource{
		baseVCSSource: baseVCSSource{
			an: an,
			dc: newMetaCache(),
			ex: existence{
				s: existsUpstream,
				f: existsUpstream,
			},
			crepo: &repo{
				r:     r,
				rpath: path,
			},
		},
	}
	src.baseVCSSource.lvfunc = src.listVersions

	return src, ustr, nil
}
<commit_msg>Improve error messages then a VCS operation fails.<commit_after>package gps

import (
	"bytes"
	"fmt"
	"net/url"
	"path/filepath"

	"github.com/Masterminds/vcs"
)

// A maybeSource represents a set of information that, given some
// typically-expensive network effort, could be transformed into a proper source.
//
// Wrapping these up as their own type kills two birds with one stone:
//
// * Allows control over when deduction logic triggers network activity
// * Makes it easy to attempt multiple URLs for a given import path
type maybeSource interface {
	try(cachedir string, an ProjectAnalyzer) (source, string, error)
}

type maybeSources []maybeSource

func (mbs maybeSources) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	var e sourceFailures
	for _, mb := range mbs {
		src, ident, err := mb.try(cachedir, an)
		if err == nil {
			return src, ident, nil
		}
		e = append(e, sourceSetupFailure{
			ident: ident,
			err:   err,
		})
	}
	return nil, "", e
}

type sourceSetupFailure struct {
	ident string
	err   error
}

func (e sourceSetupFailure) Error() string {
	return fmt.Sprintf("failed to set up %q, error %s", e.ident, e.err.Error())
}

type sourceFailures []sourceSetupFailure

func (sf sourceFailures) Error() string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "No valid source could be created:\n")
	for _, e := range sf {
		fmt.Fprintf(&buf, "\t%s", e.Error())
	}

	return buf.String()
}

type maybeGitSource struct {
	url *url.URL
}

func (m maybeGitSource) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	ustr := m.url.String()
	path := filepath.Join(cachedir, "sources", sanitizer.Replace(ustr))
	r, err := vcs.NewGitRepo(ustr, path)
	if err != nil {
		return nil, ustr, err
	}

	src := &gitSource{
		baseVCSSource: baseVCSSource{
			an: an,
			dc: newMetaCache(),
			crepo: &repo{
				r:     r,
				rpath: path,
			},
		},
	}

	src.baseVCSSource.lvfunc = src.listVersions
	if !r.CheckLocal() {
		_, err = src.listVersions()
		if err != nil {
			return nil, ustr, err
		}
	}

	return src, ustr, nil
}

type maybeGopkginSource struct {
	// the original gopkg.in import path. this is used to create the on-disk
	// location to avoid duplicate resource management - e.g., if instances of
	// a gopkg.in project are accessed via different schemes, or if the
	// underlying github repository is accessed directly.
	opath string
	// the actual upstream URL - always github
	url *url.URL
	// the major version to apply for filtering
	major uint64
}

func (m maybeGopkginSource) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	// We don't actually need a fully consistent transform into the on-disk path
	// - just something that's unique to the particular gopkg.in domain context.
	// So, it's OK to just dumb-join the scheme with the path.
	path := filepath.Join(cachedir, "sources", sanitizer.Replace(m.url.Scheme+"/"+m.opath))
	ustr := m.url.String()
	r, err := vcs.NewGitRepo(ustr, path)
	if err != nil {
		return nil, ustr, err
	}

	src := &gopkginSource{
		gitSource: gitSource{
			baseVCSSource: baseVCSSource{
				an: an,
				dc: newMetaCache(),
				crepo: &repo{
					r:     r,
					rpath: path,
				},
			},
		},
		major: m.major,
	}

	src.baseVCSSource.lvfunc = src.listVersions
	if !r.CheckLocal() {
		_, err = src.listVersions()
		if err != nil {
			return nil, ustr, err
		}
	}

	return src, ustr, nil
}

type maybeBzrSource struct {
	url *url.URL
}

func (m maybeBzrSource) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	ustr := m.url.String()
	path := filepath.Join(cachedir, "sources", sanitizer.Replace(ustr))
	r, err := vcs.NewBzrRepo(ustr, path)
	if err != nil {
		return nil, ustr, err
	}
	if !r.Ping() {
		return nil, ustr, fmt.Errorf("Remote repository at %s does not exist, or is inaccessible", ustr)
	}

	src := &bzrSource{
		baseVCSSource: baseVCSSource{
			an: an,
			dc: newMetaCache(),
			ex: existence{
				s: existsUpstream,
				f: existsUpstream,
			},
			crepo: &repo{
				r:     r,
				rpath: path,
			},
		},
	}
	src.baseVCSSource.lvfunc = src.listVersions

	return src, ustr, nil
}

type maybeHgSource struct {
	url *url.URL
}

func (m maybeHgSource) try(cachedir string, an ProjectAnalyzer) (source, string, error) {
	ustr := m.url.String()
	path := filepath.Join(cachedir, "sources", sanitizer.Replace(ustr))
	r, err := vcs.NewHgRepo(ustr, path)
	if err != nil {
		return nil, ustr, err
	}
	if !r.Ping() {
		return nil, ustr, fmt.Errorf("Remote repository at %s does not exist, or is inaccessible", ustr)
	}

	src := &hgSource{
		baseVCSSource: baseVCSSource{
			an: an,
			dc: newMetaCache(),
			ex: existence{
				s: existsUpstream,
				f: existsUpstream,
			},
			crepo: &repo{
				r:     r,
				rpath: path,
			},
		},
	}
	src.baseVCSSource.lvfunc = src.listVersions

	return src, ustr, nil
}
<|endoftext|>


<commit_before>package instructions

import (
	"github.com/zxh0/jvm.go/jvmgo/jvm/rtda"
	rtc "github.com/zxh0/jvm.go/jvmgo/jvm/rtda/class"
)

// Set field in object
type putfield struct{ Index16Instruction }

func (self *putfield) Execute(frame *rtda.Frame) {
	stack := frame.OperandStack()
	val := stack.Pop()
	ref := stack.PopRef()
	if ref == nil {
		frame.Thread().ThrowNPE()
		return
	}

	cp := frame.ConstantPool()
	kFieldRef := cp.GetConstant(self.index).(*rtc.ConstantFieldref)
	field := kFieldRef.InstanceField()

	field.PutValue(ref, val)
}

// Set static field in class
type putstatic struct{ Index16Instruction }

func (self *putstatic) Execute(frame *rtda.Frame) {
	cp := frame.Method().Class().ConstantPool()
	kFieldRef := cp.GetConstant(self.index).(*rtc.ConstantFieldref)
	field := kFieldRef.StaticField()

	class := field.Class()
	if class.InitializationNotStarted() {
		frame.RevertNextPC()
		frame.Thread().InitClass(class)
		return
	}

	val := frame.OperandStack().Pop()
	field.PutStaticValue(val)
}
<commit_msg>optimization<commit_after>package instructions

import (
	"github.com/zxh0/jvm.go/jvmgo/jvm/rtda"
	rtc "github.com/zxh0/jvm.go/jvmgo/jvm/rtda/class"
)

// Set field in object
type putfield struct {
	Index16Instruction
	field *rtc.Field
}

func (self *putfield) Execute(frame *rtda.Frame) {
	if self.field == nil {
		cp := frame.ConstantPool()
		kFieldRef := cp.GetConstant(self.index).(*rtc.ConstantFieldref)
		self.field = kFieldRef.InstanceField()
	}

	stack := frame.OperandStack()
	val := stack.Pop()
	ref := stack.PopRef()
	if ref == nil {
		frame.Thread().ThrowNPE()
		return
	}

	self.field.PutValue(ref, val)
}

// Set static field in class
type putstatic struct {
	Index16Instruction
	field *rtc.Field
}

func (self *putstatic) Execute(frame *rtda.Frame) {
	if self.field == nil {
		cp := frame.Method().Class().ConstantPool()
		kFieldRef := cp.GetConstant(self.index).(*rtc.ConstantFieldref)
		self.field = kFieldRef.StaticField()
	}

	class := self.field.Class()
	if class.InitializationNotStarted() {
		frame.RevertNextPC()
		frame.Thread().InitClass(class)
		return
	}

	val := frame.OperandStack().Pop()
	self.field.PutStaticValue(val)
}
<|endoftext|>


<commit_before>package coap

import (
	"fmt"
	"reflect"
	"testing"
)

func TestEncodeMessageSmall(t *testing.T) {
	req := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
		Options: Options{
			Option{ETag, []byte("weetag")},
			Option{MaxAge, 3},
		},
	}

	data, err := encodeMessage(req)
	if err != nil {
		t.Fatalf("Error encoding request: %v", err)
	}

	// Inspected by hand.
	exp := []byte{
		0x42, 0x1, 0x30, 0x39, 0x21, 0x3,
		0x26, 0x77, 0x65, 0x65, 0x74, 0x61, 0x67,
	}
	if !reflect.DeepEqual(exp, data) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, data)
	}
}

func TestEncodeMessageVerySmall(t *testing.T) {
	req := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
	}
	req.SetPathString("x")

	data, err := encodeMessage(req)
	if err != nil {
		t.Fatalf("Error encoding request: %v", err)
	}

	// Inspected by hand.
	exp := []byte{
		0x41, 0x1, 0x30, 0x39, 0x91, 0x78,
	}
	if !reflect.DeepEqual(exp, data) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, data)
	}
}

func TestEncodeSeveral(t *testing.T) {
	tests := map[string][]string{
		"a":   []string{"a"},
		"axe": []string{"axe"},
		"a/b/c/d/e/f/h/g/i/j": []string{"a", "b", "c", "d", "e",
			"f", "h", "g", "i", "j"},
	}
	for p, a := range tests {
		m := &Message{Type: Confirmable, Code: GET, MessageID: 12345}
		m.SetPathString(p)
		b, err := encodeMessage(*m)
		if err != nil {
			t.Errorf("Error encoding %#v", p)
			t.Fail()
			continue
		}
		m2, err := parseMessage(b)
		if err != nil {
			t.Fatalf("Can't parse my own message at %#v: %v", p, err)
		}

		if !reflect.DeepEqual(m2.Path(), a) {
			t.Errorf("Expected %#v, got %#v", a, m2.Path())
			t.Fail()
		}
	}
}

func TestEncodeLargePath(t *testing.T) {
	req := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
	}
	req.SetPathString("this_path_is_longer_than_fifteen_bytes")

	if req.PathString() != "this_path_is_longer_than_fifteen_bytes" {
		t.Fatalf("Didn't get back the same path I posted: %v",
			req.PathString())
	}

	data, err := encodeMessage(req)
	if err != nil {
		t.Fatalf("Error encoding request: %v", err)
	}

	// Inspected by hand.
	exp := []byte{
		0x41, 0x1, 0x30, 0x39, 0x9f, 0x17, 0x74, 0x68,
		0x69, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f,
		0x69, 0x73, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x65,
		0x72, 0x5f, 0x74, 0x68, 0x61, 0x6e, 0x5f, 0x66,
		0x69, 0x66, 0x74, 0x65, 0x65, 0x6e, 0x5f, 0x62,
		0x79, 0x74, 0x65, 0x73}
	if !reflect.DeepEqual(exp, data) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, data)
	}
}

func TestDecodeLargePath(t *testing.T) {
	data := []byte{
		0x41, 0x1, 0x30, 0x39, 0x9f, 0x17, 0x74, 0x68,
		0x69, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f,
		0x69, 0x73, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x65,
		0x72, 0x5f, 0x74, 0x68, 0x61, 0x6e, 0x5f, 0x66,
		0x69, 0x66, 0x74, 0x65, 0x65, 0x6e, 0x5f, 0x62,
		0x79, 0x74, 0x65, 0x73}

	req, err := parseMessage(data)
	if err != nil {
		t.Fatalf("Error parsing request: %v", err)
	}

	path := "this_path_is_longer_than_fifteen_bytes"

	exp := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
		Options: Options{
			{URIPath, path},
		},
	}

	if fmt.Sprintf("%#v", exp) != fmt.Sprintf("%#v", req) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, req)
	}
}

func TestDecodeMessageSmaller(t *testing.T) {
	data := []byte{
		0x42, 0x1, 0x30, 0x39, 0x21, 0x3,
		0x26, 0x77, 0x65, 0x65, 0x74, 0x61, 0x67,
	}

	req, err := parseMessage(data)
	if err != nil {
		t.Fatalf("Error parsing request: %v", err)
	}

	exp := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
		Options: Options{
			Option{MaxAge, uint32(3)},
			Option{ETag, []byte("weetag")},
		},
	}

	if fmt.Sprintf("%#v", exp) != fmt.Sprintf("%#v", req) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, req)
	}
}

func TestByteEncoding(t *testing.T) {
	tests := []struct {
		Value    uint32
		Expected []byte
	}{
		{0, []byte{}},
		{13, []byte{13}},
		{1024, []byte{4, 0}},
		{984284, []byte{0x0f, 0x04, 0xdc}},
		{823958824, []byte{0x31, 0x1c, 0x9d, 0x28}},
	}

	for _, v := range tests {
		got := encodeInt(v.Value)
		if !reflect.DeepEqual(got, v.Expected) {
			t.Fatalf("Expected %#v, got %#v for %v",
				v.Expected, got, v.Value)
		}
	}
}

func TestByteDecoding(t *testing.T) {
	tests := []struct {
		Value uint32
		Bytes []byte
	}{
		{0, []byte{}},
		{0, []byte{0}},
		{0, []byte{0, 0}},
		{0, []byte{0, 0, 0}},
		{0, []byte{0, 0, 0, 0}},
		{13, []byte{13}},
		{13, []byte{0, 13}},
		{13, []byte{0, 0, 13}},
		{13, []byte{0, 0, 0, 13}},
		{1024, []byte{4, 0}},
		{1024, []byte{4, 0}},
		{1024, []byte{0, 4, 0}},
		{1024, []byte{0, 0, 4, 0}},
		{984284, []byte{0x0f, 0x04, 0xdc}},
		{984284, []byte{0, 0x0f, 0x04, 0xdc}},
		{823958824, []byte{0x31, 0x1c, 0x9d, 0x28}},
	}

	for _, v := range tests {
		got := decodeInt(v.Bytes)
		if v.Value != got {
			t.Fatalf("Expected %v, got %v for %#v",
				v.Value, got, v.Bytes)
		}
	}
}
<commit_msg>Validate too many options error<commit_after>package coap

import (
	"fmt"
	"reflect"
	"testing"
)

func TestEncodeMessageTooManyOptions(t *testing.T) {
	req := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
		Options: Options{
			Option{ETag, []byte("weetag")},
			Option{MaxAge, 3},
			Option{ContentType, TextPlain},
			Option{IfMatch, "a"},
			Option{IfMatch, "b"},
			Option{IfMatch, "c"},
			Option{IfMatch, "d"},
			Option{IfMatch, "e"},
			Option{IfMatch, "f"},
			Option{IfNoneMatch, "z"},
			Option{IfNoneMatch, "y"},
			Option{IfNoneMatch, "x"},
			Option{IfNoneMatch, "w"},
			Option{IfNoneMatch, "v"},
			Option{IfNoneMatch, "u"},
		},
	}
	req.SetPathString("/a/b/c/d/e/f/g/h")

	_, err := encodeMessage(req)
	if err != TooManyOptions {
		t.Fatalf("Expected 'too many options', got: %v", err)
	}
}

func TestEncodeMessageSmall(t *testing.T) {
	req := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
		Options: Options{
			Option{ETag, []byte("weetag")},
			Option{MaxAge, 3},
		},
	}

	data, err := encodeMessage(req)
	if err != nil {
		t.Fatalf("Error encoding request: %v", err)
	}

	// Inspected by hand.
	exp := []byte{
		0x42, 0x1, 0x30, 0x39, 0x21, 0x3,
		0x26, 0x77, 0x65, 0x65, 0x74, 0x61, 0x67,
	}
	if !reflect.DeepEqual(exp, data) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, data)
	}
}

func TestEncodeMessageVerySmall(t *testing.T) {
	req := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
	}
	req.SetPathString("x")

	data, err := encodeMessage(req)
	if err != nil {
		t.Fatalf("Error encoding request: %v", err)
	}

	// Inspected by hand.
	exp := []byte{
		0x41, 0x1, 0x30, 0x39, 0x91, 0x78,
	}
	if !reflect.DeepEqual(exp, data) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, data)
	}
}

func TestEncodeSeveral(t *testing.T) {
	tests := map[string][]string{
		"a":   []string{"a"},
		"axe": []string{"axe"},
		"a/b/c/d/e/f/h/g/i/j": []string{"a", "b", "c", "d", "e",
			"f", "h", "g", "i", "j"},
	}
	for p, a := range tests {
		m := &Message{Type: Confirmable, Code: GET, MessageID: 12345}
		m.SetPathString(p)
		b, err := encodeMessage(*m)
		if err != nil {
			t.Errorf("Error encoding %#v", p)
			t.Fail()
			continue
		}
		m2, err := parseMessage(b)
		if err != nil {
			t.Fatalf("Can't parse my own message at %#v: %v", p, err)
		}

		if !reflect.DeepEqual(m2.Path(), a) {
			t.Errorf("Expected %#v, got %#v", a, m2.Path())
			t.Fail()
		}
	}
}

func TestEncodeLargePath(t *testing.T) {
	req := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
	}
	req.SetPathString("this_path_is_longer_than_fifteen_bytes")

	if req.PathString() != "this_path_is_longer_than_fifteen_bytes" {
		t.Fatalf("Didn't get back the same path I posted: %v",
			req.PathString())
	}

	data, err := encodeMessage(req)
	if err != nil {
		t.Fatalf("Error encoding request: %v", err)
	}

	// Inspected by hand.
	exp := []byte{
		0x41, 0x1, 0x30, 0x39, 0x9f, 0x17, 0x74, 0x68,
		0x69, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f,
		0x69, 0x73, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x65,
		0x72, 0x5f, 0x74, 0x68, 0x61, 0x6e, 0x5f, 0x66,
		0x69, 0x66, 0x74, 0x65, 0x65, 0x6e, 0x5f, 0x62,
		0x79, 0x74, 0x65, 0x73}
	if !reflect.DeepEqual(exp, data) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, data)
	}
}

func TestDecodeLargePath(t *testing.T) {
	data := []byte{
		0x41, 0x1, 0x30, 0x39, 0x9f, 0x17, 0x74, 0x68,
		0x69, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f,
		0x69, 0x73, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x65,
		0x72, 0x5f, 0x74, 0x68, 0x61, 0x6e, 0x5f, 0x66,
		0x69, 0x66, 0x74, 0x65, 0x65, 0x6e, 0x5f, 0x62,
		0x79, 0x74, 0x65, 0x73}

	req, err := parseMessage(data)
	if err != nil {
		t.Fatalf("Error parsing request: %v", err)
	}

	path := "this_path_is_longer_than_fifteen_bytes"

	exp := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
		Options: Options{
			{URIPath, path},
		},
	}

	if fmt.Sprintf("%#v", exp) != fmt.Sprintf("%#v", req) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, req)
	}
}

func TestDecodeMessageSmaller(t *testing.T) {
	data := []byte{
		0x42, 0x1, 0x30, 0x39, 0x21, 0x3,
		0x26, 0x77, 0x65, 0x65, 0x74, 0x61, 0x67,
	}

	req, err := parseMessage(data)
	if err != nil {
		t.Fatalf("Error parsing request: %v", err)
	}

	exp := Message{
		Type:      Confirmable,
		Code:      GET,
		MessageID: 12345,
		Options: Options{
			Option{MaxAge, uint32(3)},
			Option{ETag, []byte("weetag")},
		},
	}

	if fmt.Sprintf("%#v", exp) != fmt.Sprintf("%#v", req) {
		t.Fatalf("Expected\n%#v\ngot\n%#v", exp, req)
	}
}

func TestByteEncoding(t *testing.T) {
	tests := []struct {
		Value    uint32
		Expected []byte
	}{
		{0, []byte{}},
		{13, []byte{13}},
		{1024, []byte{4, 0}},
		{984284, []byte{0x0f, 0x04, 0xdc}},
		{823958824, []byte{0x31, 0x1c, 0x9d, 0x28}},
	}

	for _, v := range tests {
		got := encodeInt(v.Value)
		if !reflect.DeepEqual(got, v.Expected) {
			t.Fatalf("Expected %#v, got %#v for %v",
				v.Expected, got, v.Value)
		}
	}
}

func TestByteDecoding(t *testing.T) {
	tests := []struct {
		Value uint32
		Bytes []byte
	}{
		{0, []byte{}},
		{0, []byte{0}},
		{0, []byte{0, 0}},
		{0, []byte{0, 0, 0}},
		{0, []byte{0, 0, 0, 0}},
		{13, []byte{13}},
		{13, []byte{0, 13}},
		{13, []byte{0, 0, 13}},
		{13, []byte{0, 0, 0, 13}},
		{1024, []byte{4, 0}},
		{1024, []byte{4, 0}},
		{1024, []byte{0, 4, 0}},
		{1024, []byte{0, 0, 4, 0}},
		{984284, []byte{0x0f, 0x04, 0xdc}},
		{984284, []byte{0, 0x0f, 0x04, 0xdc}},
		{823958824, []byte{0x31, 0x1c, 0x9d, 0x28}},
	}

	for _, v := range tests {
		got := decodeInt(v.Bytes)
		if v.Value != got {
			t.Fatalf("Expected %v, got %v for %#v",
				v.Value, got, v.Bytes)
		}
	}
}
<|endoftext|>


<commit_before>package gumble

import (
	"io"

	"github.com/bontibon/gumble/MumbleProto"
)

type TextMessage struct {
	Sender   *User      // User who sent the message (can be nil).
	Users    []*User    // Users that receive the message.
	Channels []*Channel // Channels that receive the message.
	Message  string     // Chat message.
}

func (pm *TextMessage) WriteTo(w io.Writer) (n int64, err error) {
	packet := MumbleProto.TextMessage{
		Message: &pm.Message,
	}
	if pm.Users != nil {
		packet.Session = make([]uint32, len(pm.Users))
		for i, user := range pm.Users {
			packet.Session[i] = user.session
		}
	}
	if pm.Channels != nil {
		packet.ChannelId = make([]uint32, len(pm.Channels))
		for i, channel := range pm.Channels {
			packet.ChannelId[i] = channel.id
		}
	}
	proto := protoMessage{&packet}
	return proto.WriteTo(w)
}

func (pm *TextMessage) gumbleMessage() {
}
<commit_msg>remove return value names from TextMessage.writeTo<commit_after>package gumble

import (
	"io"

	"github.com/bontibon/gumble/MumbleProto"
)

type TextMessage struct {
	Sender   *User      // User who sent the message (can be nil).
	Users    []*User    // Users that receive the message.
	Channels []*Channel // Channels that receive the message.
	Message  string     // Chat message.
}

func (pm *TextMessage) WriteTo(w io.Writer) (int64, error) {
	packet := MumbleProto.TextMessage{
		Message: &pm.Message,
	}
	if pm.Users != nil {
		packet.Session = make([]uint32, len(pm.Users))
		for i, user := range pm.Users {
			packet.Session[i] = user.session
		}
	}
	if pm.Channels != nil {
		packet.ChannelId = make([]uint32, len(pm.Channels))
		for i, channel := range pm.Channels {
			packet.ChannelId[i] = channel.id
		}
	}
	proto := protoMessage{&packet}
	return proto.WriteTo(w)
}

func (pm *TextMessage) gumbleMessage() {
}
<|endoftext|>


<commit_before>package lint_test

import (
	"testing"

	"log"

	"fmt"
	"reflect"
	"runtime/debug"

	"strings"

	"github.com/surullabs/lint"
	"github.com/surullabs/lint/checkers"
	"github.com/surullabs/lint/dupl"
)

func TestLint(t *testing.T) {
	linters := lint.Skip(
		lint.Default.With(dupl.Check{Threshold: 25}),
		// Ignore all errors from unused.go
		lint.RegexpMatch(`unused\.go`),
		// Ignore duplicates we're okay with.
		dupl.SkipTwo, dupl.Skip("golint.go:1,12"),
	)
	if err := linters.Check("./..."); err != nil {
		t.Fatal(err)
	}
}

type checkFn func(pkgs ...string) error

func (c checkFn) Check(pkgs ...string) error { return c(pkgs...) }

func assert(t *testing.T, cond bool, msg string) {
	if cond {
		return
	}
	t.Fatal(msg, string(debug.Stack()))
}

var (
	expectRecursive = checkFn(func(args ...string) error {
		if !reflect.DeepEqual(args, []string{"./..."}) {
			return fmt.Errorf("expected [./...], got %v", args)
		}
		return nil
	})

	twoErrors = checkFn(func(...string) error {
		return checkers.Error("err1", "err2")
	})

	ungroupedError = checkFn(func(...string) error {
		return fmt.Errorf("ungrouped: %d", 1)
	})
)

func TestGroup(t *testing.T) {
	gcheck := func(fn ...lint.Checker) error {
		return lint.Group(fn).Check("./...")
	}

	// All checks pass
	err := gcheck(expectRecursive)
	assert(t, err == nil, fmt.Sprintf("%v", err))

	err = gcheck(twoErrors)
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: err1\nlint_test.checkFn: err2",
		fmt.Sprintf("%v", err))

	err = gcheck(ungroupedError)
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: ungrouped: 1",
		fmt.Sprintf("%v", err))

	// Grouped
	err = gcheck(twoErrors, ungroupedError)
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: err1\nlint_test.checkFn: err2\nlint_test.checkFn: ungrouped: 1",
		fmt.Sprintf("%v", err))
}

type skipFunc func(err string) bool

func (s skipFunc) Skip(err string) bool { return s(err) }

func errorIs(str string) skipFunc {
	return skipFunc(func(err string) bool { return str == err })
}

func scheck(c lint.Checker, skippers ...lint.Skipper) error {
	return lint.Skip(c, skippers...).Check("./...")
}

func TestSkip(t *testing.T) {
	// All checks pass
	err := scheck(expectRecursive, errorIs("err1"))
	assert(t, err == nil, fmt.Sprintf("%v", err))

	err = scheck(twoErrors, errorIs("err1"))
	assert(t,
		err != nil && err.Error() == "err2",
		fmt.Sprintf("%v", err))

	// Skip ungrouped error
	err = scheck(ungroupedError, errorIs("ungrouped: 1"))
	assert(t, err == nil, fmt.Sprintf("%v", err))

	// Skip don't skip ungrouped error
	err = scheck(ungroupedError, errorIs("err1"))
	assert(t, err != nil && err.Error() == "ungrouped: 1", fmt.Sprintf("%v", err))

	// Grouped
	err = scheck(lint.Group{twoErrors, ungroupedError}, errorIs("lint_test.checkFn: err1"))
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: err2\nlint_test.checkFn: ungrouped: 1",
		fmt.Sprintf("%v", err))

}

func TestRegexpMatch(t *testing.T) {
	// regexp match
	skipRE := lint.RegexpMatch(`err1`, `ungrouped`)
	err := scheck(lint.Group{twoErrors, ungroupedError}, skipRE)
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: err2",
		fmt.Sprintf("%v", err))

	// panic on bad RE

	func() {
		defer func() {
			r := recover()
			if r != nil {
				err = fmt.Errorf("%v", r)
			}
		}()
		scheck(twoErrors, lint.RegexpMatch(`(unmatched paren`))
	}()
	assert(t,
		err != nil && strings.HasPrefix(err.Error(), "error parsing regexp"),
		fmt.Sprintf("%v", err))
}

func Example() {
	// Choose the default list of linters
	linters := lint.Default

	// Ignore all errors from the file unused.go.
	//
	// This is intended as an example of how to skip errors and not a
	// recommendation that you skip these kinds of errors.
	filteredLinters := lint.Skip(linters, lint.RegexpMatch(
		`unused\.go:4:2: a blank import`,
		`unused\.go:7:7: don't use underscores in Go names`,
	))

	// Verify all files under this package recursively.
	if err := filteredLinters.Check("./..."); err != nil {
		// Record lint failures.
		// Use t.Fatal(err) when running in a test
		log.Fatal(err)
	}
	// Output:
}
<commit_msg>mark unchecked error in test<commit_after>package lint_test

import (
	"testing"

	"log"

	"fmt"
	"reflect"
	"runtime/debug"

	"strings"

	"github.com/surullabs/lint"
	"github.com/surullabs/lint/checkers"
	"github.com/surullabs/lint/dupl"
)

func TestLint(t *testing.T) {
	linters := lint.Skip(
		lint.Default.With(dupl.Check{Threshold: 25}),
		// Ignore all errors from unused.go
		lint.RegexpMatch(`unused\.go`),
		// Ignore duplicates we're okay with.
		dupl.SkipTwo, dupl.Skip("golint.go:1,12"),
	)
	if err := linters.Check("./..."); err != nil {
		t.Fatal(err)
	}
}

type checkFn func(pkgs ...string) error

func (c checkFn) Check(pkgs ...string) error { return c(pkgs...) }

func assert(t *testing.T, cond bool, msg string) {
	if cond {
		return
	}
	t.Fatal(msg, string(debug.Stack()))
}

var (
	expectRecursive = checkFn(func(args ...string) error {
		if !reflect.DeepEqual(args, []string{"./..."}) {
			return fmt.Errorf("expected [./...], got %v", args)
		}
		return nil
	})

	twoErrors = checkFn(func(...string) error {
		return checkers.Error("err1", "err2")
	})

	ungroupedError = checkFn(func(...string) error {
		return fmt.Errorf("ungrouped: %d", 1)
	})
)

func TestGroup(t *testing.T) {
	gcheck := func(fn ...lint.Checker) error {
		return lint.Group(fn).Check("./...")
	}

	// All checks pass
	err := gcheck(expectRecursive)
	assert(t, err == nil, fmt.Sprintf("%v", err))

	err = gcheck(twoErrors)
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: err1\nlint_test.checkFn: err2",
		fmt.Sprintf("%v", err))

	err = gcheck(ungroupedError)
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: ungrouped: 1",
		fmt.Sprintf("%v", err))

	// Grouped
	err = gcheck(twoErrors, ungroupedError)
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: err1\nlint_test.checkFn: err2\nlint_test.checkFn: ungrouped: 1",
		fmt.Sprintf("%v", err))
}

type skipFunc func(err string) bool

func (s skipFunc) Skip(err string) bool { return s(err) }

func errorIs(str string) skipFunc {
	return skipFunc(func(err string) bool { return str == err })
}

func scheck(c lint.Checker, skippers ...lint.Skipper) error {
	return lint.Skip(c, skippers...).Check("./...")
}

func TestSkip(t *testing.T) {
	// All checks pass
	err := scheck(expectRecursive, errorIs("err1"))
	assert(t, err == nil, fmt.Sprintf("%v", err))

	err = scheck(twoErrors, errorIs("err1"))
	assert(t,
		err != nil && err.Error() == "err2",
		fmt.Sprintf("%v", err))

	// Skip ungrouped error
	err = scheck(ungroupedError, errorIs("ungrouped: 1"))
	assert(t, err == nil, fmt.Sprintf("%v", err))

	// Skip don't skip ungrouped error
	err = scheck(ungroupedError, errorIs("err1"))
	assert(t, err != nil && err.Error() == "ungrouped: 1", fmt.Sprintf("%v", err))

	// Grouped
	err = scheck(lint.Group{twoErrors, ungroupedError}, errorIs("lint_test.checkFn: err1"))
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: err2\nlint_test.checkFn: ungrouped: 1",
		fmt.Sprintf("%v", err))

}

func TestRegexpMatch(t *testing.T) {
	// regexp match
	skipRE := lint.RegexpMatch(`err1`, `ungrouped`)
	err := scheck(lint.Group{twoErrors, ungroupedError}, skipRE)
	assert(t,
		err != nil && err.Error() == "lint_test.checkFn: err2",
		fmt.Sprintf("%v", err))

	// panic on bad RE

	func() {
		defer func() {
			r := recover()
			if r != nil {
				err = fmt.Errorf("%v", r)
			}
		}()
		_ = scheck(twoErrors, lint.RegexpMatch(`(unmatched paren`))
	}()
	assert(t,
		err != nil && strings.HasPrefix(err.Error(), "error parsing regexp"),
		fmt.Sprintf("%v", err))
}

func Example() {
	// Choose the default list of linters
	linters := lint.Default

	// Ignore all errors from the file unused.go.
	//
	// This is intended as an example of how to skip errors and not a
	// recommendation that you skip these kinds of errors.
	filteredLinters := lint.Skip(linters, lint.RegexpMatch(
		`unused\.go:4:2: a blank import`,
		`unused\.go:7:7: don't use underscores in Go names`,
	))

	// Verify all files under this package recursively.
	if err := filteredLinters.Check("./..."); err != nil {
		// Record lint failures.
		// Use t.Fatal(err) when running in a test
		log.Fatal(err)
	}
	// Output:
}
<|endoftext|>


<commit_before>package list

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
	"text/tabwriter"

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

// arrayContains returns that arr contains str.
func arrayContains(arr []string, str string) bool {
	for _, v := range arr {
		if v == str {
			return true
		}
	}
	return false
}

// toggle select line (multi select)
func (l *ListInfo) toggle(newLine string) {
	tmpList := []string{}

	addFlag := true
	for _, selectedLine := range l.SelectName {
		if selectedLine != newLine {
			tmpList = append(tmpList, selectedLine)
		} else {
			addFlag = false
		}
	}
	if addFlag == true {
		tmpList = append(tmpList, newLine)
	}
	l.SelectName = []string{}
	l.SelectName = tmpList
}

func (l *ListInfo) allToggle(allFlag bool) {
	SelectedList := []string{}
	allSelectedList := []string{} // WARN: is not used
	// selectedList in allSelectedList
	for _, selectedLine := range l.SelectName {
		SelectedList = append(SelectedList, selectedLine)
	}

	// allFlag is False
	if allFlag == false {
		// On each lines that except a header line and are not selected line,
		// toggles left end fields
		for _, addLine := range l.ViewText[1:] {
			addName := strings.Fields(addLine)[0]
			if !arrayContains(SelectedList, addName) {
				allSelectedList = append(allSelectedList, addName)
				l.toggle(addName)
			}
		}
		return
	} else {
		// On each lines that except a header line, toggles left end fields
		for _, addLine := range l.ViewText[1:] {
			addName := strings.Fields(addLine)[0]
			l.toggle(addName)
		}
		return
	}
}

// Create view text (use text/tabwriter)
func (l *ListInfo) getText() {
	buffer := &bytes.Buffer{}
	tabWriterBuffer := new(tabwriter.Writer)
	tabWriterBuffer.Init(buffer, 0, 4, 8, ' ', 0)
	fmt.Fprintln(tabWriterBuffer, "ServerName \tConnect Infomation \tNote \t")

	// Create list table
	for _, key := range l.NameList {
		name := key
		conInfo := l.DataList.Server[key].User + "@" + l.DataList.Server[key].Addr
		note := l.DataList.Server[key].Note

		fmt.Fprintln(tabWriterBuffer, name+"\t"+conInfo+"\t"+note)
	}

	tabWriterBuffer.Flush()
	line, err := buffer.ReadString('\n')
	for err == nil {
		str := strings.Replace(line, "\t", " ", -1)
		l.DataText = append(l.DataText, str)
		line, err = buffer.ReadString('\n')
	}
}

func (l *ListInfo) getFilterText() {
	// Initialization ViewText
	l.ViewText = []string{}

	// SearchText Bounds Space
	keywords := strings.Fields(l.Keyword)
	r := l.DataText[1:]
	line := ""
	tmpText := []string{}
	l.ViewText = append(l.ViewText, l.DataText[0])

	// if No words
	if len(keywords) == 0 {
		l.ViewText = l.DataText
		return
	}

	for i := 0; i < len(keywords); i += 1 {
		lowKeyword := regexp.QuoteMeta(strings.ToLower(keywords[i]))
		re := regexp.MustCompile(lowKeyword)
		tmpText = []string{}

		for j := 0; j < len(r); j += 1 {
			line += string(r[j])
			if re.MatchString(strings.ToLower(line)) {
				tmpText = append(tmpText, line)
			}
			line = ""
		}
		r = tmpText
	}
	l.ViewText = append(l.ViewText, tmpText...)
	return
}

func (l *ListInfo) View() {
	if err := termbox.Init(); err != nil {
		panic(err)
	}
	defer termbox.Close()

	// enable termbox mouse input
	termbox.SetInputMode(termbox.InputMouse)

	l.getText()
	l.keyEvent()
}
<commit_msg>:bulb: add documentation comments<commit_after>package list

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
	"text/tabwriter"

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

// arrayContains returns that arr contains str.
func arrayContains(arr []string, str string) bool {
	for _, v := range arr {
		if v == str {
			return true
		}
	}
	return false
}

// toggle select line (multi select)
func (l *ListInfo) toggle(newLine string) {
	tmpList := []string{}

	addFlag := true
	for _, selectedLine := range l.SelectName {
		if selectedLine != newLine {
			tmpList = append(tmpList, selectedLine)
		} else {
			addFlag = false
		}
	}
	if addFlag == true {
		tmpList = append(tmpList, newLine)
	}
	l.SelectName = []string{}
	l.SelectName = tmpList
}

func (l *ListInfo) allToggle(allFlag bool) {
	SelectedList := []string{}
	allSelectedList := []string{} // WARN: is not used
	// selectedList in allSelectedList
	for _, selectedLine := range l.SelectName {
		SelectedList = append(SelectedList, selectedLine)
	}

	// allFlag is False
	if allFlag == false {
		// On each lines that except a header line and are not selected line,
		// toggles left end fields
		for _, addLine := range l.ViewText[1:] {
			addName := strings.Fields(addLine)[0]
			if !arrayContains(SelectedList, addName) {
				allSelectedList = append(allSelectedList, addName)
				l.toggle(addName)
			}
		}
		return
	} else {
		// On each lines that except a header line, toggles left end fields
		for _, addLine := range l.ViewText[1:] {
			addName := strings.Fields(addLine)[0]
			l.toggle(addName)
		}
		return
	}
}

// Create view text (use text/tabwriter)
func (l *ListInfo) getText() {
	buffer := &bytes.Buffer{}
	tabWriterBuffer := new(tabwriter.Writer)
	tabWriterBuffer.Init(buffer, 0, 4, 8, ' ', 0)
	fmt.Fprintln(tabWriterBuffer, "ServerName \tConnect Infomation \tNote \t")

	// Create list table
	for _, key := range l.NameList {
		name := key
		conInfo := l.DataList.Server[key].User + "@" + l.DataList.Server[key].Addr
		note := l.DataList.Server[key].Note

		fmt.Fprintln(tabWriterBuffer, name+"\t"+conInfo+"\t"+note)
	}

	tabWriterBuffer.Flush()
	line, err := buffer.ReadString('\n')
	for err == nil {
		str := strings.Replace(line, "\t", " ", -1)
		l.DataText = append(l.DataText, str)
		line, err = buffer.ReadString('\n')
	}
}

// getFilterText updates l.ViewText with matching keyword (ignore case).
// DataText sets ViewText if keyword is empty.
func (l *ListInfo) getFilterText() {
	// Initialization ViewText
	l.ViewText = []string{}

	// SearchText Bounds Space
	keywords := strings.Fields(l.Keyword)
	r := l.DataText[1:]
	line := ""
	tmpText := []string{}
	l.ViewText = append(l.ViewText, l.DataText[0])

	// if No words
	if len(keywords) == 0 {
		l.ViewText = l.DataText
		return
	}

	for i := 0; i < len(keywords); i += 1 {
		lowKeyword := regexp.QuoteMeta(strings.ToLower(keywords[i]))
		re := regexp.MustCompile(lowKeyword)
		tmpText = []string{}

		for j := 0; j < len(r); j += 1 {
			line += string(r[j])
			if re.MatchString(strings.ToLower(line)) {
				tmpText = append(tmpText, line)
			}
			line = ""
		}
		r = tmpText
	}
	l.ViewText = append(l.ViewText, tmpText...)
	return
}

func (l *ListInfo) View() {
	if err := termbox.Init(); err != nil {
		panic(err)
	}
	defer termbox.Close()

	// enable termbox mouse input
	termbox.SetInputMode(termbox.InputMouse)

	l.getText()
	l.keyEvent()
}
<|endoftext|>


<commit_before>package engine

import (
	"fmt"
	"testing"

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

func Test_Rect_Location(t *testing.T) {
	rect := Rect{
		x: 0,
		y: 0,
		w: 1,
		h: 5,
	}

	require.Equal(t, Location{
		{0, 0},
		{0, 1},
		{0, 2},
		{0, 3},
		{0, 4},
	}, rect.Location())
}

func Test_Rect_Dot_HorizontalRect(t *testing.T) {
	rect := Rect{
		x: 0,
		y: 0,
		w: 1,
		h: 5,
	}

	require.Equal(t, Dot{0, 0}, rect.Dot(0))
	require.Equal(t, Dot{0, 1}, rect.Dot(1))
	require.Equal(t, Dot{0, 2}, rect.Dot(2))
	require.Equal(t, Dot{0, 3}, rect.Dot(3))
	require.Equal(t, Dot{0, 4}, rect.Dot(4))
}

func Test_Rect_Dot_VerticalRect(t *testing.T) {
	rect := Rect{
		x: 0,
		y: 0,
		w: 5,
		h: 1,
	}

	require.Equal(t, Dot{0, 0}, rect.Dot(0))
	require.Equal(t, Dot{1, 0}, rect.Dot(1))
	require.Equal(t, Dot{2, 0}, rect.Dot(2))
	require.Equal(t, Dot{3, 0}, rect.Dot(3))
	require.Equal(t, Dot{4, 0}, rect.Dot(4))
}

func Test_Rect_Dot_VerticalRectWithXY(t *testing.T) {
	rect := Rect{
		x: 5,
		y: 5,
		w: 5,
		h: 1,
	}

	require.Equal(t, Dot{5, 5}, rect.Dot(0))
	require.Equal(t, Dot{6, 5}, rect.Dot(1))
	require.Equal(t, Dot{7, 5}, rect.Dot(2))
	require.Equal(t, Dot{8, 5}, rect.Dot(3))
	require.Equal(t, Dot{9, 5}, rect.Dot(4))
}

func Test_Rect_Dot_HorizontalRectWithXY(t *testing.T) {
	rect := Rect{
		x: 5,
		y: 5,
		w: 1,
		h: 5,
	}

	require.Equal(t, Dot{5, 5}, rect.Dot(0))
	require.Equal(t, Dot{5, 6}, rect.Dot(1))
	require.Equal(t, Dot{5, 7}, rect.Dot(2))
	require.Equal(t, Dot{5, 8}, rect.Dot(3))
	require.Equal(t, Dot{5, 9}, rect.Dot(4))
}

func Test_Rect_Dot_SquareRectWithXY(t *testing.T) {
	rect := Rect{
		x: 5,
		y: 5,
		w: 3,
		h: 3,
	}

	require.Equal(t, Dot{5, 5}, rect.Dot(0))
	require.Equal(t, Dot{6, 5}, rect.Dot(1))
	require.Equal(t, Dot{7, 5}, rect.Dot(2))
	require.Equal(t, Dot{5, 6}, rect.Dot(3))
	require.Equal(t, Dot{6, 6}, rect.Dot(4))
	require.Equal(t, Dot{7, 6}, rect.Dot(5))
	require.Equal(t, Dot{5, 7}, rect.Dot(6))
	require.Equal(t, Dot{6, 7}, rect.Dot(7))
	require.Equal(t, Dot{7, 7}, rect.Dot(8))
}

func Test_Rect_MarshalJSON(t *testing.T) {
	tests := []struct {
		rect Rect
		json []byte
	}{
		{
			Rect{},
			[]byte("[0,0,0,0]"),
		},
		{
			Rect{1, 2, 3, 4},
			[]byte("[1,2,3,4]"),
		},
		{
			Rect{4, 3, 2, 1},
			[]byte("[4,3,2,1]"),
		},
		{
			Rect{255, 200, 160, 100},
			[]byte("[255,200,160,100]"),
		},
		{
			Rect{255, 200, 160, 255},
			[]byte("[255,200,160,255]"),
		},
	}

	for i, test := range tests {
		actualJSON, err := test.rect.MarshalJSON()
		require.Nil(t, err, "test %d", i)
		require.Equal(t, test.json, actualJSON, "test %d", i)
	}
}

func Test_Rect_Width_ReturnsRectWidth(t *testing.T) {
	tests := []struct {
		rect Rect
	}{
		{Rect{0, 0, 10, 3}},
		{Rect{0, 0, 22, 4}},
		{Rect{0, 0, 123, 5}},
		{Rect{0, 0, 0, 233}},
	}

	for i, test := range tests {
		require.Equal(t, test.rect.w, test.rect.Width(), fmt.Sprintf("number: %d", i))
	}
}

func Test_Rect_Height_ReturnsRectHeight(t *testing.T) {
	tests := []struct {
		rect Rect
	}{
		{Rect{0, 0, 10, 3}},
		{Rect{0, 0, 22, 4}},
		{Rect{0, 0, 123, 5}},
		{Rect{0, 0, 0, 233}},
	}

	for i, test := range tests {
		require.Equal(t, test.rect.h, test.rect.Height(), fmt.Sprintf("number: %d", i))
	}
}

func Test_Rect_X_ReturnsRectX(t *testing.T) {
	tests := []struct {
		rect Rect
	}{
		{Rect{3, 23, 10, 3}},
		{Rect{32, 1, 22, 4}},
		{Rect{1, 32, 123, 5}},
		{Rect{42, 231, 0, 233}},
	}

	for i, test := range tests {
		require.Equal(t, test.rect.x, test.rect.X(), fmt.Sprintf("number: %d", i))
	}
}

func Test_Rect_Y_ReturnsRectY(t *testing.T) {
	tests := []struct {
		rect Rect
	}{
		{Rect{213, 231, 10, 3}},
		{Rect{23, 32, 22, 4}},
		{Rect{123, 132, 123, 5}},
		{Rect{22, 3, 0, 233}},
	}

	for i, test := range tests {
		require.Equal(t, test.rect.y, test.rect.Y(), fmt.Sprintf("number: %d", i))
	}
}

func Test_Rect_Dots_ReturnsDotList(t *testing.T) {
	tests := []struct {
		rect Rect
		dots []Dot
	}{
		{Rect{213, 231, 10, 3}, []Dot{
			// 0
			{213, 231}, {214, 231}, {215, 231}, {216, 231}, {217, 231},
			{218, 231}, {219, 231}, {220, 231}, {221, 231}, {222, 231},
			// 1
			{213, 232}, {214, 232}, {215, 232}, {216, 232}, {217, 232},
			{218, 232}, {219, 232}, {220, 232}, {221, 232}, {222, 232},
			// 2
			{213, 233}, {214, 233}, {215, 233}, {216, 233}, {217, 233},
			{218, 233}, {219, 233}, {220, 233}, {221, 233}, {222, 233},
		}},
		{Rect{23, 32, 3, 3}, []Dot{
			{23, 32}, {24, 32}, {25, 32},
			{23, 33}, {24, 33}, {25, 33},
			{23, 34}, {24, 34}, {25, 34},
		}},
		{Rect{123, 132, 1, 1}, []Dot{
			{123, 132},
		}},
		{Rect{22, 3, 0, 233}, []Dot{}},
		{Rect{22, 3, 123, 0}, []Dot{}},
		{Rect{22, 3, 0, 0}, []Dot{}},
	}

	for i, test := range tests {
		require.Equal(t, test.dots, test.rect.Dots(), fmt.Sprintf("number: %d", i))
	}
}
<commit_msg>Create test for engine.NewRect<commit_after>package engine

import (
	"fmt"
	"testing"

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

func Test_Rect_Location(t *testing.T) {
	rect := Rect{
		x: 0,
		y: 0,
		w: 1,
		h: 5,
	}

	require.Equal(t, Location{
		{0, 0},
		{0, 1},
		{0, 2},
		{0, 3},
		{0, 4},
	}, rect.Location())
}

func Test_Rect_Dot_HorizontalRect(t *testing.T) {
	rect := Rect{
		x: 0,
		y: 0,
		w: 1,
		h: 5,
	}

	require.Equal(t, Dot{0, 0}, rect.Dot(0))
	require.Equal(t, Dot{0, 1}, rect.Dot(1))
	require.Equal(t, Dot{0, 2}, rect.Dot(2))
	require.Equal(t, Dot{0, 3}, rect.Dot(3))
	require.Equal(t, Dot{0, 4}, rect.Dot(4))
}

func Test_Rect_Dot_VerticalRect(t *testing.T) {
	rect := Rect{
		x: 0,
		y: 0,
		w: 5,
		h: 1,
	}

	require.Equal(t, Dot{0, 0}, rect.Dot(0))
	require.Equal(t, Dot{1, 0}, rect.Dot(1))
	require.Equal(t, Dot{2, 0}, rect.Dot(2))
	require.Equal(t, Dot{3, 0}, rect.Dot(3))
	require.Equal(t, Dot{4, 0}, rect.Dot(4))
}

func Test_Rect_Dot_VerticalRectWithXY(t *testing.T) {
	rect := Rect{
		x: 5,
		y: 5,
		w: 5,
		h: 1,
	}

	require.Equal(t, Dot{5, 5}, rect.Dot(0))
	require.Equal(t, Dot{6, 5}, rect.Dot(1))
	require.Equal(t, Dot{7, 5}, rect.Dot(2))
	require.Equal(t, Dot{8, 5}, rect.Dot(3))
	require.Equal(t, Dot{9, 5}, rect.Dot(4))
}

func Test_Rect_Dot_HorizontalRectWithXY(t *testing.T) {
	rect := Rect{
		x: 5,
		y: 5,
		w: 1,
		h: 5,
	}

	require.Equal(t, Dot{5, 5}, rect.Dot(0))
	require.Equal(t, Dot{5, 6}, rect.Dot(1))
	require.Equal(t, Dot{5, 7}, rect.Dot(2))
	require.Equal(t, Dot{5, 8}, rect.Dot(3))
	require.Equal(t, Dot{5, 9}, rect.Dot(4))
}

func Test_Rect_Dot_SquareRectWithXY(t *testing.T) {
	rect := Rect{
		x: 5,
		y: 5,
		w: 3,
		h: 3,
	}

	require.Equal(t, Dot{5, 5}, rect.Dot(0))
	require.Equal(t, Dot{6, 5}, rect.Dot(1))
	require.Equal(t, Dot{7, 5}, rect.Dot(2))
	require.Equal(t, Dot{5, 6}, rect.Dot(3))
	require.Equal(t, Dot{6, 6}, rect.Dot(4))
	require.Equal(t, Dot{7, 6}, rect.Dot(5))
	require.Equal(t, Dot{5, 7}, rect.Dot(6))
	require.Equal(t, Dot{6, 7}, rect.Dot(7))
	require.Equal(t, Dot{7, 7}, rect.Dot(8))
}

func Test_Rect_MarshalJSON(t *testing.T) {
	tests := []struct {
		rect Rect
		json []byte
	}{
		{
			Rect{},
			[]byte("[0,0,0,0]"),
		},
		{
			Rect{1, 2, 3, 4},
			[]byte("[1,2,3,4]"),
		},
		{
			Rect{4, 3, 2, 1},
			[]byte("[4,3,2,1]"),
		},
		{
			Rect{255, 200, 160, 100},
			[]byte("[255,200,160,100]"),
		},
		{
			Rect{255, 200, 160, 255},
			[]byte("[255,200,160,255]"),
		},
	}

	for i, test := range tests {
		actualJSON, err := test.rect.MarshalJSON()
		require.Nil(t, err, "test %d", i)
		require.Equal(t, test.json, actualJSON, "test %d", i)
	}
}

func Test_Rect_Width_ReturnsRectWidth(t *testing.T) {
	tests := []struct {
		rect Rect
	}{
		{Rect{0, 0, 10, 3}},
		{Rect{0, 0, 22, 4}},
		{Rect{0, 0, 123, 5}},
		{Rect{0, 0, 0, 233}},
	}

	for i, test := range tests {
		require.Equal(t, test.rect.w, test.rect.Width(), fmt.Sprintf("number: %d", i))
	}
}

func Test_Rect_Height_ReturnsRectHeight(t *testing.T) {
	tests := []struct {
		rect Rect
	}{
		{Rect{0, 0, 10, 3}},
		{Rect{0, 0, 22, 4}},
		{Rect{0, 0, 123, 5}},
		{Rect{0, 0, 0, 233}},
	}

	for i, test := range tests {
		require.Equal(t, test.rect.h, test.rect.Height(), fmt.Sprintf("number: %d", i))
	}
}

func Test_Rect_X_ReturnsRectX(t *testing.T) {
	tests := []struct {
		rect Rect
	}{
		{Rect{3, 23, 10, 3}},
		{Rect{32, 1, 22, 4}},
		{Rect{1, 32, 123, 5}},
		{Rect{42, 231, 0, 233}},
	}

	for i, test := range tests {
		require.Equal(t, test.rect.x, test.rect.X(), fmt.Sprintf("number: %d", i))
	}
}

func Test_Rect_Y_ReturnsRectY(t *testing.T) {
	tests := []struct {
		rect Rect
	}{
		{Rect{213, 231, 10, 3}},
		{Rect{23, 32, 22, 4}},
		{Rect{123, 132, 123, 5}},
		{Rect{22, 3, 0, 233}},
	}

	for i, test := range tests {
		require.Equal(t, test.rect.y, test.rect.Y(), fmt.Sprintf("number: %d", i))
	}
}

func Test_Rect_Dots_ReturnsDotList(t *testing.T) {
	tests := []struct {
		rect Rect
		dots []Dot
	}{
		{Rect{213, 231, 10, 3}, []Dot{
			// 0
			{213, 231}, {214, 231}, {215, 231}, {216, 231}, {217, 231},
			{218, 231}, {219, 231}, {220, 231}, {221, 231}, {222, 231},
			// 1
			{213, 232}, {214, 232}, {215, 232}, {216, 232}, {217, 232},
			{218, 232}, {219, 232}, {220, 232}, {221, 232}, {222, 232},
			// 2
			{213, 233}, {214, 233}, {215, 233}, {216, 233}, {217, 233},
			{218, 233}, {219, 233}, {220, 233}, {221, 233}, {222, 233},
		}},
		{Rect{23, 32, 3, 3}, []Dot{
			{23, 32}, {24, 32}, {25, 32},
			{23, 33}, {24, 33}, {25, 33},
			{23, 34}, {24, 34}, {25, 34},
		}},
		{Rect{123, 132, 1, 1}, []Dot{
			{123, 132},
		}},
		{Rect{22, 3, 0, 233}, []Dot{}},
		{Rect{22, 3, 123, 0}, []Dot{}},
		{Rect{22, 3, 0, 0}, []Dot{}},
	}

	for i, test := range tests {
		require.Equal(t, test.dots, test.rect.Dots(), fmt.Sprintf("number: %d", i))
	}
}

func Test_NewRect_CreatesRectCorrectly(t *testing.T) {
	r := NewRect(1, 2, 3, 4)
	require.True(t, 1 == r.x)
	require.True(t, 2 == r.y)
	require.True(t, 3 == r.w)
	require.True(t, 4 == r.h)
}
<|endoftext|>