func MergeCells(flaky bool, cells ...Cell) Cell {
	var out Cell
	if len(cells) == 0 {
		panic("empty cells")
	}
	out = cells[0]
	if len(cells) == 1 {
		return out
	}
	var pass int
	var passMsg string
	var fail int
	var failMsg string
	means := map[string][]float64{}
	issues := map[string]bool{}
	current := out.Result
	passMessageResult := current
	failMessageResult := current
	for _, c := range cells {
		if result.GTE(c.Result, current) {
			current = c.Result
		}
		switch {
		case result.Passing(c.Result):
			pass++
			if c.Message != "" && result.GTE(c.Result, passMessageResult) {
				passMsg = c.Message
				passMessageResult = c.Result
			}
		case result.Failing(c.Result):
			fail++
			if c.Message != "" && result.GTE(c.Result, failMessageResult) {
				failMsg = c.Message
				failMessageResult = c.Result
			}
		}
		for metric, mean := range c.Metrics {
			means[metric] = append(means[metric], mean)
		}
		for _, i := range c.Issues {
			issues[i] = true
		}
	}
	if n := len(issues); n > 0 {
		out.Issues = make([]string, 0, len(issues))
		for key := range issues {
			out.Issues = append(out.Issues, key)
		}
		sort.Strings(out.Issues)
	}
	if flaky && pass > 0 && fail > 0 {
		out.Result = statuspb.TestStatus_FLAKY
	} else {
		out.Result = current
	}
	total := len(cells)
	out.Icon = strconv.Itoa(pass) + "/" + strconv.Itoa(total)
	var msg string
	if failMsg != "" {
		msg = failMsg
	} else if passMsg != "" {
		msg = passMsg
	}
	if msg != "" {
		msg = ": " + msg
	}
	out.Message = out.Icon + " runs passed" + msg
	if len(means) > 0 {
		out.Metrics = make(map[string]float64, len(means))
		for metric, means := range means {
			var sum float64
			for _, m := range means {
				sum += m
			}
			out.Metrics[metric] = sum / float64(len(means))
		}
	}
	return out
}
MergeCells will combine the cells into a single result.

The flaky argument determines whether returned result
is flaky (true) or failing when merging cells with both passing
and failing results.

Merging multiple results will set the icon to n/N passes

Includes the message from the "most relevant" cell that includes a message.
Where relevance is determined by result.GTE.
package updater
import (
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
	"github.com/sirupsen/logrus"
	"github.com/GoogleCloudPlatform/testgrid/internal/result"
	"github.com/GoogleCloudPlatform/testgrid/metadata"
	"github.com/GoogleCloudPlatform/testgrid/metadata/junit"
	statepb "github.com/GoogleCloudPlatform/testgrid/pb/state"
	statuspb "github.com/GoogleCloudPlatform/testgrid/pb/test_status"
	"github.com/GoogleCloudPlatform/testgrid/util/gcs"
)
type gcsResult struct {
	podInfo   gcs.PodInfo
	started   gcs.Started
	finished  gcs.Finished
	suites    []gcs.SuitesMeta
	job       string
	build     string
	malformed []string
}
func (r gcsResult) deadline() time.Time {
	f := r.finished.Timestamp
	if f == nil {
		return time.Unix(r.started.Timestamp, 0).Add(24 * time.Hour)
	}
	return time.Unix(*f, 0).Add(time.Hour)
}
const maxDuplicates = 20
const EmailListKey = "EmailAddresses"
var overflowCell = Cell{
	Result:  statuspb.TestStatus_FAIL,
	Icon:    "...",
	Message: "Too many duplicately named rows",
}
func propertyMap(r *junit.Result) map[string][]string {
	out := map[string][]string{}
	if r.Properties == nil {
		return out
	}
	for _, p := range r.Properties.PropertyList {
		out[p.Name] = append(out[p.Name], p.Value)
	}
	return out
}
func Means(properties map[string][]string) map[string]float64 {
	out := make(map[string]float64, len(properties))
	for name, values := range properties {
		var sum float64
		var n int
		for _, str := range values {
			v, err := strconv.ParseFloat(str, 64)
			if err != nil {
				continue
			}
			sum += v
			n++
		}
		if n == 0 {
			continue
		}
		out[name] = sum / float64(n)
	}
	return out
}
func first(properties map[string][]string) map[string]string {
	out := make(map[string]string, len(properties))
	for k, v := range properties {
		if len(v) == 0 {
			continue
		}
		out[k] = v[0]
	}
	return out
}
const (
	overallRow = "Overall"
	podInfoRow = "Pod"
)