<commit_before>package autoroll

/*
	Convenience functions for retrieving AutoRoll CLs.
*/

import (
	"encoding/json"
	"errors"
	"fmt"
	"regexp"
	"sort"
	"time"

	"go.skia.org/infra/go/buildbucket"
	"go.skia.org/infra/go/comment"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/util"
)

const (
	AUTOROLL_STATUS_URL = "https://autoroll.skia.org/json/status"
	POLLER_ROLLS_LIMIT  = 10
	RECENT_ROLLS_LIMIT  = 200
	RIETVELD_URL        = "https://codereview.chromium.org"

	ROLL_RESULT_DRY_RUN_SUCCESS     = "dry run succeeded"
	ROLL_RESULT_DRY_RUN_FAILURE     = "dry run failed"
	ROLL_RESULT_DRY_RUN_IN_PROGRESS = "dry run in progress"
	ROLL_RESULT_IN_PROGRESS         = "in progress"
	ROLL_RESULT_SUCCESS             = "succeeded"
	ROLL_RESULT_FAILURE             = "failed"

	TRYBOT_CATEGORY_CQ = "cq"

	TRYBOT_STATUS_STARTED   = "STARTED"
	TRYBOT_STATUS_COMPLETED = "COMPLETED"
	TRYBOT_STATUS_SCHEDULED = "SCHEDULED"

	TRYBOT_RESULT_CANCELED = "CANCELED"
	TRYBOT_RESULT_SUCCESS  = "SUCCESS"
	TRYBOT_RESULT_FAILURE  = "FAILURE"
)

var (
	ROLL_REV_REGEX = regexp.MustCompile("Roll .+ ([0-9a-zA-Z]+)\\.\\.([0-9a-zA-Z]+) \\(\\d+ commit.*\\)\\.*")

	OPEN_ROLL_VALID_RESULTS = []string{
		ROLL_RESULT_DRY_RUN_FAILURE,
		ROLL_RESULT_DRY_RUN_IN_PROGRESS,
		ROLL_RESULT_DRY_RUN_SUCCESS,
		ROLL_RESULT_IN_PROGRESS,
	}

	DRY_RUN_RESULTS = []string{
		ROLL_RESULT_DRY_RUN_FAILURE,
		ROLL_RESULT_DRY_RUN_IN_PROGRESS,
		ROLL_RESULT_DRY_RUN_SUCCESS,
	}

	FAILURE_RESULTS = []string{
		ROLL_RESULT_DRY_RUN_FAILURE,
		ROLL_RESULT_FAILURE,
	}

	SUCCESS_RESULTS = []string{
		ROLL_RESULT_DRY_RUN_SUCCESS,
		ROLL_RESULT_SUCCESS,
	}
)

// AutoRollIssue is a struct containing the information we care about for
// AutoRoll CLs.
type AutoRollIssue struct {
	Closed            bool               `json:"closed"`
	Comments          []*comment.Comment `json:"comments"`
	Committed         bool               `json:"committed"`
	CommitQueue       bool               `json:"commitQueue"`
	CommitQueueDryRun bool               `json:"cqDryRun"`
	Created           time.Time          `json:"created"`
	Issue             int64              `json:"issue"`
	Modified          time.Time          `json:"modified"`
	Patchsets         []int64            `json:"patchSets"`
	Result            string             `json:"result"`
	RollingFrom       string             `json:"rollingFrom"`
	RollingTo         string             `json:"rollingTo"`
	Subject           string             `json:"subject"`
	TryResults        []*TryResult       `json:"tryResults"`
}

// Validate returns an error iff there is some problem with the issue.
func (i *AutoRollIssue) Validate() error {
	if i.Closed {
		if i.Result == ROLL_RESULT_IN_PROGRESS {
			return fmt.Errorf("AutoRollIssue cannot have a Result of %q if it is Closed.", ROLL_RESULT_IN_PROGRESS)
		}
	} else {
		if i.Committed {
			return errors.New("AutoRollIssue cannot be Committed without being Closed.")
		}
		if !util.In(i.Result, OPEN_ROLL_VALID_RESULTS) {
			return fmt.Errorf("AutoRollIssue which is not Closed must have as a Result one of: %v", OPEN_ROLL_VALID_RESULTS)
		}
	}
	return nil
}

// Copy returns a copy of the AutoRollIssue.
func (i *AutoRollIssue) Copy() *AutoRollIssue {
	var commentsCpy []*comment.Comment
	if i.Comments != nil {
		commentsCpy = make([]*comment.Comment, 0, len(i.Comments))
		for _, c := range i.Comments {
			commentsCpy = append(commentsCpy, c.Copy())
		}
	}
	var patchsetsCpy []int64
	if i.Patchsets != nil {
		patchsetsCpy = make([]int64, len(i.Patchsets))
		copy(patchsetsCpy, i.Patchsets)
	}
	var tryResultsCpy []*TryResult
	if i.TryResults != nil {
		tryResultsCpy = make([]*TryResult, 0, len(i.TryResults))
		for _, t := range i.TryResults {
			tryResultsCpy = append(tryResultsCpy, t.Copy())
		}
	}
	return &AutoRollIssue{
		Closed:            i.Closed,
		Comments:          commentsCpy,
		Committed:         i.Committed,
		CommitQueue:       i.CommitQueue,
		CommitQueueDryRun: i.CommitQueueDryRun,
		Created:           i.Created,
		Issue:             i.Issue,
		Modified:          i.Modified,
		Patchsets:         patchsetsCpy,
		Result:            i.Result,
		RollingFrom:       i.RollingFrom,
		RollingTo:         i.RollingTo,
		Subject:           i.Subject,
		TryResults:        tryResultsCpy,
	}
}

// ToGerritChangeInfo returns a GerritChangeInfo instance based on the
// AutoRollIssue.
func (a *AutoRollIssue) ToGerritChangeInfo() (*gerrit.ChangeInfo, error) {
	patchsets := make([]*gerrit.Revision, 0, len(a.Patchsets))
	for _, ps := range a.Patchsets {
		patchsets = append(patchsets, &gerrit.Revision{
			ID: fmt.Sprintf("%d", ps),
		})
	}
	return &gerrit.ChangeInfo{
		ChangeId:  fmt.Sprintf("%d", a.Issue),
		Patchsets: patchsets,
	}, nil
}

// FromGerritChangeInfo returns an AutoRollIssue instance based on the given
// gerrit.ChangeInfo.
func FromGerritChangeInfo(i *gerrit.ChangeInfo, fullHashFn func(string) (string, error), rollIntoAndroid bool) (*AutoRollIssue, error) {
	cq := false
	dryRun := false
	if rollIntoAndroid {
		rejected := false
		if _, ok := i.Labels[gerrit.PRESUBMIT_VERIFIED_LABEL]; ok {
			for _, lb := range i.Labels[gerrit.PRESUBMIT_VERIFIED_LABEL].All {
				if lb.Value == gerrit.PRESUBMIT_VERIFIED_LABEL_REJECTED {
					rejected = true
					break
				}
			}
		}
		if !rejected {
			if _, ok := i.Labels[gerrit.AUTOSUBMIT_LABEL]; ok {
				for _, lb := range i.Labels[gerrit.AUTOSUBMIT_LABEL].All {
					if lb.Value == gerrit.AUTOSUBMIT_LABEL_NONE {
						cq = true
						dryRun = true
					} else if lb.Value == gerrit.AUTOSUBMIT_LABEL_SUBMIT {
						cq = true
						dryRun = false
						break
					}
				}
			}
		}
	} else {
		for _, lb := range i.Labels[gerrit.COMMITQUEUE_LABEL].All {
			if lb.Value == gerrit.COMMITQUEUE_LABEL_DRY_RUN {
				cq = true
				dryRun = true
			} else if lb.Value == gerrit.COMMITQUEUE_LABEL_SUBMIT {
				cq = true
			}
		}
	}

	ps := make([]int64, 0, len(i.Patchsets))
	for _, p := range i.Patchsets {
		ps = append(ps, p.Number)
	}
	roll := &AutoRollIssue{
		Closed:            i.IsClosed(),
		Committed:         i.Committed,
		CommitQueue:       cq,
		CommitQueueDryRun: dryRun,
		Created:           i.Created,
		Issue:             i.Issue,
		Modified:          i.Updated,
		Patchsets:         ps,
		Subject:           i.Subject,
	}
	roll.Result = rollResult(roll)
	from, to, err := RollRev(roll.Subject, fullHashFn)
	if err != nil {
		return nil, err
	}
	roll.RollingFrom = from
	roll.RollingTo = to
	return roll, nil
}

// rollResult derives a result string for the roll.
func rollResult(roll *AutoRollIssue) string {
	if roll.Closed {
		if roll.Committed {
			return ROLL_RESULT_SUCCESS
		} else {
			return ROLL_RESULT_FAILURE
		}
	}
	return ROLL_RESULT_IN_PROGRESS
}

// RollRev returns the commit the given roll is rolling from and to.
func RollRev(subject string, fullHashFn func(string) (string, error)) (string, string, error) {
	matches := ROLL_REV_REGEX.FindStringSubmatch(subject)
	if matches == nil {
		return "", "", fmt.Errorf("No roll revision found in %q", subject)
	}
	if len(matches) != 3 {
		return "", "", fmt.Errorf("Unable to parse revisions from issue subject: %q", subject)
	}
	if fullHashFn == nil {
		return matches[1], matches[2], nil
	}
	from, err := fullHashFn(matches[1])
	if err != nil {
		return "", "", err
	}
	to, err := fullHashFn(matches[2])
	if err != nil {
		return "", "", err
	}
	return from, to, nil
}

// AllTrybotsFinished returns true iff all CQ trybots have finished for the
// given issue.
func (a *AutoRollIssue) AllTrybotsFinished() bool {
	for _, t := range a.TryResults {
		if t.Category != TRYBOT_CATEGORY_CQ {
			continue
		}
		if !t.Finished() {
			return false
		}
	}
	return true
}

// AllTrybotsSucceeded returns true iff all CQ trybots have succeeded for the
// given issue. Note that some trybots may fail and be retried, in which case a
// successful retry counts as a success.
func (a *AutoRollIssue) AllTrybotsSucceeded() bool {
	// For each trybot, find the most recent result.
	bots := map[string]*TryResult{}
	for _, t := range a.TryResults {
		if prev, ok := bots[t.Builder]; !ok || prev.Created.Before(t.Created) {
			bots[t.Builder] = t
		}
	}
	sklog.Infof("AllTrybotsSucceeded? %d results.", len(bots))
	for _, t := range bots {
		sklog.Infof("  %s: %s (%s)", t.Builder, t.Result, t.Category)
		if t.Category != TRYBOT_CATEGORY_CQ {
			sklog.Infof("    ...skipping, not a CQ bot (category %q not %q)", t.Category, TRYBOT_CATEGORY_CQ)
			continue
		}
		if !t.Succeeded() {
			sklog.Infof("    ...failed")
			return false
		}
	}
	return true
}

// Failed returns true iff the roll failed (including dry run failure).
func (a *AutoRollIssue) Failed() bool {
	return util.In(a.Result, FAILURE_RESULTS)
}

// Succeeded returns true iff the roll succeeded (including dry run success).
func (a *AutoRollIssue) Succeeded() bool {
	return util.In(a.Result, SUCCESS_RESULTS)
}

// TryResult is a struct which contains trybot result details.
type TryResult struct {
	Builder  string    `json:"builder"`
	Category string    `json:"category"`
	Created  time.Time `json:"created_ts"`
	Result   string    `json:"result"`
	Status   string    `json:"status"`
	Url      string    `json:"url"`
}

// TryResultFromBuildbucket returns a new TryResult based on a buildbucket.Build.
func TryResultFromBuildbucket(b *buildbucket.Build) (*TryResult, error) {
	var params struct {
		Builder    string `json:"builder_name"`
		Properties struct {
			Category string `json:"category"`
		} `json:"properties"`
	}
	if err := json.Unmarshal([]byte(b.ParametersJson), &params); err != nil {
		return nil, err
	}
	return &TryResult{
		Builder:  params.Builder,
		Category: params.Properties.Category,
		Created:  time.Time(b.Created),
		Result:   b.Result,
		Status:   b.Status,
		Url:      b.Url,
	}, nil
}

// TryResultsFromBuildbucket returns a slice of TryResults based on a slice of
// buildbucket.Builds.
func TryResultsFromBuildbucket(tries []*buildbucket.Build) ([]*TryResult, error) {
	res := make([]*TryResult, 0, len(tries))
	for _, t := range tries {
		tryResult, err := TryResultFromBuildbucket(t)
		if err != nil {
			return nil, err
		}
		res = append(res, tryResult)
	}
	sort.Sort(tryResultSlice(res))
	return res, nil
}

// Finished returns true iff the trybot is done running.
func (t TryResult) Finished() bool {
	return t.Status == TRYBOT_STATUS_COMPLETED
}

// Succeeded returns true iff the trybot completed successfully.
func (t TryResult) Succeeded() bool {
	return t.Finished() && t.Result == TRYBOT_RESULT_SUCCESS
}

// Copy returns a copy of the TryResult.
func (t *TryResult) Copy() *TryResult {
	return &TryResult{
		Builder:  t.Builder,
		Category: t.Category,
		Created:  t.Created,
		Result:   t.Result,
		Status:   t.Status,
		Url:      t.Url,
	}
}

type autoRollIssueSlice []*AutoRollIssue

func (s autoRollIssueSlice) Len() int           { return len(s) }
func (s autoRollIssueSlice) Less(i, j int) bool { return s[i].Modified.After(s[j].Modified) }
func (s autoRollIssueSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

type tryResultSlice []*TryResult

func (s tryResultSlice) Len() int           { return len(s) }
func (s tryResultSlice) Less(i, j int) bool { return s[i].Builder < s[j].Builder }
func (s tryResultSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

// GetTryResultsFromGerrit returns trybot results for the given roll.
func GetTryResultsFromGerrit(g *gerrit.Gerrit, roll *AutoRollIssue) ([]*TryResult, error) {
	tries, err := g.GetTrybotResults(roll.Issue, roll.Patchsets[0])
	if err != nil {
		return nil, err
	}
	return TryResultsFromBuildbucket(tries)
}
<commit_msg>[autoroll] Fix out-of-bounds error<commit_after>package autoroll

/*
	Convenience functions for retrieving AutoRoll CLs.
*/

import (
	"encoding/json"
	"errors"
	"fmt"
	"regexp"
	"sort"
	"time"

	"go.skia.org/infra/go/buildbucket"
	"go.skia.org/infra/go/comment"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/util"
)

const (
	AUTOROLL_STATUS_URL = "https://autoroll.skia.org/json/status"
	POLLER_ROLLS_LIMIT  = 10
	RECENT_ROLLS_LIMIT  = 200
	RIETVELD_URL        = "https://codereview.chromium.org"

	ROLL_RESULT_DRY_RUN_SUCCESS     = "dry run succeeded"
	ROLL_RESULT_DRY_RUN_FAILURE     = "dry run failed"
	ROLL_RESULT_DRY_RUN_IN_PROGRESS = "dry run in progress"
	ROLL_RESULT_IN_PROGRESS         = "in progress"
	ROLL_RESULT_SUCCESS             = "succeeded"
	ROLL_RESULT_FAILURE             = "failed"

	TRYBOT_CATEGORY_CQ = "cq"

	TRYBOT_STATUS_STARTED   = "STARTED"
	TRYBOT_STATUS_COMPLETED = "COMPLETED"
	TRYBOT_STATUS_SCHEDULED = "SCHEDULED"

	TRYBOT_RESULT_CANCELED = "CANCELED"
	TRYBOT_RESULT_SUCCESS  = "SUCCESS"
	TRYBOT_RESULT_FAILURE  = "FAILURE"
)

var (
	ROLL_REV_REGEX = regexp.MustCompile("Roll .+ ([0-9a-zA-Z]+)\\.\\.([0-9a-zA-Z]+) \\(\\d+ commit.*\\)\\.*")

	OPEN_ROLL_VALID_RESULTS = []string{
		ROLL_RESULT_DRY_RUN_FAILURE,
		ROLL_RESULT_DRY_RUN_IN_PROGRESS,
		ROLL_RESULT_DRY_RUN_SUCCESS,
		ROLL_RESULT_IN_PROGRESS,
	}

	DRY_RUN_RESULTS = []string{
		ROLL_RESULT_DRY_RUN_FAILURE,
		ROLL_RESULT_DRY_RUN_IN_PROGRESS,
		ROLL_RESULT_DRY_RUN_SUCCESS,
	}

	FAILURE_RESULTS = []string{
		ROLL_RESULT_DRY_RUN_FAILURE,
		ROLL_RESULT_FAILURE,
	}

	SUCCESS_RESULTS = []string{
		ROLL_RESULT_DRY_RUN_SUCCESS,
		ROLL_RESULT_SUCCESS,
	}
)

// AutoRollIssue is a struct containing the information we care about for
// AutoRoll CLs.
type AutoRollIssue struct {
	Closed            bool               `json:"closed"`
	Comments          []*comment.Comment `json:"comments"`
	Committed         bool               `json:"committed"`
	CommitQueue       bool               `json:"commitQueue"`
	CommitQueueDryRun bool               `json:"cqDryRun"`
	Created           time.Time          `json:"created"`
	Issue             int64              `json:"issue"`
	Modified          time.Time          `json:"modified"`
	Patchsets         []int64            `json:"patchSets"`
	Result            string             `json:"result"`
	RollingFrom       string             `json:"rollingFrom"`
	RollingTo         string             `json:"rollingTo"`
	Subject           string             `json:"subject"`
	TryResults        []*TryResult       `json:"tryResults"`
}

// Validate returns an error iff there is some problem with the issue.
func (i *AutoRollIssue) Validate() error {
	if i.Closed {
		if i.Result == ROLL_RESULT_IN_PROGRESS {
			return fmt.Errorf("AutoRollIssue cannot have a Result of %q if it is Closed.", ROLL_RESULT_IN_PROGRESS)
		}
	} else {
		if i.Committed {
			return errors.New("AutoRollIssue cannot be Committed without being Closed.")
		}
		if !util.In(i.Result, OPEN_ROLL_VALID_RESULTS) {
			return fmt.Errorf("AutoRollIssue which is not Closed must have as a Result one of: %v", OPEN_ROLL_VALID_RESULTS)
		}
	}
	return nil
}

// Copy returns a copy of the AutoRollIssue.
func (i *AutoRollIssue) Copy() *AutoRollIssue {
	var commentsCpy []*comment.Comment
	if i.Comments != nil {
		commentsCpy = make([]*comment.Comment, 0, len(i.Comments))
		for _, c := range i.Comments {
			commentsCpy = append(commentsCpy, c.Copy())
		}
	}
	var patchsetsCpy []int64
	if i.Patchsets != nil {
		patchsetsCpy = make([]int64, len(i.Patchsets))
		copy(patchsetsCpy, i.Patchsets)
	}
	var tryResultsCpy []*TryResult
	if i.TryResults != nil {
		tryResultsCpy = make([]*TryResult, 0, len(i.TryResults))
		for _, t := range i.TryResults {
			tryResultsCpy = append(tryResultsCpy, t.Copy())
		}
	}
	return &AutoRollIssue{
		Closed:            i.Closed,
		Comments:          commentsCpy,
		Committed:         i.Committed,
		CommitQueue:       i.CommitQueue,
		CommitQueueDryRun: i.CommitQueueDryRun,
		Created:           i.Created,
		Issue:             i.Issue,
		Modified:          i.Modified,
		Patchsets:         patchsetsCpy,
		Result:            i.Result,
		RollingFrom:       i.RollingFrom,
		RollingTo:         i.RollingTo,
		Subject:           i.Subject,
		TryResults:        tryResultsCpy,
	}
}

// ToGerritChangeInfo returns a GerritChangeInfo instance based on the
// AutoRollIssue.
func (a *AutoRollIssue) ToGerritChangeInfo() (*gerrit.ChangeInfo, error) {
	patchsets := make([]*gerrit.Revision, 0, len(a.Patchsets))
	for _, ps := range a.Patchsets {
		patchsets = append(patchsets, &gerrit.Revision{
			ID: fmt.Sprintf("%d", ps),
		})
	}
	return &gerrit.ChangeInfo{
		ChangeId:  fmt.Sprintf("%d", a.Issue),
		Patchsets: patchsets,
	}, nil
}

// FromGerritChangeInfo returns an AutoRollIssue instance based on the given
// gerrit.ChangeInfo.
func FromGerritChangeInfo(i *gerrit.ChangeInfo, fullHashFn func(string) (string, error), rollIntoAndroid bool) (*AutoRollIssue, error) {
	cq := false
	dryRun := false
	if rollIntoAndroid {
		rejected := false
		if _, ok := i.Labels[gerrit.PRESUBMIT_VERIFIED_LABEL]; ok {
			for _, lb := range i.Labels[gerrit.PRESUBMIT_VERIFIED_LABEL].All {
				if lb.Value == gerrit.PRESUBMIT_VERIFIED_LABEL_REJECTED {
					rejected = true
					break
				}
			}
		}
		if !rejected {
			if _, ok := i.Labels[gerrit.AUTOSUBMIT_LABEL]; ok {
				for _, lb := range i.Labels[gerrit.AUTOSUBMIT_LABEL].All {
					if lb.Value == gerrit.AUTOSUBMIT_LABEL_NONE {
						cq = true
						dryRun = true
					} else if lb.Value == gerrit.AUTOSUBMIT_LABEL_SUBMIT {
						cq = true
						dryRun = false
						break
					}
				}
			}
		}
	} else {
		if _, ok := i.Labels[gerrit.COMMITQUEUE_LABEL]; ok {
			for _, lb := range i.Labels[gerrit.COMMITQUEUE_LABEL].All {
				if lb.Value == gerrit.COMMITQUEUE_LABEL_DRY_RUN {
					cq = true
					dryRun = true
				} else if lb.Value == gerrit.COMMITQUEUE_LABEL_SUBMIT {
					cq = true
				}
			}
		}
	}

	ps := make([]int64, 0, len(i.Patchsets))
	for _, p := range i.Patchsets {
		ps = append(ps, p.Number)
	}
	roll := &AutoRollIssue{
		Closed:            i.IsClosed(),
		Committed:         i.Committed,
		CommitQueue:       cq,
		CommitQueueDryRun: dryRun,
		Created:           i.Created,
		Issue:             i.Issue,
		Modified:          i.Updated,
		Patchsets:         ps,
		Subject:           i.Subject,
	}
	roll.Result = rollResult(roll)
	from, to, err := RollRev(roll.Subject, fullHashFn)
	if err != nil {
		return nil, err
	}
	roll.RollingFrom = from
	roll.RollingTo = to
	return roll, nil
}

// rollResult derives a result string for the roll.
func rollResult(roll *AutoRollIssue) string {
	if roll.Closed {
		if roll.Committed {
			return ROLL_RESULT_SUCCESS
		} else {
			return ROLL_RESULT_FAILURE
		}
	}
	return ROLL_RESULT_IN_PROGRESS
}

// RollRev returns the commit the given roll is rolling from and to.
func RollRev(subject string, fullHashFn func(string) (string, error)) (string, string, error) {
	matches := ROLL_REV_REGEX.FindStringSubmatch(subject)
	if matches == nil {
		return "", "", fmt.Errorf("No roll revision found in %q", subject)
	}
	if len(matches) != 3 {
		return "", "", fmt.Errorf("Unable to parse revisions from issue subject: %q", subject)
	}
	if fullHashFn == nil {
		return matches[1], matches[2], nil
	}
	from, err := fullHashFn(matches[1])
	if err != nil {
		return "", "", err
	}
	to, err := fullHashFn(matches[2])
	if err != nil {
		return "", "", err
	}
	return from, to, nil
}

// AllTrybotsFinished returns true iff all CQ trybots have finished for the
// given issue.
func (a *AutoRollIssue) AllTrybotsFinished() bool {
	for _, t := range a.TryResults {
		if t.Category != TRYBOT_CATEGORY_CQ {
			continue
		}
		if !t.Finished() {
			return false
		}
	}
	return true
}

// AllTrybotsSucceeded returns true iff all CQ trybots have succeeded for the
// given issue. Note that some trybots may fail and be retried, in which case a
// successful retry counts as a success.
func (a *AutoRollIssue) AllTrybotsSucceeded() bool {
	// For each trybot, find the most recent result.
	bots := map[string]*TryResult{}
	for _, t := range a.TryResults {
		if prev, ok := bots[t.Builder]; !ok || prev.Created.Before(t.Created) {
			bots[t.Builder] = t
		}
	}
	sklog.Infof("AllTrybotsSucceeded? %d results.", len(bots))
	for _, t := range bots {
		sklog.Infof("  %s: %s (%s)", t.Builder, t.Result, t.Category)
		if t.Category != TRYBOT_CATEGORY_CQ {
			sklog.Infof("    ...skipping, not a CQ bot (category %q not %q)", t.Category, TRYBOT_CATEGORY_CQ)
			continue
		}
		if !t.Succeeded() {
			sklog.Infof("    ...failed")
			return false
		}
	}
	return true
}

// Failed returns true iff the roll failed (including dry run failure).
func (a *AutoRollIssue) Failed() bool {
	return util.In(a.Result, FAILURE_RESULTS)
}

// Succeeded returns true iff the roll succeeded (including dry run success).
func (a *AutoRollIssue) Succeeded() bool {
	return util.In(a.Result, SUCCESS_RESULTS)
}

// TryResult is a struct which contains trybot result details.
type TryResult struct {
	Builder  string    `json:"builder"`
	Category string    `json:"category"`
	Created  time.Time `json:"created_ts"`
	Result   string    `json:"result"`
	Status   string    `json:"status"`
	Url      string    `json:"url"`
}

// TryResultFromBuildbucket returns a new TryResult based on a buildbucket.Build.
func TryResultFromBuildbucket(b *buildbucket.Build) (*TryResult, error) {
	var params struct {
		Builder    string `json:"builder_name"`
		Properties struct {
			Category string `json:"category"`
		} `json:"properties"`
	}
	if err := json.Unmarshal([]byte(b.ParametersJson), &params); err != nil {
		return nil, err
	}
	return &TryResult{
		Builder:  params.Builder,
		Category: params.Properties.Category,
		Created:  time.Time(b.Created),
		Result:   b.Result,
		Status:   b.Status,
		Url:      b.Url,
	}, nil
}

// TryResultsFromBuildbucket returns a slice of TryResults based on a slice of
// buildbucket.Builds.
func TryResultsFromBuildbucket(tries []*buildbucket.Build) ([]*TryResult, error) {
	res := make([]*TryResult, 0, len(tries))
	for _, t := range tries {
		tryResult, err := TryResultFromBuildbucket(t)
		if err != nil {
			return nil, err
		}
		res = append(res, tryResult)
	}
	sort.Sort(tryResultSlice(res))
	return res, nil
}

// Finished returns true iff the trybot is done running.
func (t TryResult) Finished() bool {
	return t.Status == TRYBOT_STATUS_COMPLETED
}

// Succeeded returns true iff the trybot completed successfully.
func (t TryResult) Succeeded() bool {
	return t.Finished() && t.Result == TRYBOT_RESULT_SUCCESS
}

// Copy returns a copy of the TryResult.
func (t *TryResult) Copy() *TryResult {
	return &TryResult{
		Builder:  t.Builder,
		Category: t.Category,
		Created:  t.Created,
		Result:   t.Result,
		Status:   t.Status,
		Url:      t.Url,
	}
}

type autoRollIssueSlice []*AutoRollIssue

func (s autoRollIssueSlice) Len() int           { return len(s) }
func (s autoRollIssueSlice) Less(i, j int) bool { return s[i].Modified.After(s[j].Modified) }
func (s autoRollIssueSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

type tryResultSlice []*TryResult

func (s tryResultSlice) Len() int           { return len(s) }
func (s tryResultSlice) Less(i, j int) bool { return s[i].Builder < s[j].Builder }
func (s tryResultSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

// GetTryResultsFromGerrit returns trybot results for the given roll.
func GetTryResultsFromGerrit(g *gerrit.Gerrit, roll *AutoRollIssue) ([]*TryResult, error) {
	tries, err := g.GetTrybotResults(roll.Issue, roll.Patchsets[0])
	if err != nil {
		return nil, err
	}
	return TryResultsFromBuildbucket(tries)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"testing"

	"github.com/freneticmonkey/migrate/go/exec"
	"github.com/freneticmonkey/migrate/go/metadata"
	"github.com/freneticmonkey/migrate/go/migration"
	"github.com/freneticmonkey/migrate/go/mysql"
	"github.com/freneticmonkey/migrate/go/test"
	"github.com/freneticmonkey/migrate/go/util"
	"github.com/urfave/cli"
)

func TestValidate(t *testing.T) {
	var err error
	var result *cli.ExitError
	var projectDB test.ProjectDB
	var mgmtDB test.ManagementDB

	// Teardown()
	util.SetConfigTesting()

	testName := "TestValidate"

	// No project or version for this test
	project := ""
	version := ""

	// Configure testing data
	testConfig := test.GetTestConfig()
	dogsTbl := GetTableAddressDogs()

	////////////////////////////////////////////////////////
	// Configure source YAML files for Schema validation
	//

	test.WriteFile(
		"animals/dogs.yml",
		GetYAMLTableDogs(),
		0644,
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////
	// Configure MySQL db reads for Schema validation
	//

	// Configure the test databases
	// Setup the mock project database
	projectDB, err = test.CreateProjectDB(testName, t)

	if err == nil {
		// Connect to Project DB
		exec.SetProjectDB(projectDB.Db)
		mysql.Setup(testConfig)

		// Connect to Project DB
		mysql.SetProjectDB(projectDB.Db.Db)
	}

	// Configure the Mock Managment DB
	mgmtDB, err = test.CreateManagementDB(testName, t)

	if err == nil {
		// migration.Setup(mgmtDB.Db, 1)
		exec.Setup(mgmtDB.Db, 1, testConfig.Project.DB.ConnectString())
		migration.Setup(mgmtDB.Db, 1)
		metadata.Setup(mgmtDB.Db, 1)
	}

	// SHOW TABLES Query
	projectDB.ShowTables([]test.DBRow{{dogsTbl.Name}}, false)

	// SHOW CREATE TABLE Query
	projectDB.ShowCreateTable(dogsTbl.Name, GetMySQLCreateTableDogs())

	// ParseCreateTable which includes a Table.LoadDBMetadata() call
	mgmtDB.MetadataSelectName(
		dogsTbl.Name,
		test.GetDBRowMetadata(dogsTbl.Metadata),
		false,
	)

	mgmtDB.MetadataLoadAllTableMetadata(dogsTbl.Metadata.PropertyID,
		1,
		[]test.DBRow{
			test.GetDBRowMetadata(dogsTbl.Metadata),
			test.GetDBRowMetadata(dogsTbl.Columns[0].Metadata),
			test.GetDBRowMetadata(dogsTbl.PrimaryIndex.Metadata),
		},
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	result = validate(project, version, "mysql", testConfig)

	if result.ExitCode() > 0 {
		t.Errorf("TestValidate failed with error: %v", err)
		return
	}

	projectDB.ExpectionsMet(testName, t)

	mgmtDB.ExpectionsMet(testName, t)

	Teardown()

}

func TestGitCloneValidate(t *testing.T) {
	var err error
	var result *cli.ExitError
	var projectDB test.ProjectDB
	var mgmtDB test.ManagementDB

	util.SetConfigTesting()

	// Teardown()

	testName := "TestValidate"

	// No project or version for this test
	project := "animals"
	version := "abc123"

	// Configure testing data
	testConfig := test.GetTestConfig()
	dogsTbl := GetTableAddressDogs()

	////////////////////////////////////////////////////////
	// Configure Git Checkout

	var sparseExists bool
	var data []byte

	expectedSparseFile := `schema/*
schemaTwo/*`

	// Configure unit test shell
	util.Config(testConfig)

	checkoutPath := util.WorkingSubDir(testConfig.Project.Name)

	// Build Git Commands

	shell := util.GetShell().(*util.MockShellExecutor)

	// init
	params := []string{
		"-C",
		checkoutPath,
		"init",
	}
	shell.ExpectExec("git", params, "", nil)

	// git add remote
	params = []string{
		"-C",
		checkoutPath,
		"remote",
		"add",
		"-f",
		"origin",
		testConfig.Project.Schema.Url,
	}
	shell.ExpectExec("git", params, "", nil)

	// git config sparse checkout
	params = []string{
		"-C",
		checkoutPath,
		"config",
		"core.sparseCheckout",
		"true",
	}
	shell.ExpectExec("git", params, "", nil)

	// Checkout
	// git config sparse checkout
	params = []string{
		"-C",
		checkoutPath,
		"checkout",
		testConfig.Project.Schema.Version,
	}
	shell.ExpectExec("git", params, "", nil)

	//
	////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////
	// Configure source YAML files for Schema validation
	//

	test.WriteFile(
		"animals/dogs.yml",
		GetYAMLTableDogs(),
		0644,
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////
	// Configure MySQL db reads for Schema validation
	//

	// Configure the test databases
	// Setup the mock project database
	projectDB, err = test.CreateProjectDB(testName, t)

	if err == nil {
		// Connect to Project DB
		exec.SetProjectDB(projectDB.Db)
		mysql.Setup(testConfig)

		// Connect to Project DB
		mysql.SetProjectDB(projectDB.Db.Db)
	}

	// Configure the Mock Managment DB
	mgmtDB, err = test.CreateManagementDB(testName, t)

	if err == nil {
		// migration.Setup(mgmtDB.Db, 1)
		exec.Setup(mgmtDB.Db, 1, testConfig.Project.DB.ConnectString())
		migration.Setup(mgmtDB.Db, 1)
		metadata.Setup(mgmtDB.Db, 1)
	}

	// SHOW TABLES Query
	projectDB.ShowTables([]test.DBRow{{dogsTbl.Name}}, false)

	// SHOW CREATE TABLE Query
	projectDB.ShowCreateTable(dogsTbl.Name, GetMySQLCreateTableDogs())

	// ParseCreateTable which includes a Table.LoadDBMetadata() call
	mgmtDB.MetadataSelectName(
		dogsTbl.Name,
		test.GetDBRowMetadata(dogsTbl.Metadata),
		false,
	)

	mgmtDB.MetadataLoadAllTableMetadata(dogsTbl.Metadata.PropertyID,
		1,
		[]test.DBRow{
			test.GetDBRowMetadata(dogsTbl.Metadata),
			test.GetDBRowMetadata(dogsTbl.Columns[0].Metadata),
			test.GetDBRowMetadata(dogsTbl.PrimaryIndex.Metadata),
		},
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	result = validate(project, version, "mysql", testConfig)

	if result.ExitCode() > 0 {
		t.Errorf("TestValidate failed with error: %v", err)
		return
	}

	////////////////////////////////////////////////////////
	// DB was accessed correctly
	projectDB.ExpectionsMet(testName, t)
	mgmtDB.ExpectionsMet(testName, t)

	////////////////////////////////////////////////////////
	// Git was checked out correctly

	if err != nil {
		t.Errorf("%s FAILED with error: %v", testName, err)
		return
	}

	// Validate that the sparse checkout file was created correctly
	sparseFile := fmt.Sprintf("%s/.git/info/sparse-checkout", checkoutPath)
	sparseExists, err = util.FileExists(sparseFile)

	if err != nil {
		t.Errorf("%s FAILED: there was an error while reading the sparse checkut file at path: [%s] with error: [%v]", testName, sparseFile, err)
		return
	}

	if !sparseExists {
		t.Errorf("%s FAILED: sparse checkut file missing from path: [%s]", testName, sparseFile)
		return
	}

	data, err = util.ReadFile(sparseFile)
	sparseData := string(data)

	if sparseData != expectedSparseFile {
		t.Errorf("%s FAILED: sparse data file contents: [%s] doesn't match expected contents: [%s]", testName, sparseData, expectedSparseFile)
		return
	}

	// Ensure that all of the anticipated shell calls were made
	if err = shell.ExpectationsWereMet(); err != nil {
		t.Errorf("%s FAILED: Not all shell commands were executed: error [%v]", testName, err)
	}

	Teardown()
}
<commit_msg>Added additional tests for YAML and MySQL specific validation<commit_after>package cmd

import (
	"fmt"
	"testing"

	"github.com/freneticmonkey/migrate/go/exec"
	"github.com/freneticmonkey/migrate/go/metadata"
	"github.com/freneticmonkey/migrate/go/migration"
	"github.com/freneticmonkey/migrate/go/mysql"
	"github.com/freneticmonkey/migrate/go/test"
	"github.com/freneticmonkey/migrate/go/util"
	"github.com/urfave/cli"
)

func TestValidate(t *testing.T) {
	var err error
	var result *cli.ExitError
	var projectDB test.ProjectDB
	var mgmtDB test.ManagementDB

	// Teardown()
	util.SetConfigTesting()

	testName := "TestValidate"

	// No project or version for this test
	project := ""
	version := ""

	// Configure testing data
	testConfig := test.GetTestConfig()
	dogsTbl := GetTableAddressDogs()

	////////////////////////////////////////////////////////
	// Configure source YAML files for Schema validation
	//

	test.WriteFile(
		"animals/dogs.yml",
		GetYAMLTableDogs(),
		0644,
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////
	// Configure MySQL db reads for Schema validation
	//

	// Configure the test databases
	// Setup the mock project database
	projectDB, err = test.CreateProjectDB(testName, t)

	if err == nil {
		// Connect to Project DB
		exec.SetProjectDB(projectDB.Db)
		mysql.Setup(testConfig)

		// Connect to Project DB
		mysql.SetProjectDB(projectDB.Db.Db)
	}

	// Configure the Mock Managment DB
	mgmtDB, err = test.CreateManagementDB(testName, t)

	if err == nil {
		// migration.Setup(mgmtDB.Db, 1)
		exec.Setup(mgmtDB.Db, 1, testConfig.Project.DB.ConnectString())
		migration.Setup(mgmtDB.Db, 1)
		metadata.Setup(mgmtDB.Db, 1)
	}

	// SHOW TABLES Query
	projectDB.ShowTables([]test.DBRow{{dogsTbl.Name}}, false)

	// SHOW CREATE TABLE Query
	projectDB.ShowCreateTable(dogsTbl.Name, GetMySQLCreateTableDogs())

	// ParseCreateTable which includes a Table.LoadDBMetadata() call
	mgmtDB.MetadataSelectName(
		dogsTbl.Name,
		test.GetDBRowMetadata(dogsTbl.Metadata),
		false,
	)

	mgmtDB.MetadataLoadAllTableMetadata(dogsTbl.Metadata.PropertyID,
		1,
		[]test.DBRow{
			test.GetDBRowMetadata(dogsTbl.Metadata),
			test.GetDBRowMetadata(dogsTbl.Columns[0].Metadata),
			test.GetDBRowMetadata(dogsTbl.PrimaryIndex.Metadata),
		},
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	result = validate(project, version, "both", testConfig)

	if result.ExitCode() > 0 {
		t.Errorf("TestValidate failed with error: %v", err)
		return
	}

	projectDB.ExpectionsMet(testName, t)

	mgmtDB.ExpectionsMet(testName, t)

	Teardown()

}

func TestValidateYAML(t *testing.T) {
	var err error
	var result *cli.ExitError

	// Teardown()
	util.SetConfigTesting()

	testName := "TestValidate"

	// No project or version for this test
	project := ""
	version := ""

	// Configure testing data
	testConfig := test.GetTestConfig()

	////////////////////////////////////////////////////////
	// Configure source YAML files for Schema validation
	//

	test.WriteFile(
		"animals/dogs.yml",
		GetYAMLTableDogs(),
		0644,
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	result = validate(project, version, "yaml", testConfig)

	if result.ExitCode() > 0 {
		t.Errorf("%s failed with error: %v", testName, err)
		return
	}

	Teardown()

}

func TestValidateMySQL(t *testing.T) {
	var err error
	var result *cli.ExitError
	var projectDB test.ProjectDB
	var mgmtDB test.ManagementDB

	// Teardown()
	util.SetConfigTesting()

	testName := "TestValidate"

	// No project or version for this test
	project := ""
	version := ""

	// Configure testing data
	testConfig := test.GetTestConfig()
	dogsTbl := GetTableAddressDogs()

	////////////////////////////////////////////////////////
	// Configure MySQL db reads for Schema validation
	//

	// Configure the test databases
	// Setup the mock project database
	projectDB, err = test.CreateProjectDB(testName, t)

	if err == nil {
		// Connect to Project DB
		exec.SetProjectDB(projectDB.Db)
		mysql.Setup(testConfig)

		// Connect to Project DB
		mysql.SetProjectDB(projectDB.Db.Db)
	}

	// Configure the Mock Managment DB
	mgmtDB, err = test.CreateManagementDB(testName, t)

	if err == nil {
		// migration.Setup(mgmtDB.Db, 1)
		exec.Setup(mgmtDB.Db, 1, testConfig.Project.DB.ConnectString())
		migration.Setup(mgmtDB.Db, 1)
		metadata.Setup(mgmtDB.Db, 1)
	}

	// SHOW TABLES Query
	projectDB.ShowTables([]test.DBRow{{dogsTbl.Name}}, false)

	// SHOW CREATE TABLE Query
	projectDB.ShowCreateTable(dogsTbl.Name, GetMySQLCreateTableDogs())

	// ParseCreateTable which includes a Table.LoadDBMetadata() call
	mgmtDB.MetadataSelectName(
		dogsTbl.Name,
		test.GetDBRowMetadata(dogsTbl.Metadata),
		false,
	)

	mgmtDB.MetadataLoadAllTableMetadata(dogsTbl.Metadata.PropertyID,
		1,
		[]test.DBRow{
			test.GetDBRowMetadata(dogsTbl.Metadata),
			test.GetDBRowMetadata(dogsTbl.Columns[0].Metadata),
			test.GetDBRowMetadata(dogsTbl.PrimaryIndex.Metadata),
		},
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	result = validate(project, version, "mysql", testConfig)

	if result.ExitCode() > 0 {
		t.Errorf("TestValidate failed with error: %v", err)
		return
	}

	projectDB.ExpectionsMet(testName, t)

	mgmtDB.ExpectionsMet(testName, t)

	Teardown()

}

func TestGitCloneValidate(t *testing.T) {
	var err error
	var result *cli.ExitError
	var projectDB test.ProjectDB
	var mgmtDB test.ManagementDB

	util.SetConfigTesting()

	// Teardown()

	testName := "TestValidate"

	// No project or version for this test
	project := "animals"
	version := "abc123"

	// Configure testing data
	testConfig := test.GetTestConfig()
	dogsTbl := GetTableAddressDogs()

	////////////////////////////////////////////////////////
	// Configure Git Checkout

	var sparseExists bool
	var data []byte

	expectedSparseFile := `schema/*
schemaTwo/*`

	// Configure unit test shell
	util.Config(testConfig)

	checkoutPath := util.WorkingSubDir(testConfig.Project.Name)

	// Build Git Commands

	shell := util.GetShell().(*util.MockShellExecutor)

	// init
	params := []string{
		"-C",
		checkoutPath,
		"init",
	}
	shell.ExpectExec("git", params, "", nil)

	// git add remote
	params = []string{
		"-C",
		checkoutPath,
		"remote",
		"add",
		"-f",
		"origin",
		testConfig.Project.Schema.Url,
	}
	shell.ExpectExec("git", params, "", nil)

	// git config sparse checkout
	params = []string{
		"-C",
		checkoutPath,
		"config",
		"core.sparseCheckout",
		"true",
	}
	shell.ExpectExec("git", params, "", nil)

	// Checkout
	// git config sparse checkout
	params = []string{
		"-C",
		checkoutPath,
		"checkout",
		testConfig.Project.Schema.Version,
	}
	shell.ExpectExec("git", params, "", nil)

	//
	////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////
	// Configure source YAML files for Schema validation
	//

	test.WriteFile(
		"animals/dogs.yml",
		GetYAMLTableDogs(),
		0644,
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////
	// Configure MySQL db reads for Schema validation
	//

	// Configure the test databases
	// Setup the mock project database
	projectDB, err = test.CreateProjectDB(testName, t)

	if err == nil {
		// Connect to Project DB
		exec.SetProjectDB(projectDB.Db)
		mysql.Setup(testConfig)

		// Connect to Project DB
		mysql.SetProjectDB(projectDB.Db.Db)
	}

	// Configure the Mock Managment DB
	mgmtDB, err = test.CreateManagementDB(testName, t)

	if err == nil {
		// migration.Setup(mgmtDB.Db, 1)
		exec.Setup(mgmtDB.Db, 1, testConfig.Project.DB.ConnectString())
		migration.Setup(mgmtDB.Db, 1)
		metadata.Setup(mgmtDB.Db, 1)
	}

	// SHOW TABLES Query
	projectDB.ShowTables([]test.DBRow{{dogsTbl.Name}}, false)

	// SHOW CREATE TABLE Query
	projectDB.ShowCreateTable(dogsTbl.Name, GetMySQLCreateTableDogs())

	// ParseCreateTable which includes a Table.LoadDBMetadata() call
	mgmtDB.MetadataSelectName(
		dogsTbl.Name,
		test.GetDBRowMetadata(dogsTbl.Metadata),
		false,
	)

	mgmtDB.MetadataLoadAllTableMetadata(dogsTbl.Metadata.PropertyID,
		1,
		[]test.DBRow{
			test.GetDBRowMetadata(dogsTbl.Metadata),
			test.GetDBRowMetadata(dogsTbl.Columns[0].Metadata),
			test.GetDBRowMetadata(dogsTbl.PrimaryIndex.Metadata),
		},
		false,
	)

	//
	//
	////////////////////////////////////////////////////////

	result = validate(project, version, "both", testConfig)

	if result.ExitCode() > 0 {
		t.Errorf("TestValidate failed with error: %v", err)
		return
	}

	////////////////////////////////////////////////////////
	// DB was accessed correctly
	projectDB.ExpectionsMet(testName, t)
	mgmtDB.ExpectionsMet(testName, t)

	////////////////////////////////////////////////////////
	// Git was checked out correctly

	if err != nil {
		t.Errorf("%s FAILED with error: %v", testName, err)
		return
	}

	// Validate that the sparse checkout file was created correctly
	sparseFile := fmt.Sprintf("%s/.git/info/sparse-checkout", checkoutPath)
	sparseExists, err = util.FileExists(sparseFile)

	if err != nil {
		t.Errorf("%s FAILED: there was an error while reading the sparse checkut file at path: [%s] with error: [%v]", testName, sparseFile, err)
		return
	}

	if !sparseExists {
		t.Errorf("%s FAILED: sparse checkut file missing from path: [%s]", testName, sparseFile)
		return
	}

	data, err = util.ReadFile(sparseFile)
	sparseData := string(data)

	if sparseData != expectedSparseFile {
		t.Errorf("%s FAILED: sparse data file contents: [%s] doesn't match expected contents: [%s]", testName, sparseData, expectedSparseFile)
		return
	}

	// Ensure that all of the anticipated shell calls were made
	if err = shell.ExpectationsWereMet(); err != nil {
		t.Errorf("%s FAILED: Not all shell commands were executed: error [%v]", testName, err)
	}

	Teardown()
}
<|endoftext|>


<commit_before>package metrics2

import (
	"sync"
	"time"

	"golang.org/x/net/context"

	"go.skia.org/infra/go/util"
)

const (
	MEASUREMENT_LIVENESS = "liveness"
)

// Liveness keeps a time-since-last-successful-update metric.
//
// The unit of the metrics is in seconds.
//
// It is used to keep track of periodic processes to make sure that they are running
// successfully. Every liveness metric should have a corresponding alert set up that
// will fire of the time-since-last-successful-update metric gets too large.
type Liveness struct {
	cancelFn             func()
	lastSuccessfulUpdate time.Time
	m                    *Int64Metric
	mtx                  sync.Mutex
}

// NewLiveness creates a new Liveness metric helper. The current value is
// reported at the given frequency; if the report frequency is zero, the value
// is only reported when it changes.
func (c *Client) NewLiveness(name string, tagsList ...map[string]string) *Liveness {
	// Make a copy of the tags and add the name.
	tags := util.AddParams(map[string]string{}, tagsList...)
	tags["name"] = name
	ctx, cancelFn := context.WithCancel(context.Background())
	l := &Liveness{
		cancelFn:             cancelFn,
		lastSuccessfulUpdate: time.Now(),
		m:                    c.GetInt64Metric(MEASUREMENT_LIVENESS, tags),
		mtx:                  sync.Mutex{},
	}
	go util.RepeatCtx(time.Minute, ctx, func() {
		for _ = range time.Tick(c.reportFrequency) {
			l.update()
		}
	})
	return l
}

// NewLiveness creates a new Liveness metric helper using the default client.
// The current value is reported at the given frequency; if the report frequency
// is zero, the value is only reported when it changes.
func NewLiveness(name string, tags ...map[string]string) *Liveness {
	return DefaultClient.NewLiveness(name, tags...)
}

// getLocked returns the current value of the Liveness. Assumes the caller holds a lock.
func (l *Liveness) getLocked() int64 {
	return int64(time.Since(l.lastSuccessfulUpdate).Seconds())
}

// Get returns the current value of the Liveness.
func (l *Liveness) Get() int64 {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	return l.getLocked()
}

// updateLocked sets the value of the Liveness. Assumes the caller holds a lock.
func (l *Liveness) updateLocked() {
	l.m.Update(l.getLocked())
}

// update sets the value of the Liveness.
func (l *Liveness) update() {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	l.updateLocked()
}

// Reset should be called when some work has been successfully completed.
func (l *Liveness) Reset() {
	l.ManualReset(time.Now())
}

// ManualReset sets the last-successful-update time of the Liveness to a
// specific value. Useful for tracking processes whose lifetimes are outside
// of that of the current process, but should not be needed in most cases.
func (l *Liveness) ManualReset(lastSuccessfulUpdate time.Time) {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	l.lastSuccessfulUpdate = lastSuccessfulUpdate
	l.updateLocked()
}

// Delete removes the Liveness from metrics.
func (l *Liveness) Delete() error {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	l.cancelFn()
	return l.m.Delete()
}
<commit_msg>Fix metrics2 Liveness goroutine leak<commit_after>package metrics2

import (
	"sync"
	"time"

	"golang.org/x/net/context"

	"go.skia.org/infra/go/util"
)

const (
	MEASUREMENT_LIVENESS = "liveness"
)

// Liveness keeps a time-since-last-successful-update metric.
//
// The unit of the metrics is in seconds.
//
// It is used to keep track of periodic processes to make sure that they are running
// successfully. Every liveness metric should have a corresponding alert set up that
// will fire of the time-since-last-successful-update metric gets too large.
type Liveness struct {
	cancelFn             func()
	lastSuccessfulUpdate time.Time
	m                    *Int64Metric
	mtx                  sync.Mutex
}

// NewLiveness creates a new Liveness metric helper. The current value is
// reported at the given frequency; if the report frequency is zero, the value
// is only reported when it changes.
func (c *Client) NewLiveness(name string, tagsList ...map[string]string) *Liveness {
	// Make a copy of the tags and add the name.
	tags := util.AddParams(map[string]string{}, tagsList...)
	tags["name"] = name
	ctx, cancelFn := context.WithCancel(context.Background())
	l := &Liveness{
		cancelFn:             cancelFn,
		lastSuccessfulUpdate: time.Now(),
		m:                    c.GetInt64Metric(MEASUREMENT_LIVENESS, tags),
		mtx:                  sync.Mutex{},
	}
	go util.RepeatCtx(c.reportFrequency, ctx, func() {
		l.update()
	})
	return l
}

// NewLiveness creates a new Liveness metric helper using the default client.
// The current value is reported at the given frequency; if the report frequency
// is zero, the value is only reported when it changes.
func NewLiveness(name string, tags ...map[string]string) *Liveness {
	return DefaultClient.NewLiveness(name, tags...)
}

// getLocked returns the current value of the Liveness. Assumes the caller holds a lock.
func (l *Liveness) getLocked() int64 {
	return int64(time.Since(l.lastSuccessfulUpdate).Seconds())
}

// Get returns the current value of the Liveness.
func (l *Liveness) Get() int64 {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	return l.getLocked()
}

// updateLocked sets the value of the Liveness. Assumes the caller holds a lock.
func (l *Liveness) updateLocked() {
	l.m.Update(l.getLocked())
}

// update sets the value of the Liveness.
func (l *Liveness) update() {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	l.updateLocked()
}

// Reset should be called when some work has been successfully completed.
func (l *Liveness) Reset() {
	l.ManualReset(time.Now())
}

// ManualReset sets the last-successful-update time of the Liveness to a
// specific value. Useful for tracking processes whose lifetimes are outside
// of that of the current process, but should not be needed in most cases.
func (l *Liveness) ManualReset(lastSuccessfulUpdate time.Time) {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	l.lastSuccessfulUpdate = lastSuccessfulUpdate
	l.updateLocked()
}

// Delete removes the Liveness from metrics.
func (l *Liveness) Delete() error {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	l.cancelFn()
	return l.m.Delete()
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"os"
)

type Stacktrace struct {
	Stack []uint64
	oldSP uint64
}

func (s *Stacktrace) Print(u Usercorn) {
	for i := len(s.Stack) - 1; i >= 0; i-- {
		addr := s.Stack[i]
		sym, _ := u.Symbolicate(addr)
		fmt.Fprintf(os.Stderr, "  0x%x %s\n", addr, sym)
	}
}

func (s *Stacktrace) Push(addr uint64) {
	s.Stack = append(s.Stack, addr)
}

func (s *Stacktrace) Pop() uint64 {
	if len(s.Stack) == 0 {
		return 0
	}
	ret := s.Stack[len(s.Stack)-1]
	s.Stack = s.Stack[len(s.Stack)-1:]
	return ret
}

func (s *Stacktrace) Update(addr, sp uint64) {
	if sp < s.oldSP {
		s.Push(addr)
	} else if s.oldSP < sp {
		s.Pop()
	}
	s.oldSP = sp
}
<commit_msg>include PC in stacktrace<commit_after>package models

import (
	"fmt"
	"os"
)

type Stacktrace struct {
	Stack []uint64
	oldSP uint64
}

func (s *Stacktrace) Print(u Usercorn) {
	pc, _ := u.RegRead(u.Arch().PC)
	stack := append(s.Stack, pc)
	for i := len(stack) - 1; i >= 0; i-- {
		addr := stack[i]
		sym, _ := u.Symbolicate(addr)
		fmt.Fprintf(os.Stderr, "  0x%x %s\n", addr, sym)
	}
}

func (s *Stacktrace) Push(addr uint64) {
	s.Stack = append(s.Stack, addr)
}

func (s *Stacktrace) Pop() uint64 {
	if len(s.Stack) == 0 {
		return 0
	}
	ret := s.Stack[len(s.Stack)-1]
	s.Stack = s.Stack[len(s.Stack)-1:]
	return ret
}

func (s *Stacktrace) Update(addr, sp uint64) {
	if sp < s.oldSP {
		s.Push(addr)
	} else if s.oldSP < sp {
		s.Pop()
	}
	s.oldSP = sp
}
<|endoftext|>


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

// This file tests types.Check by using it to
// typecheck the standard library and tests.

package types

import (
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/scanner"
	"go/token"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"
)

var verbose = flag.Bool("types.v", false, "verbose mode")

var (
	pkgCount int // number of packages processed
	start    = time.Now()
)

func TestStdlib(t *testing.T) {
	walkDirs(t, filepath.Join(runtime.GOROOT(), "src/pkg"))
	if *verbose {
		fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
	}
}

// firstComment returns the contents of the first comment in
// the given file, assuming there's one within the first KB.
func firstComment(filename string) string {
	f, err := os.Open(filename)
	if err != nil {
		return ""
	}
	defer f.Close()

	var src [1 << 10]byte // read at most 1KB
	n, _ := f.Read(src[:])

	var s scanner.Scanner
	s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil, scanner.ScanComments)
	for {
		_, tok, lit := s.Scan()
		switch tok {
		case token.COMMENT:
			// remove trailing */ of multi-line comment
			if lit[1] == '*' {
				lit = lit[:len(lit)-2]
			}
			return strings.TrimSpace(lit[2:])
		case token.EOF:
			return ""
		}
	}
}

func testTestDir(t *testing.T, path string, ignore ...string) {
	files, err := ioutil.ReadDir(path)
	if err != nil {
		t.Fatal(err)
	}

	excluded := make(map[string]bool)
	for _, filename := range ignore {
		excluded[filename] = true
	}

	fset := token.NewFileSet()
	for _, f := range files {
		// filter directory contents
		if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] {
			continue
		}

		// get per-file instructions
		expectErrors := false
		filename := filepath.Join(path, f.Name())
		if cmd := firstComment(filename); cmd != "" {
			switch cmd {
			case "skip", "compiledir":
				continue // ignore this file
			case "errorcheck":
				expectErrors = true
			}
		}

		// parse and type-check file
		file, err := parser.ParseFile(fset, filename, nil, 0)
		if err == nil {
			_, err = Check(filename, fset, []*ast.File{file})
		}

		if expectErrors {
			if err == nil {
				t.Errorf("expected errors but found none in %s", filename)
			}
		} else {
			if err != nil {
				t.Error(err)
			}
		}
	}
}

func TestStdtest(t *testing.T) {
	testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
		"cmplxdivide.go",          // also needs file cmplxdivide1.go - ignore
		"mapnan.go", "sigchld.go", // don't work on Windows; testTestDir should consult build tags
	)
}

func TestStdfixed(t *testing.T) {
	testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
		"bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks
		"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
		"issue3924.go", // TODO(gri) && and || produce bool result (not untyped bool)
		"issue4847.go", // TODO(gri) initialization cycle error not found
	)
}

func TestStdken(t *testing.T) {
	testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken"))
}

// Package paths of excluded packages.
var excluded = map[string]bool{
	"builtin": true,
}

// typecheck typechecks the given package files.
func typecheck(t *testing.T, path string, filenames []string) {
	fset := token.NewFileSet()

	// parse package files
	var files []*ast.File
	for _, filename := range filenames {
		file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
		if err != nil {
			// the parser error may be a list of individual errors; report them all
			if list, ok := err.(scanner.ErrorList); ok {
				for _, err := range list {
					t.Error(err)
				}
				return
			}
			t.Error(err)
			return
		}

		if *verbose {
			if len(files) == 0 {
				fmt.Println("package", file.Name.Name)
			}
			fmt.Println("\t", filename)
		}

		files = append(files, file)
	}

	// typecheck package files
	var conf Config
	conf.Error = func(err error) { t.Error(err) }
	conf.Check(path, fset, files, nil)
	pkgCount++
}

// pkgfiles returns the list of package files for the given directory.
func pkgfiles(t *testing.T, dir string) []string {
	ctxt := build.Default
	ctxt.CgoEnabled = false
	pkg, err := ctxt.ImportDir(dir, 0)
	if err != nil {
		if _, nogo := err.(*build.NoGoError); !nogo {
			t.Error(err)
		}
		return nil
	}
	if excluded[pkg.ImportPath] {
		return nil
	}
	var filenames []string
	for _, name := range pkg.GoFiles {
		filenames = append(filenames, filepath.Join(pkg.Dir, name))
	}
	for _, name := range pkg.TestGoFiles {
		filenames = append(filenames, filepath.Join(pkg.Dir, name))
	}
	return filenames
}

// Note: Could use filepath.Walk instead of walkDirs but that wouldn't
//       necessarily be shorter or clearer after adding the code to
//       terminate early for -short tests.

func walkDirs(t *testing.T, dir string) {
	// limit run time for short tests
	if testing.Short() && time.Since(start) >= 750*time.Millisecond {
		return
	}

	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		t.Error(err)
		return
	}

	// typecheck package in directory
	if files := pkgfiles(t, dir); files != nil {
		typecheck(t, dir, files)
	}

	// traverse subdirectories, but don't walk into testdata
	for _, fi := range fis {
		if fi.IsDir() && fi.Name() != "testdata" {
			walkDirs(t, filepath.Join(dir, fi.Name()))
		}
	}
}
<commit_msg>go.tools/go/types: update a TODO comment (not a TODO anymore)<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file tests types.Check by using it to
// typecheck the standard library and tests.

package types

import (
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/scanner"
	"go/token"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"
)

var verbose = flag.Bool("types.v", false, "verbose mode")

var (
	pkgCount int // number of packages processed
	start    = time.Now()
)

func TestStdlib(t *testing.T) {
	walkDirs(t, filepath.Join(runtime.GOROOT(), "src/pkg"))
	if *verbose {
		fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
	}
}

// firstComment returns the contents of the first comment in
// the given file, assuming there's one within the first KB.
func firstComment(filename string) string {
	f, err := os.Open(filename)
	if err != nil {
		return ""
	}
	defer f.Close()

	var src [1 << 10]byte // read at most 1KB
	n, _ := f.Read(src[:])

	var s scanner.Scanner
	s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil, scanner.ScanComments)
	for {
		_, tok, lit := s.Scan()
		switch tok {
		case token.COMMENT:
			// remove trailing */ of multi-line comment
			if lit[1] == '*' {
				lit = lit[:len(lit)-2]
			}
			return strings.TrimSpace(lit[2:])
		case token.EOF:
			return ""
		}
	}
}

func testTestDir(t *testing.T, path string, ignore ...string) {
	files, err := ioutil.ReadDir(path)
	if err != nil {
		t.Fatal(err)
	}

	excluded := make(map[string]bool)
	for _, filename := range ignore {
		excluded[filename] = true
	}

	fset := token.NewFileSet()
	for _, f := range files {
		// filter directory contents
		if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] {
			continue
		}

		// get per-file instructions
		expectErrors := false
		filename := filepath.Join(path, f.Name())
		if cmd := firstComment(filename); cmd != "" {
			switch cmd {
			case "skip", "compiledir":
				continue // ignore this file
			case "errorcheck":
				expectErrors = true
			}
		}

		// parse and type-check file
		file, err := parser.ParseFile(fset, filename, nil, 0)
		if err == nil {
			_, err = Check(filename, fset, []*ast.File{file})
		}

		if expectErrors {
			if err == nil {
				t.Errorf("expected errors but found none in %s", filename)
			}
		} else {
			if err != nil {
				t.Error(err)
			}
		}
	}
}

func TestStdtest(t *testing.T) {
	testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
		"cmplxdivide.go",          // also needs file cmplxdivide1.go - ignore
		"mapnan.go", "sigchld.go", // don't work on Windows; testTestDir should consult build tags
	)
}

func TestStdfixed(t *testing.T) {
	testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
		"bug223.go", "bug413.go", "bug459.go", // TODO(gri) complete initialization checks
		"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
		"issue3924.go", // incorrect test - see issue 6671
		"issue4847.go", // TODO(gri) initialization cycle error not found
	)
}

func TestStdken(t *testing.T) {
	testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken"))
}

// Package paths of excluded packages.
var excluded = map[string]bool{
	"builtin": true,
}

// typecheck typechecks the given package files.
func typecheck(t *testing.T, path string, filenames []string) {
	fset := token.NewFileSet()

	// parse package files
	var files []*ast.File
	for _, filename := range filenames {
		file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
		if err != nil {
			// the parser error may be a list of individual errors; report them all
			if list, ok := err.(scanner.ErrorList); ok {
				for _, err := range list {
					t.Error(err)
				}
				return
			}
			t.Error(err)
			return
		}

		if *verbose {
			if len(files) == 0 {
				fmt.Println("package", file.Name.Name)
			}
			fmt.Println("\t", filename)
		}

		files = append(files, file)
	}

	// typecheck package files
	var conf Config
	conf.Error = func(err error) { t.Error(err) }
	conf.Check(path, fset, files, nil)
	pkgCount++
}

// pkgfiles returns the list of package files for the given directory.
func pkgfiles(t *testing.T, dir string) []string {
	ctxt := build.Default
	ctxt.CgoEnabled = false
	pkg, err := ctxt.ImportDir(dir, 0)
	if err != nil {
		if _, nogo := err.(*build.NoGoError); !nogo {
			t.Error(err)
		}
		return nil
	}
	if excluded[pkg.ImportPath] {
		return nil
	}
	var filenames []string
	for _, name := range pkg.GoFiles {
		filenames = append(filenames, filepath.Join(pkg.Dir, name))
	}
	for _, name := range pkg.TestGoFiles {
		filenames = append(filenames, filepath.Join(pkg.Dir, name))
	}
	return filenames
}

// Note: Could use filepath.Walk instead of walkDirs but that wouldn't
//       necessarily be shorter or clearer after adding the code to
//       terminate early for -short tests.

func walkDirs(t *testing.T, dir string) {
	// limit run time for short tests
	if testing.Short() && time.Since(start) >= 750*time.Millisecond {
		return
	}

	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		t.Error(err)
		return
	}

	// typecheck package in directory
	if files := pkgfiles(t, dir); files != nil {
		typecheck(t, dir, files)
	}

	// traverse subdirectories, but don't walk into testdata
	for _, fi := range fis {
		if fi.IsDir() && fi.Name() != "testdata" {
			walkDirs(t, filepath.Join(dir, fi.Name()))
		}
	}
}
<|endoftext|>


<commit_before>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import (
	"fmt"
	"reflect"
)

func GetComputeImageCaiObject(d TerraformResourceData, config *Config) (Asset, error) {
	name, err := assetName(d, config, "//compute.googleapis.com/projects/{{project}}/global/images/{{name}}")
	if err != nil {
		return Asset{}, err
	}
	if obj, err := GetComputeImageApiObject(d, config); err == nil {
		return Asset{
			Name: name,
			Type: "compute.googleapis.com/Image",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest",
				DiscoveryName:        "Image",
				Data:                 obj,
			},
		}, nil
	} else {
		return Asset{}, err
	}
}

func GetComputeImageApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	descriptionProp, err := expandComputeImageDescription(d.Get("description"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
		obj["description"] = descriptionProp
	}
	diskSizeGbProp, err := expandComputeImageDiskSizeGb(d.Get("disk_size_gb"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("disk_size_gb"); !isEmptyValue(reflect.ValueOf(diskSizeGbProp)) && (ok || !reflect.DeepEqual(v, diskSizeGbProp)) {
		obj["diskSizeGb"] = diskSizeGbProp
	}
	familyProp, err := expandComputeImageFamily(d.Get("family"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("family"); !isEmptyValue(reflect.ValueOf(familyProp)) && (ok || !reflect.DeepEqual(v, familyProp)) {
		obj["family"] = familyProp
	}
	guestOsFeaturesProp, err := expandComputeImageGuestOsFeatures(d.Get("guest_os_features"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("guest_os_features"); !isEmptyValue(reflect.ValueOf(guestOsFeaturesProp)) && (ok || !reflect.DeepEqual(v, guestOsFeaturesProp)) {
		obj["guestOsFeatures"] = guestOsFeaturesProp
	}
	labelsProp, err := expandComputeImageLabels(d.Get("labels"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
		obj["labels"] = labelsProp
	}
	labelFingerprintProp, err := expandComputeImageLabelFingerprint(d.Get("label_fingerprint"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("label_fingerprint"); !isEmptyValue(reflect.ValueOf(labelFingerprintProp)) && (ok || !reflect.DeepEqual(v, labelFingerprintProp)) {
		obj["labelFingerprint"] = labelFingerprintProp
	}
	licensesProp, err := expandComputeImageLicenses(d.Get("licenses"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("licenses"); !isEmptyValue(reflect.ValueOf(licensesProp)) && (ok || !reflect.DeepEqual(v, licensesProp)) {
		obj["licenses"] = licensesProp
	}
	nameProp, err := expandComputeImageName(d.Get("name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
		obj["name"] = nameProp
	}
	rawDiskProp, err := expandComputeImageRawDisk(d.Get("raw_disk"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("raw_disk"); !isEmptyValue(reflect.ValueOf(rawDiskProp)) && (ok || !reflect.DeepEqual(v, rawDiskProp)) {
		obj["rawDisk"] = rawDiskProp
	}
	sourceDiskProp, err := expandComputeImageSourceDisk(d.Get("source_disk"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("source_disk"); !isEmptyValue(reflect.ValueOf(sourceDiskProp)) && (ok || !reflect.DeepEqual(v, sourceDiskProp)) {
		obj["sourceDisk"] = sourceDiskProp
	}

	return obj, nil
}

func expandComputeImageDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageDiskSizeGb(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageFamily(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageGuestOsFeatures(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		if raw == nil {
			continue
		}
		original := raw.(map[string]interface{})
		transformed := make(map[string]interface{})

		transformedType, err := expandComputeImageGuestOsFeaturesType(original["type"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedType); val.IsValid() && !isEmptyValue(val) {
			transformed["type"] = transformedType
		}

		req = append(req, transformed)
	}
	return req, nil
}

func expandComputeImageGuestOsFeaturesType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) {
	if v == nil {
		return map[string]string{}, nil
	}
	m := make(map[string]string)
	for k, val := range v.(map[string]interface{}) {
		m[k] = val.(string)
	}
	return m, nil
}

func expandComputeImageLabelFingerprint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageLicenses(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		f, err := parseGlobalFieldValue("licenses", raw.(string), "project", d, config, true)
		if err != nil {
			return nil, fmt.Errorf("Invalid value for licenses: %s", err)
		}
		req = append(req, f.RelativeLink())
	}
	return req, nil
}

func expandComputeImageName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageRawDisk(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedContainerType, err := expandComputeImageRawDiskContainerType(original["container_type"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedContainerType); val.IsValid() && !isEmptyValue(val) {
		transformed["containerType"] = transformedContainerType
	}

	transformedSha1, err := expandComputeImageRawDiskSha1(original["sha1"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedSha1); val.IsValid() && !isEmptyValue(val) {
		transformed["sha1Checksum"] = transformedSha1
	}

	transformedSource, err := expandComputeImageRawDiskSource(original["source"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedSource); val.IsValid() && !isEmptyValue(val) {
		transformed["source"] = transformedSource
	}

	return transformed, nil
}

func expandComputeImageRawDiskContainerType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageRawDiskSha1(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageRawDiskSource(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageSourceDisk(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	f, err := parseZonalFieldValue("disks", v.(string), "project", "zone", d, config, true)
	if err != nil {
		return nil, fmt.Errorf("Invalid value for source_disk: %s", err)
	}
	return f.RelativeLink(), nil
}
<commit_msg>make guest_os_features a set not a list<commit_after>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import (
	"fmt"
	"reflect"

	"github.com/hashicorp/terraform/helper/schema"
)

func GetComputeImageCaiObject(d TerraformResourceData, config *Config) (Asset, error) {
	name, err := assetName(d, config, "//compute.googleapis.com/projects/{{project}}/global/images/{{name}}")
	if err != nil {
		return Asset{}, err
	}
	if obj, err := GetComputeImageApiObject(d, config); err == nil {
		return Asset{
			Name: name,
			Type: "compute.googleapis.com/Image",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest",
				DiscoveryName:        "Image",
				Data:                 obj,
			},
		}, nil
	} else {
		return Asset{}, err
	}
}

func GetComputeImageApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	descriptionProp, err := expandComputeImageDescription(d.Get("description"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
		obj["description"] = descriptionProp
	}
	diskSizeGbProp, err := expandComputeImageDiskSizeGb(d.Get("disk_size_gb"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("disk_size_gb"); !isEmptyValue(reflect.ValueOf(diskSizeGbProp)) && (ok || !reflect.DeepEqual(v, diskSizeGbProp)) {
		obj["diskSizeGb"] = diskSizeGbProp
	}
	familyProp, err := expandComputeImageFamily(d.Get("family"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("family"); !isEmptyValue(reflect.ValueOf(familyProp)) && (ok || !reflect.DeepEqual(v, familyProp)) {
		obj["family"] = familyProp
	}
	guestOsFeaturesProp, err := expandComputeImageGuestOsFeatures(d.Get("guest_os_features"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("guest_os_features"); !isEmptyValue(reflect.ValueOf(guestOsFeaturesProp)) && (ok || !reflect.DeepEqual(v, guestOsFeaturesProp)) {
		obj["guestOsFeatures"] = guestOsFeaturesProp
	}
	labelsProp, err := expandComputeImageLabels(d.Get("labels"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
		obj["labels"] = labelsProp
	}
	labelFingerprintProp, err := expandComputeImageLabelFingerprint(d.Get("label_fingerprint"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("label_fingerprint"); !isEmptyValue(reflect.ValueOf(labelFingerprintProp)) && (ok || !reflect.DeepEqual(v, labelFingerprintProp)) {
		obj["labelFingerprint"] = labelFingerprintProp
	}
	licensesProp, err := expandComputeImageLicenses(d.Get("licenses"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("licenses"); !isEmptyValue(reflect.ValueOf(licensesProp)) && (ok || !reflect.DeepEqual(v, licensesProp)) {
		obj["licenses"] = licensesProp
	}
	nameProp, err := expandComputeImageName(d.Get("name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
		obj["name"] = nameProp
	}
	rawDiskProp, err := expandComputeImageRawDisk(d.Get("raw_disk"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("raw_disk"); !isEmptyValue(reflect.ValueOf(rawDiskProp)) && (ok || !reflect.DeepEqual(v, rawDiskProp)) {
		obj["rawDisk"] = rawDiskProp
	}
	sourceDiskProp, err := expandComputeImageSourceDisk(d.Get("source_disk"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("source_disk"); !isEmptyValue(reflect.ValueOf(sourceDiskProp)) && (ok || !reflect.DeepEqual(v, sourceDiskProp)) {
		obj["sourceDisk"] = sourceDiskProp
	}

	return obj, nil
}

func expandComputeImageDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageDiskSizeGb(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageFamily(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageGuestOsFeatures(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	v = v.(*schema.Set).List()
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		if raw == nil {
			continue
		}
		original := raw.(map[string]interface{})
		transformed := make(map[string]interface{})

		transformedType, err := expandComputeImageGuestOsFeaturesType(original["type"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedType); val.IsValid() && !isEmptyValue(val) {
			transformed["type"] = transformedType
		}

		req = append(req, transformed)
	}
	return req, nil
}

func expandComputeImageGuestOsFeaturesType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) {
	if v == nil {
		return map[string]string{}, nil
	}
	m := make(map[string]string)
	for k, val := range v.(map[string]interface{}) {
		m[k] = val.(string)
	}
	return m, nil
}

func expandComputeImageLabelFingerprint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageLicenses(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		f, err := parseGlobalFieldValue("licenses", raw.(string), "project", d, config, true)
		if err != nil {
			return nil, fmt.Errorf("Invalid value for licenses: %s", err)
		}
		req = append(req, f.RelativeLink())
	}
	return req, nil
}

func expandComputeImageName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageRawDisk(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedContainerType, err := expandComputeImageRawDiskContainerType(original["container_type"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedContainerType); val.IsValid() && !isEmptyValue(val) {
		transformed["containerType"] = transformedContainerType
	}

	transformedSha1, err := expandComputeImageRawDiskSha1(original["sha1"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedSha1); val.IsValid() && !isEmptyValue(val) {
		transformed["sha1Checksum"] = transformedSha1
	}

	transformedSource, err := expandComputeImageRawDiskSource(original["source"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedSource); val.IsValid() && !isEmptyValue(val) {
		transformed["source"] = transformedSource
	}

	return transformed, nil
}

func expandComputeImageRawDiskContainerType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageRawDiskSha1(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageRawDiskSource(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeImageSourceDisk(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	f, err := parseZonalFieldValue("disks", v.(string), "project", "zone", d, config, true)
	if err != nil {
		return nil, fmt.Errorf("Invalid value for source_disk: %s", err)
	}
	return f.RelativeLink(), nil
}
<|endoftext|>


<commit_before>package epochs

import (
	"testing"
	"time"
)

var GoogleCalendarTests = []struct {
	f   func(int64) time.Time
	num int64
	exp time.Time
}{
	{
		GoogleCalendar,
		1297899090,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		GoogleCalendar,
		1234567890,
		time.Date(2007, time.March, 16, 23, 31, 30, 0, time.UTC),
	},
}

func TestGoogleCalendar(t *testing.T) {
	for _, tt := range GoogleCalendarTests {

		obs := tt.f(tt.num)
		if obs != tt.exp {
			t.Errorf("%q(%q) => %q, want %q", tt.f, tt.num, obs, tt.exp)
		}
	}
}

var ToGoogleCalendarTests = []struct {
	f   func(time.Time) int64
	t   time.Time
	exp int64
}{
	{
		ToGoogleCalendar,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
		1297899090,
	},
	{
		ToGoogleCalendar,
		time.Date(2007, time.March, 16, 23, 31, 30, 0, time.UTC),
		1234567890,
	},
}

func TestToGoogleCalendar(t *testing.T) {
	for _, tt := range ToGoogleCalendarTests {

		obs := tt.f(tt.t)
		if obs != tt.exp {
			t.Errorf("%q(%q) => %q, want %q", tt.f, tt.t, obs, tt.exp)
		}
	}
}
<commit_msg>no function name<commit_after>package epochs

import (
	"testing"
	"time"
)

var GoogleCalendarTests = []struct {
	f   func(int64) time.Time
	num int64
	exp time.Time
}{
	{
		GoogleCalendar,
		1297899090,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
	},
	{
		GoogleCalendar,
		1234567890,
		time.Date(2007, time.March, 16, 23, 31, 30, 0, time.UTC),
	},
}

func TestGoogleCalendar(t *testing.T) {
	for _, tt := range GoogleCalendarTests {

		obs := tt.f(tt.num)
		if obs != tt.exp {
			t.Errorf("%q => %q, want %q", tt.num, obs, tt.exp)
		}
	}
}

var ToGoogleCalendarTests = []struct {
	f   func(time.Time) int64
	t   time.Time
	exp int64
}{
	{
		ToGoogleCalendar,
		time.Date(2009, time.February, 13, 23, 31, 30, 0, time.UTC),
		1297899090,
	},
	{
		ToGoogleCalendar,
		time.Date(2007, time.March, 16, 23, 31, 30, 0, time.UTC),
		1234567890,
	},
}

func TestToGoogleCalendar(t *testing.T) {
	for _, tt := range ToGoogleCalendarTests {

		obs := tt.f(tt.t)
		if obs != tt.exp {
			t.Errorf("%q => %q, want %q", tt.t, obs, tt.exp)
		}
	}
}
<|endoftext|>


<commit_before>package banner

import (
	toast "github.com/jacobmarshall/go-toast"
	"github.com/variadico/noti"
)

// Notify displays a Windows 10 Toast Notification.
func Notify(n noti.Params) error {
	notification := toast.Notification{
		AppID:   "noti",
		Title:   n.Title,
		Message: n.Message,
		Icon:    "",
		Actions: nil}

	return notification.Push()
}
<commit_msg>Fix Windows import<commit_after>package banner

import (
	"github.com/go-toast/toast"
	"github.com/variadico/noti"
)

// Notify displays a Windows 10 Toast Notification.
func Notify(n noti.Params) error {
	notification := toast.Notification{
		AppID:   "noti",
		Title:   n.Title,
		Message: n.Message,
		Icon:    "",
		Actions: nil}

	return notification.Push()
}
<|endoftext|>


<commit_before>package slack

import (
	"context"
	"errors"
	"log"
	"net/url"
	"os"
)

var logger *log.Logger // A logger that can be set by consumers
/*
  Added as a var so that we can change this for testing purposes
*/
var SLACK_API string = "https://slack.com/api/"
var SLACK_WEB_API_FORMAT string = "https://%s.slack.com/api/users.admin.%s?t=%s"

type SlackResponse struct {
	Ok    bool   `json:"ok"`
	Error string `json:"error"`
}

type AuthTestResponse struct {
	URL    string `json:"url"`
	Team   string `json:"team"`
	User   string `json:"user"`
	TeamID string `json:"team_id"`
	UserID string `json:"user_id"`
}

type authTestResponseFull struct {
	SlackResponse
	AuthTestResponse
}

type Client struct {
	config struct {
		token string
	}
	info  Info
	debug bool
}

// SetLogger let's library users supply a logger, so that api debugging
// can be logged along with the application's debugging info.
func SetLogger(l *log.Logger) {
	logger = l
}

func New(token string) *Client {
	s := &Client{}
	s.config.token = token
	return s
}

// AuthTest tests if the user is able to do authenticated requests or not
func (api *Client) AuthTest() (response *AuthTestResponse, error error) {
	return api.AuthTestContext(context.Background())
}

// AuthTestContext tests if the user is able to do authenticated requests or not with a custom context
func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestResponse, error error) {
	responseFull := &authTestResponseFull{}
	err := post(ctx, "auth.test", url.Values{"token": {api.config.token}}, responseFull, api.debug)
	if err != nil {
		return nil, err
	}
	if !responseFull.Ok {
		return nil, errors.New(responseFull.Error)
	}
	return &responseFull.AuthTestResponse, nil
}

// SetDebug switches the api into debug mode
// When in debug mode, it logs various info about what its doing
// If you ever use this in production, don't call SetDebug(true)
func (api *Client) SetDebug(debug bool) {
	api.debug = debug
	if debug && logger == nil {
		logger = log.New(os.Stdout, "nlopes/slack", log.LstdFlags|log.Lshortfile)
	}
}

func (api *Client) Debugf(format string, v ...interface{}) {
	if api.debug {
		logger.Printf(format, v...)
	}
}

func (api *Client) Debugln(v ...interface{}) {
	if api.debug {
		logger.Println(v...)
	}
}
<commit_msg>Changed Client.Debugf and Debugln to show from where they were called.<commit_after>package slack

import (
	"context"
	"errors"
	"log"
	"net/url"
	"os"
	"fmt"
)

var logger *log.Logger // A logger that can be set by consumers
/*
  Added as a var so that we can change this for testing purposes
*/
var SLACK_API string = "https://slack.com/api/"
var SLACK_WEB_API_FORMAT string = "https://%s.slack.com/api/users.admin.%s?t=%s"

type SlackResponse struct {
	Ok    bool   `json:"ok"`
	Error string `json:"error"`
}

type AuthTestResponse struct {
	URL    string `json:"url"`
	Team   string `json:"team"`
	User   string `json:"user"`
	TeamID string `json:"team_id"`
	UserID string `json:"user_id"`
}

type authTestResponseFull struct {
	SlackResponse
	AuthTestResponse
}

type Client struct {
	config struct {
		token string
	}
	info  Info
	debug bool
}

// SetLogger let's library users supply a logger, so that api debugging
// can be logged along with the application's debugging info.
func SetLogger(l *log.Logger) {
	logger = l
}

func New(token string) *Client {
	s := &Client{}
	s.config.token = token
	return s
}

// AuthTest tests if the user is able to do authenticated requests or not
func (api *Client) AuthTest() (response *AuthTestResponse, error error) {
	return api.AuthTestContext(context.Background())
}

// AuthTestContext tests if the user is able to do authenticated requests or not with a custom context
func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestResponse, error error) {
	responseFull := &authTestResponseFull{}
	err := post(ctx, "auth.test", url.Values{"token": {api.config.token}}, responseFull, api.debug)
	if err != nil {
		return nil, err
	}
	if !responseFull.Ok {
		return nil, errors.New(responseFull.Error)
	}
	return &responseFull.AuthTestResponse, nil
}

// SetDebug switches the api into debug mode
// When in debug mode, it logs various info about what its doing
// If you ever use this in production, don't call SetDebug(true)
func (api *Client) SetDebug(debug bool) {
	api.debug = debug
	if debug && logger == nil {
		logger = log.New(os.Stdout, "nlopes/slack", log.LstdFlags|log.Lshortfile)
	}
}

func (api *Client) Debugf(format string, v ...interface{}) {
	if api.debug {
		logger.Output(2,fmt.Sprintf(format, v...))
	}
}

func (api *Client) Debugln(v ...interface{}) {
	if api.debug {
		logger.Output(2, fmt.Sprintln(v...))
	}
}
<|endoftext|>


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

// copied from the http package in the standard library
package main

import (
	"bytes"
	"encoding/binary"
)

// The algorithm uses at most sniffLen bytes to make its decision.
const sniffLen = 512

// DetectContentType implements the algorithm described
// at http://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data.  It considers at most the
// first 512 bytes of data.  DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
func DetectContentType(data []byte) string {
	if len(data) > sniffLen {
		data = data[:sniffLen]
	}

	// Index of the first non-whitespace byte in data.
	firstNonWS := 0
	for ; firstNonWS < len(data) && isWS(data[firstNonWS]); firstNonWS++ {
	}

	for _, sig := range sniffSignatures {
		if ct := sig.match(data, firstNonWS); ct != "" {
			return ct
		}
	}

	return "application/octet-stream" // fallback
}

func isWS(b byte) bool {
	switch b {
	case '\t', '\n', '\x0c', '\r', ' ':
		return true
	}
	return false
}

type sniffSig interface {
	// match returns the MIME type of the data, or "" if unknown.
	match(data []byte, firstNonWS int) string
}

// Data matching the table in section 6.
var sniffSignatures = []sniffSig{
	htmlSig("<!DOCTYPE HTML"),
	htmlSig("<HTML"),
	htmlSig("<HEAD"),
	htmlSig("<SCRIPT"),
	htmlSig("<IFRAME"),
	htmlSig("<H1"),
	htmlSig("<DIV"),
	htmlSig("<FONT"),
	htmlSig("<TABLE"),
	htmlSig("<A"),
	htmlSig("<STYLE"),
	htmlSig("<TITLE"),
	htmlSig("<B"),
	htmlSig("<BODY"),
	htmlSig("<BR"),
	htmlSig("<P"),
	htmlSig("<!--"),

	&maskedSig{mask: []byte("\xFF\xFF\xFF\xFF\xFF"), pat: []byte("<?xml"), skipWS: true, ct: "text/xml; charset=utf-8"},

	&exactSig{[]byte("%PDF-"), "application/pdf"},
	&exactSig{[]byte("%!PS-Adobe-"), "application/postscript"},

	// UTF BOMs.
	&maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFE\xFF\x00\x00"), ct: "text/plain; charset=utf-16be"},
	&maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFF\xFE\x00\x00"), ct: "text/plain; charset=utf-16le"},
	&maskedSig{mask: []byte("\xFF\xFF\xFF\x00"), pat: []byte("\xEF\xBB\xBF\x00"), ct: "text/plain; charset=utf-8"},

	&exactSig{[]byte("GIF87a"), "image/gif"},
	&exactSig{[]byte("GIF89a"), "image/gif"},
	&exactSig{[]byte("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"), "image/png"},
	&exactSig{[]byte("\xFF\xD8\xFF"), "image/jpeg"},
	&exactSig{[]byte("BM"), "image/bmp"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WEBPVP"),
		ct:   "image/webp",
	},
	&exactSig{[]byte("\x00\x00\x01\x00"), "image/vnd.microsoft.icon"},
	&exactSig{[]byte("\x4F\x67\x67\x53\x00"), "application/ogg"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WAVE"),
		ct:   "audio/wave",
	},
	&exactSig{[]byte("\x1A\x45\xDF\xA3"), "video/webm"},
	&exactSig{[]byte("\x52\x61\x72\x20\x1A\x07\x00"), "application/x-rar-compressed"},
	&exactSig{[]byte("\x50\x4B\x03\x04"), "application/zip"},
	&exactSig{[]byte("\x1F\x8B\x08"), "application/x-gzip"},

	// TODO(dsymonds): Re-enable this when the spec is sorted w.r.t. MP4.
	//mp4Sig(0),

	textSig(0), // should be last
}

type exactSig struct {
	sig []byte
	ct  string
}

func (e *exactSig) match(data []byte, firstNonWS int) string {
	if bytes.HasPrefix(data, e.sig) {
		return e.ct
	}
	return ""
}

type maskedSig struct {
	mask, pat []byte
	skipWS    bool
	ct        string
}

func (m *maskedSig) match(data []byte, firstNonWS int) string {
	if m.skipWS {
		data = data[firstNonWS:]
	}
	if len(data) < len(m.mask) {
		return ""
	}
	for i, mask := range m.mask {
		db := data[i] & mask
		if db != m.pat[i] {
			return ""
		}
	}
	return m.ct
}

type htmlSig []byte

func (h htmlSig) match(data []byte, firstNonWS int) string {
	data = data[firstNonWS:]
	if len(data) < len(h)+1 {
		return ""
	}
	for i, b := range h {
		db := data[i]
		if 'A' <= b && b <= 'Z' {
			db &= 0xDF
		}
		if b != db {
			return ""
		}
	}
	// Next byte must be space or right angle bracket.
	if db := data[len(h)]; db != ' ' && db != '>' {
		return ""
	}
	return "text/html; charset=utf-8"
}

var mp4ftype = []byte("ftyp")

type mp4Sig int

func (mp4Sig) match(data []byte, firstNonWS int) string {
	// c.f. section 6.1.
	if len(data) < 8 {
		return ""
	}
	boxSize := int(binary.BigEndian.Uint32(data[:4]))
	if boxSize%4 != 0 || len(data) < boxSize {
		return ""
	}
	if !bytes.Equal(data[4:8], mp4ftype) {
		return ""
	}
	for st := 8; st < boxSize; st += 4 {
		if st == 12 {
			// minor version number
			continue
		}
		seg := string(data[st : st+3])
		switch seg {
		case "mp4", "iso", "M4V", "M4P", "M4B":
			return "video/mp4"
			/* The remainder are not in the spec.
			case "M4A":
				return "audio/mp4"
			case "3gp":
				return "video/3gpp"
			case "jp2":
				return "image/jp2" // JPEG 2000
			*/
		}
	}
	return ""
}

type textSig int

func (textSig) match(data []byte, firstNonWS int) string {
	// c.f. section 5, step 4.
	for _, b := range data[firstNonWS:] {
		switch {
		case 0x00 <= b && b <= 0x08,
			b == 0x0B,
			0x0E <= b && b <= 0x1A,
			0x1C <= b && b <= 0x1F:
			return ""
		}
	}
	return "text/plain; charset=utf-8"
}
<commit_msg>removes unused code from sniff<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// copied from the http package in the standard library
package main

import (
	"bytes"
)

// The algorithm uses at most sniffLen bytes to make its decision.
const sniffLen = 512

// DetectContentType implements the algorithm described
// at http://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data.  It considers at most the
// first 512 bytes of data.  DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
func DetectContentType(data []byte) string {
	if len(data) > sniffLen {
		data = data[:sniffLen]
	}

	// Index of the first non-whitespace byte in data.
	firstNonWS := 0
	for ; firstNonWS < len(data) && isWS(data[firstNonWS]); firstNonWS++ {
	}

	for _, sig := range sniffSignatures {
		if ct := sig.match(data, firstNonWS); ct != "" {
			return ct
		}
	}

	return "application/octet-stream" // fallback
}

func isWS(b byte) bool {
	switch b {
	case '\t', '\n', '\x0c', '\r', ' ':
		return true
	}
	return false
}

type sniffSig interface {
	// match returns the MIME type of the data, or "" if unknown.
	match(data []byte, firstNonWS int) string
}

// Data matching the table in section 6.
var sniffSignatures = []sniffSig{
	htmlSig("<!DOCTYPE HTML"),
	htmlSig("<HTML"),
	htmlSig("<HEAD"),
	htmlSig("<SCRIPT"),
	htmlSig("<IFRAME"),
	htmlSig("<H1"),
	htmlSig("<DIV"),
	htmlSig("<FONT"),
	htmlSig("<TABLE"),
	htmlSig("<A"),
	htmlSig("<STYLE"),
	htmlSig("<TITLE"),
	htmlSig("<B"),
	htmlSig("<BODY"),
	htmlSig("<BR"),
	htmlSig("<P"),
	htmlSig("<!--"),

	&maskedSig{mask: []byte("\xFF\xFF\xFF\xFF\xFF"), pat: []byte("<?xml"), skipWS: true, ct: "text/xml; charset=utf-8"},

	&exactSig{[]byte("%PDF-"), "application/pdf"},
	&exactSig{[]byte("%!PS-Adobe-"), "application/postscript"},

	// UTF BOMs.
	&maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFE\xFF\x00\x00"), ct: "text/plain; charset=utf-16be"},
	&maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFF\xFE\x00\x00"), ct: "text/plain; charset=utf-16le"},
	&maskedSig{mask: []byte("\xFF\xFF\xFF\x00"), pat: []byte("\xEF\xBB\xBF\x00"), ct: "text/plain; charset=utf-8"},

	&exactSig{[]byte("GIF87a"), "image/gif"},
	&exactSig{[]byte("GIF89a"), "image/gif"},
	&exactSig{[]byte("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"), "image/png"},
	&exactSig{[]byte("\xFF\xD8\xFF"), "image/jpeg"},
	&exactSig{[]byte("BM"), "image/bmp"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WEBPVP"),
		ct:   "image/webp",
	},
	&exactSig{[]byte("\x00\x00\x01\x00"), "image/vnd.microsoft.icon"},
	&exactSig{[]byte("\x4F\x67\x67\x53\x00"), "application/ogg"},
	&maskedSig{
		mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"),
		pat:  []byte("RIFF\x00\x00\x00\x00WAVE"),
		ct:   "audio/wave",
	},
	&exactSig{[]byte("\x1A\x45\xDF\xA3"), "video/webm"},
	&exactSig{[]byte("\x52\x61\x72\x20\x1A\x07\x00"), "application/x-rar-compressed"},
	&exactSig{[]byte("\x50\x4B\x03\x04"), "application/zip"},
	&exactSig{[]byte("\x1F\x8B\x08"), "application/x-gzip"},

	// TODO(dsymonds): Re-enable this when the spec is sorted w.r.t. MP4.
	//mp4Sig(0),

	textSig(0), // should be last
}

type exactSig struct {
	sig []byte
	ct  string
}

func (e *exactSig) match(data []byte, firstNonWS int) string {
	if bytes.HasPrefix(data, e.sig) {
		return e.ct
	}
	return ""
}

type maskedSig struct {
	mask, pat []byte
	skipWS    bool
	ct        string
}

func (m *maskedSig) match(data []byte, firstNonWS int) string {
	if m.skipWS {
		data = data[firstNonWS:]
	}
	if len(data) < len(m.mask) {
		return ""
	}
	for i, mask := range m.mask {
		db := data[i] & mask
		if db != m.pat[i] {
			return ""
		}
	}
	return m.ct
}

type htmlSig []byte

func (h htmlSig) match(data []byte, firstNonWS int) string {
	data = data[firstNonWS:]
	if len(data) < len(h)+1 {
		return ""
	}
	for i, b := range h {
		db := data[i]
		if 'A' <= b && b <= 'Z' {
			db &= 0xDF
		}
		if b != db {
			return ""
		}
	}
	// Next byte must be space or right angle bracket.
	if db := data[len(h)]; db != ' ' && db != '>' {
		return ""
	}
	return "text/html; charset=utf-8"
}

type textSig int

func (textSig) match(data []byte, firstNonWS int) string {
	// c.f. section 5, step 4.
	for _, b := range data[firstNonWS:] {
		switch {
		case 0x00 <= b && b <= 0x08,
			b == 0x0B,
			0x0E <= b && b <= 0x1A,
			0x1C <= b && b <= 0x1F:
			return ""
		}
	}
	return "text/plain; charset=utf-8"
}
<|endoftext|>