| | | 1 | | package reporter |
| | | 2 | | |
| | | 3 | | import ( |
| | | 4 | | "context" |
| | | 5 | | "fmt" |
| | | 6 | | "io" |
| | | 7 | | "sort" |
| | | 8 | | |
| | | 9 | | "github.com/jedi-knights/neospec/internal/domain" |
| | | 10 | | ) |
| | | 11 | | |
| | | 12 | | // LCOV writes coverage data in LCOV tracefile format. |
| | | 13 | | // See https://ltp.sourceforge.net/coverage/lcov/geninfo.1.php for the format. |
| | | 14 | | // Test result data is not included in LCOV output — it is a coverage-only format. |
| | | 15 | | type LCOV struct{} |
| | | 16 | | |
| | | 17 | | // NewLCOV creates an LCOV reporter. |
| | | 18 | | func NewLCOV() *LCOV { return &LCOV{} } |
| | | 19 | | |
| | 3 | 20 | | func (l *LCOV) Write(_ context.Context, w io.Writer, _ *domain.SuiteResult, cov *domain.CoverageData) error { |
| | 1 | 21 | | if cov == nil { |
| | 1 | 22 | | return nil |
| | 1 | 23 | | } |
| | | 24 | | |
| | 2 | 25 | | for _, file := range cov.Files { |
| | 3 | 26 | | // TN: test name (empty is valid) |
| | 3 | 27 | | fmt.Fprintln(w, "TN:") |
| | 3 | 28 | | fmt.Fprintf(w, "SF:%s\n", file.Path) |
| | 3 | 29 | | |
| | 3 | 30 | | // Collect and sort line numbers for deterministic output. |
| | 3 | 31 | | lines := make([]int, 0, len(file.Lines)) |
| | 3 | 32 | | for ln := range file.Lines { |
| | 5 | 33 | | lines = append(lines, ln) |
| | 5 | 34 | | } |
| | 3 | 35 | | sort.Ints(lines) |
| | 3 | 36 | | |
| | 3 | 37 | | for _, ln := range lines { |
| | 5 | 38 | | fmt.Fprintf(w, "DA:%d,%d\n", ln, file.Lines[ln]) |
| | 5 | 39 | | } |
| | | 40 | | |
| | 3 | 41 | | fmt.Fprintf(w, "LH:%d\n", file.HitLines()) |
| | 3 | 42 | | fmt.Fprintf(w, "LF:%d\n", file.TotalLines()) |
| | 3 | 43 | | fmt.Fprintln(w, "end_of_record") |
| | | 44 | | } |
| | | 45 | | |
| | 2 | 46 | | return nil |
| | | 47 | | } |