2025-11-25 20:47:31 -05:00

108 lines
2.9 KiB
Go

package main
import (
"bytes"
"errors"
"flag"
"fmt"
"log"
"os"
"os/exec"
"strings"
"time"
)
var (
BaseOutputFilename string
)
func ProfileOutputFilename() string {
return fmt.Sprintf("%s.profile", BaseOutputFilename)
}
func HTMLOutputFilename() string {
return fmt.Sprintf("%s.html", ProfileOutputFilename())
}
func GenerateGoTestArgs() []string {
return []string{
"test", // run the test command in go
fmt.Sprintf("-coverprofile=%s", ProfileOutputFilename()), // generate a coverprofile with the given output name
"./...", // on any and all go modules here, recursively
}
}
func GenerateGoToolCoverArgs() []string {
return []string{
"tool",
"cover", // go tool cover
fmt.Sprintf("-html=%s", ProfileOutputFilename()),
"-o",
HTMLOutputFilename(),
}
}
func GenerateCoverageReport() (string, error) {
cmd := exec.Command("go", GenerateGoTestArgs()...)
output, err := cmd.CombinedOutput()
output = bytes.TrimSpace(output)
if err != nil {
return "", fmt.Errorf("error generating the coverage profile: %w\n%s", err, output)
}
return string(output), nil
}
func GenerateHTMLOutput() (string, error) {
cmd := exec.Command("go", GenerateGoToolCoverArgs()...)
output, err := cmd.CombinedOutput()
output = bytes.TrimSpace(output)
if err != nil {
return "", fmt.Errorf("error generating the html output: %w\n%s", err, output)
}
return string(output), nil
}
func Clean() error {
errs := make([]error, 0, 2)
profileOutputFilename := ProfileOutputFilename()
htmlOutputFilename := HTMLOutputFilename()
if err := os.Remove(profileOutputFilename); err != nil && !errors.Is(err, os.ErrNotExist) {
errs = append(errs, fmt.Errorf("error deleting %s: %w", profileOutputFilename, err))
}
if err := os.Remove(htmlOutputFilename); err != nil && !errors.Is(err, os.ErrNotExist) {
errs = append(errs, fmt.Errorf("error deleting %s: %w", htmlOutputFilename, err))
}
return errors.Join(errs...)
}
func main() {
programStart := time.Now()
log.SetFlags(0)
flag.StringVar(&BaseOutputFilename, "o", "testcoverage", "base filename used for output files (testcoverage.profile, testcoverage.html, etc)")
flag.Parse()
if args := flag.Args(); len(args) == 1 {
if strings.EqualFold(args[0], "clean") {
if err := Clean(); err != nil {
log.Fatalf("error cleaning: %v\n", err)
}
log.Printf("finished cleaning after %v\n", time.Since(programStart))
return
}
}
if _, err := GenerateCoverageReport(); err != nil {
log.Fatalf("%v; cannot proceed\n", err)
}
placeholderTime := time.Now()
log.Printf("wrote %s after %v\n", ProfileOutputFilename(), placeholderTime.Sub(programStart))
if _, err := GenerateHTMLOutput(); err != nil {
log.Fatalf("%v; cannot proceed\n", err)
}
finishedTime := time.Now()
log.Printf("wrote %s after %v\n", HTMLOutputFilename(), finishedTime.Sub(placeholderTime))
log.Printf("finished generating report after %v\n", time.Since(programStart))
}