dev/coverage/coverage.go (116 lines of code) (raw):

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. // File partially copied from elastic-package. package coverage import ( "bytes" "encoding/xml" "fmt" "os" ) // GenericCoverage is the root element for a Cobertura XML report. type GenericCoverage struct { XMLName xml.Name `xml:"coverage"` Version int64 `xml:"version,attr"` Files []*GenericFile `xml:"file"` Timestamp int64 `xml:"-"` TestType string `xml:",comment"` } type GenericFile struct { Path string `xml:"path,attr"` Lines []*GenericLine `xml:"lineToCover"` } type GenericLine struct { LineNumber int64 `xml:"lineNumber,attr"` Covered bool `xml:"covered,attr"` } func (c *GenericCoverage) Bytes() ([]byte, error) { out, err := xml.MarshalIndent(&c, "", " ") if err != nil { return nil, fmt.Errorf("unable to format test results as Coverage: %w", err) } var buffer bytes.Buffer buffer.WriteString(xml.Header) buffer.WriteString("\n") buffer.Write(out) return buffer.Bytes(), nil } func (c *GenericFile) merge(b *GenericFile) error { // Merge files for _, coverageLine := range b.Lines { found := false foundId := 0 for idx, existingLine := range c.Lines { if existingLine.LineNumber == coverageLine.LineNumber { found = true foundId = idx break } } if !found { c.Lines = append(c.Lines, coverageLine) } else { c.Lines[foundId].Covered = c.Lines[foundId].Covered || coverageLine.Covered } } return nil } // merge merges two coverage reports. func (c *GenericCoverage) Merge(other *GenericCoverage) error { // Merge files for _, coverageFile := range other.Files { var target *GenericFile for _, existingFile := range c.Files { if existingFile.Path == coverageFile.Path { target = existingFile break } } if target != nil { if err := target.merge(coverageFile); err != nil { return err } } else { c.Files = append(c.Files, coverageFile) } } return nil } func ReadGenericCoverage(path string) (*GenericCoverage, error) { f, err := os.Open(path) if err != nil { return nil, fmt.Errorf("open failed: %w", err) } defer f.Close() dec := xml.NewDecoder(f) var coverage GenericCoverage err = dec.Decode(&coverage) if err != nil { return nil, fmt.Errorf("xml decode failed: %w", err) } return &coverage, nil } func MergeGenericCoverageFiles(paths []string, output string) error { f, err := os.Create(output) if err != nil { return fmt.Errorf("cannot open file %s to write merged coverage: %w", output, err) } defer f.Close() var coverage *GenericCoverage for _, path := range paths { c, err := ReadGenericCoverage(path) if err != nil { return fmt.Errorf("failed to read coverage from %s: %w", path, err) } if coverage == nil { coverage = c continue } err = coverage.Merge(c) if err != nil { return fmt.Errorf("failed to merge coverage from %s: %w", path, err) } } d, err := coverage.Bytes() if err != nil { return fmt.Errorf("failed to encode merged coverage: %w", err) } _, err = f.Write(d) if err != nil { return fmt.Errorf("cannot write merged coverage to %s: %w", output, err) } return nil }