s3-tests-parser/cmd/parser/modules/compatibility.go
Denis Kirillov c53f3fa5d0 [#11] Add all flag
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2025-04-15 15:22:21 +03:00

227 lines
5.5 KiB
Go

package modules
import (
_ "embed"
"fmt"
"os"
"sort"
"strings"
"git.frostfs.info/TrueCloudLab/s3-tests-parser/internal/parser"
"git.frostfs.info/TrueCloudLab/s3-tests-parser/internal/s3"
"git.frostfs.info/TrueCloudLab/s3-tests-parser/internal/templates"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var compatibilityCmd = &cobra.Command{
Use: "compatibility",
Short: "Shows compatibility results",
Long: "Form compatibility table based on passed s3 tests",
Example: `s3-tests-parser compatibility suite.csv
s3-tests-parser compatibility suite.json --format json
s3-tests-parser compatibility suite.json --format json --output-format md
s3-tests-parser compatibility suite.json --format json --output-format md --output result.md
s3-tests-parser compatibility suite.json --format json --output-format txt --output result.txt --verbose
s3-tests-parser compatibility suite.json --format json --output-format txt --output result.txt --verbose
s3-tests-parser compatibility suite.json --format json --output-format txt --output result.txt --verbose --include-ignored
`,
RunE: runCompatibilityCmd,
}
type (
Results struct {
Verbose bool
Legend []Status
TagGroups []TagGroup
}
TagGroup struct {
Name string
Tests []TestResult
}
Status struct {
Color string
Description string
}
TestResult struct {
Color string
Name string
Comment string
Passed int
Total int
FailedTests []string
PassedTests []string
}
)
const (
formatFlag = "format"
outputFlag = "output"
outputFormatFlag = "output-format"
verboseFlag = "verbose"
allFlag = "all"
)
func initCompatibilityCmd() {
compatibilityCmd.Flags().String(formatFlag, "csv", "format of input test suite file")
compatibilityCmd.Flags().String(outputFlag, "", "file to write output, if missed the stdout is used")
compatibilityCmd.Flags().String(outputFormatFlag, "txt", "format of output")
compatibilityCmd.Flags().Bool(verboseFlag, false, "produce additional info")
compatibilityCmd.Flags().Bool(allFlag, false, "include in output ignored tests")
}
func runCompatibilityCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("expected exactly one arg, got: %v", args)
}
testStruct, err := s3.ParseTestsStruct()
if err != nil {
return err
}
testsMap, err := parser.ParseSuite(args[0], viper.GetString(formatFlag))
if err != nil {
return fmt.Errorf("parse tests: %w", err)
}
res := formResults(testStruct, testsMap)
res.Verbose = viper.GetBool(verboseFlag)
if !viper.GetBool(allFlag) {
for i, group := range res.TagGroups {
if group.Name == "Ignored" {
res.TagGroups = append(res.TagGroups[:i], res.TagGroups[i+1:]...)
break
}
}
}
return printResults(cmd, res)
}
var legend = []Status{
{
Color: templates.GreenColor,
Description: "Supported",
},
{
Color: templates.YellowColor,
Description: "Partially supported",
},
{
Color: templates.RedColor,
Description: "Badly supported",
},
{
Color: templates.BlueColor,
Description: "Not supported",
},
{
Color: templates.BlackColor,
Description: "Not applicable or will never be supported",
},
}
func formResults(testStruct s3.TestsStructure, testsMap map[string]bool) Results {
tagGroups := make(map[string]TagGroup)
groupTests := make(map[string][]string)
for _, group := range testStruct.Groups {
groupTests[group.Name] = group.Tests
}
for _, group := range testStruct.Groups {
tagGroup, ok := tagGroups[group.Tag]
if !ok {
tagGroup.Name = group.Tag
}
for _, n := range group.Include {
group.Tests = append(group.Tests, groupTests[n]...)
}
tagGroup.Tests = append(tagGroup.Tests, formTestResult(group, testsMap))
tagGroups[group.Tag] = tagGroup
}
res := Results{Legend: legend}
for _, group := range tagGroups {
res.TagGroups = append(res.TagGroups, group)
}
sort.Slice(res.TagGroups, func(i, j int) bool {
return res.TagGroups[i].Name < res.TagGroups[j].Name
})
return res
}
func formTestResult(group s3.Group, testsMap map[string]bool) TestResult {
ln := len(group.Tests)
var failed []string
var passed []string
for _, test := range group.Tests {
split := strings.Split(test, "::") // to trim test path
if len(split) != 2 {
continue
}
testName := split[1]
if testsMap[testName] {
passed = append(passed, testName)
} else {
failed = append(failed, testName)
}
}
var color string
if strings.Contains(group.Comment, "Not supported") {
color = templates.BlueColor
} else if strings.Contains(group.Comment, "Not applicable") {
color = templates.BlackColor
} else if ln == 0 && !group.Skip {
color = templates.GreenColor
}
if color == "" {
color = templates.RedColor
rate := float64(len(passed)) / float64(ln)
if rate > 0.9 {
color = templates.GreenColor
} else if rate > 0.5 {
color = templates.YellowColor
}
}
return TestResult{
Color: color,
Name: group.Name,
Comment: group.Comment,
Passed: len(passed),
Total: ln,
FailedTests: failed,
PassedTests: passed,
}
}
func printResults(cmd *cobra.Command, res Results) error {
w := cmd.OutOrStdout()
if outFile := viper.GetString(outputFlag); outFile != "" {
f, err := os.Create(outFile)
if err != nil {
return fmt.Errorf("create out file: %w", err)
}
w = f
defer f.Close()
}
outTemplate, err := templates.GetTemplate(viper.GetString(outputFormatFlag))
if err != nil {
return fmt.Errorf("form out template: %w", err)
}
return outTemplate.Execute(w, res)
}