func (t Test) To() *resultstore.Action {
	a := t.Action.to()
	a.ActionType = &resultstore.Action_TestAction{
		TestAction: &resultstore.TestAction{
			Warnings:  warnings(t.Warnings),
			TestSuite: t.Suite.To(),
		},
	}
	a.Files = Files(t.Files)
	a.Properties = properties(t.Properties)
	return a
}
To converts the test into the corresponding ResultStore Action proto
package resultstore
import (
	"fmt"
	"net/url"
	"time"
	"github.com/golang/protobuf/ptypes/duration"
	"github.com/golang/protobuf/ptypes/timestamp"
	"github.com/golang/protobuf/ptypes/wrappers"
	resultstore "google.golang.org/genproto/googleapis/devtools/resultstore/v2"
)
type Invocation struct {
	Name string
	Project string
	Details string
	Duration time.Duration
	Start time.Time
	Files []File
	Properties []Property
	Status Status
	Description string
}
func URL(resourceName string) string {
	u := url.URL{
		Scheme: "https",
		Host:   "source.cloud.google.com",
		Path:   "results/" + resourceName,
	}
	return u.String()
}
func fromInvocation(rsi *resultstore.Invocation) Invocation {
	i := Invocation{
		Name:       rsi.Name,
		Files:      fromFiles(rsi.Files),
		Properties: fromProperties(rsi.Properties),
	}
	if ia := rsi.InvocationAttributes; ia != nil {
		i.Project = ia.ProjectId
		i.Description = ia.Description
	}
	if rsi.Timing != nil {
		i.Start, i.Duration = fromTiming(rsi.Timing)
	}
	i.Status, i.Description = fromStatus(rsi.StatusAttributes)
	return i
}
func (i Invocation) To() *resultstore.Invocation {
	inv := resultstore.Invocation{
		Name:             i.Name,
		Timing:           timing(i.Start, i.Duration),
		StatusAttributes: status(i.Status, i.Description),
		Files:            Files(i.Files),
		Properties:       properties(i.Properties),
	}
	if i.Project != "" || i.Details != "" {
		inv.InvocationAttributes = &resultstore.InvocationAttributes{
			ProjectId:   i.Project,
			Description: i.Details,
		}
	}
	return &inv
}
func dur(d time.Duration) *duration.Duration {
	return &duration.Duration{
		Seconds: int64(d / time.Second),
		Nanos:   int32(d % time.Second),
	}
}
func stamp(when time.Time) *timestamp.Timestamp {
	if when.IsZero() {
		return nil
	}
	return &timestamp.Timestamp{
		Seconds: when.Unix(),
		Nanos:   int32(when.UnixNano() % int64(time.Second)),
	}
}
func protoTimeToGoTime(t *timestamp.Timestamp) time.Time {
	return time.Unix(t.Seconds, int64(t.Nanos))
}
func protoDurationToGoDuration(d *duration.Duration) time.Duration {
	return time.Duration(d.Seconds)*time.Second + time.Duration(d.Nanos)*time.Nanosecond
}
func fromTiming(t *resultstore.Timing) (time.Time, time.Duration) {
	var when time.Time
	var dur time.Duration
	if t == nil {
		return when, dur
	}
	if s := t.StartTime; s != nil {
		when = protoTimeToGoTime(s)
	}
	if d := t.Duration; d != nil {
		dur = protoDurationToGoDuration(d)
	}
	return when, dur
}
func timing(when time.Time, d time.Duration) *resultstore.Timing {
	never := when.IsZero()
	if never && d == 0 {
		return nil
	}
	rst := resultstore.Timing{}
	if !never {
		rst.StartTime = stamp(when)
	}
	if d > 0 {
		rst.Duration = dur(d)
	}
	return &rst
}
type Failure struct {
	Message string
	Type string
	Stack string
	Expected []string
	Actual []string
}
func fromFailures(tfs []*resultstore.TestFailure) []Failure {
	var ret []Failure
	for _, tf := range tfs {
		ret = append(ret, Failure{
			Message:  tf.FailureMessage,
			Type:     tf.ExceptionType,
			Stack:    tf.StackTrace,
			Expected: tf.Expected,
			Actual:   tf.Actual,
		})
	}
	return ret
}
func (f Failure) To() *resultstore.TestFailure {
	return &resultstore.TestFailure{
		FailureMessage: f.Message,
		ExceptionType:  f.Type,
		StackTrace:     f.Stack,
		Expected:       f.Expected,
		Actual:         f.Actual,
	}
}
func failures(fs []Failure) []*resultstore.TestFailure {
	var rstfs []*resultstore.TestFailure
	for _, f := range fs {
		rstfs = append(rstfs, f.To())
	}
	return rstfs
}
type Error struct {
	Message string
	Type string
	Stack string
}
func (e Error) To() *resultstore.TestError {
	return &resultstore.TestError{
		ErrorMessage:  e.Message,
		ExceptionType: e.Type,
		StackTrace:    e.Stack,
	}
}
func fromErrors(tes []*resultstore.TestError) []Error {
	var ret []Error
	for _, te := range tes {
		ret = append(ret, Error{
			Message: te.ErrorMessage,
			Type:    te.ExceptionType,
			Stack:   te.StackTrace,
		})
	}
	return ret
}
func errors(es []Error) []*resultstore.TestError {
	var rstes []*resultstore.TestError
	for _, e := range es {
		rstes = append(rstes, e.To())
	}
	return rstes
}
func Properties(pairs ...string) []Property {
	if len(pairs)%2 == 1 {
		panic(fmt.Sprintf("unbalanced properties: %v", pairs))
	}
	var out []Property
	for i := 0; i < len(pairs); i += 2 {
		out = append(out, Property{Key: pairs[i], Value: pairs[i+1]})
	}
	return out
}
type Property = resultstore.Property
func properties(ps []Property) []*Property {
	var out []*Property
	for _, p := range ps {
		p2 := p
		out = append(out, &p2)
	}
	return out
}
func fromProperties(ps []*Property) []Property {
	var out []Property
	for _, p := range ps {
		out = append(out, *p)
	}
	return out
}
const (
	BuildLog = "build.log"
	Stdout = "stdout"
	Stderr = "stderr"
	TestLog = "test.log"
	TestXML = "test.xml"
	TestCov = "test.lcov"
	BaselineCov = "baseline.lcov"
)
const (
	InvocationLog = BuildLog
	TargetLog = TestLog
)
type File struct {
	ID string
	ContentType string
	Length int64
	URL string
}
func wrap64(v int64) *wrappers.Int64Value {
	if v == 0 {
		return nil
	}
	return &wrappers.Int64Value{Value: v}
}
func unwrap64(w *wrappers.Int64Value) int64 {
	if w == nil {
		return 0
	}
	return w.Value
}
func (f File) To() *resultstore.File {
	return &resultstore.File{
		Uid:         f.ID,
		Uri:         f.URL,
		Length:      wrap64(f.Length),
		ContentType: f.ContentType,
	}
}
func Files(fs []File) []*resultstore.File {
	var rsfs []*resultstore.File
	for _, f := range fs {
		rsfs = append(rsfs, f.To())
	}
	return rsfs
}
func fromFiles(fs []*resultstore.File) []File {
	var out []File
	for _, f := range fs {
		out = append(out, File{
			ID:          f.Uid,
			URL:         f.Uri,
			Length:      unwrap64(f.Length),
			ContentType: f.ContentType,
		})
	}
	return out
}
type Result = resultstore.TestCase_Result
const (
	Completed = resultstore.TestCase_COMPLETED
	Cancelled = resultstore.TestCase_CANCELLED
	Skipped = resultstore.TestCase_SKIPPED
)
type Case struct {
	Name string
	Class string
	Result Result
	Duration time.Duration
	Errors []Error
	Failures []Failure
	Files []File
	Properties []Property
	Start time.Time
}
func fromCase(tc *resultstore.TestCase) Case {
	c := Case{
		Name:       tc.CaseName,
		Class:      tc.ClassName,
		Result:     tc.Result,
		Properties: fromProperties(tc.Properties),
		Errors:     fromErrors(tc.Errors),
		Failures:   fromFailures(tc.Failures),
	}
	c.Start, c.Duration = fromTiming(tc.Timing)
	return c
}
func (c Case) To() *resultstore.TestCase {
	return &resultstore.TestCase{
		CaseName:   c.Name,
		ClassName:  c.Class,
		Errors:     errors(c.Errors),
		Failures:   failures(c.Failures),
		Result:     c.Result,
		Timing:     timing(c.Start, c.Duration),
		Properties: properties(c.Properties),
	}
}
type Status = resultstore.Status
const (
	Running = resultstore.Status_TESTING
	Passed = resultstore.Status_PASSED
	Failed = resultstore.Status_FAILED
)
type Test struct {
	Action
	Suite
	Warnings []string
}