nixtest/internal/report/console/console.go

143 lines
3.6 KiB
Go

package console
import (
"fmt"
"os"
"sort"
"strings"
"time"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/rs/zerolog/log"
"github.com/sergi/go-diff/diffmatchpatch"
"gitlab.com/technofab/nixtest/internal/types"
"gitlab.com/technofab/nixtest/internal/util"
)
// PrintErrors prints error messages for failed tests
func PrintErrors(results types.Results, noColor bool) {
for _, suiteResults := range results {
for _, result := range suiteResults {
if result.Status == types.StatusSuccess || result.Status == types.StatusSkipped {
continue
}
fmt.Println(text.FgRed.Sprintf("⚠ Test \"%s/%s\" failed:", result.Spec.Suite, result.Spec.Name))
message := result.ErrorMessage
if result.Status == types.StatusFailure && message == "" {
if noColor {
var err error
message, err = util.ComputeDiff(result.Expected, result.Actual)
if err != nil {
log.Panic().Err(err).Msg("failed to compute diff")
}
} else {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(result.Expected, result.Actual, true)
message = fmt.Sprintf("Diff:\n%s", dmp.DiffPrettyText(diffs))
}
}
if message == "" {
message = "- no output -"
}
for _, line := range strings.Split(strings.TrimRight(message, "\n"), "\n") {
fmt.Printf("%s %s\n", text.FgRed.Sprint("|"), line)
}
fmt.Println()
}
}
}
// PrintSummary prints a table summarizing test results
func PrintSummary(results types.Results, totalSuccessCount int, totalTestCount int) {
t := table.NewWriter()
t.SetStyle(table.StyleLight)
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Suite / Test", "Duration", "Status", "File:Line"})
log.Info().Msg("Summary:")
suiteNames := make([]string, 0, len(results))
for name := range results {
suiteNames = append(suiteNames, name)
}
sort.Strings(suiteNames)
for _, suiteName := range suiteNames {
suiteResults := results[suiteName]
suiteTotal := len(suiteResults)
suiteSuccess := 0
suiteSkipped := 0
for _, res := range suiteResults {
if res.Status == types.StatusSuccess {
suiteSuccess++
} else if res.Status == types.StatusSkipped {
suiteSkipped++
}
}
statusStr := fmt.Sprintf("%d/%d", suiteSuccess, suiteTotal)
if suiteSkipped > 0 {
statusStr += fmt.Sprintf(" (%d skipped)", suiteSkipped)
}
t.AppendRow(table.Row{
text.Bold.Sprint(suiteName),
"",
statusStr,
"",
})
sort.Slice(suiteResults, func(i, j int) bool {
return suiteResults[i].Spec.Name < suiteResults[j].Spec.Name
})
for _, res := range suiteResults {
var symbol string
switch res.Status {
case types.StatusSuccess:
symbol = text.FgGreen.Sprint("✅ PASS")
case types.StatusFailure:
symbol = text.FgRed.Sprint("❌ FAIL")
case types.StatusError:
symbol = text.FgYellow.Sprint("❗ ERROR")
case types.StatusSkipped:
symbol = text.FgBlue.Sprint("⏭️ SKIP")
default:
symbol = "UNKNOWN"
}
t.AppendRow([]any{
" " + res.Spec.Name,
fmt.Sprintf("%s", res.Duration.Round(time.Millisecond)),
symbol,
res.Spec.Pos,
})
}
t.AppendSeparator()
}
overallStatusStr := fmt.Sprintf("%d/%d", totalSuccessCount, totalTestCount)
totalSkipped := 0
for _, suiteResults := range results {
for _, res := range suiteResults {
if res.Status == types.StatusSkipped {
totalSkipped++
}
}
}
if totalSkipped > 0 {
overallStatusStr += fmt.Sprintf(" (%d skipped)", totalSkipped)
}
t.AppendFooter(table.Row{
text.Bold.Sprint("TOTAL"),
"",
text.Bold.Sprint(overallStatusStr),
"",
})
t.Render()
}