184 lines
3.9 KiB
Go
184 lines
3.9 KiB
Go
package modules
|
|
|
|
import (
|
|
"bytes"
|
|
_ "embed"
|
|
"encoding/csv"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
//go:embed resources/tests-struct.json
|
|
var testStructData []byte
|
|
|
|
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`,
|
|
RunE: runCompatibilityCmd,
|
|
}
|
|
|
|
type (
|
|
TestsStructure struct {
|
|
Groups []Group `json:"groups"`
|
|
}
|
|
|
|
Group struct {
|
|
Name string `json:"name"`
|
|
Tag string `json:"tag"`
|
|
Skip bool `json:"skip"`
|
|
SkipReason string `json:"skip_reason"`
|
|
Tests []string `json:"tests"`
|
|
}
|
|
)
|
|
|
|
var (
|
|
Reset = "\033[0m"
|
|
Red = "\033[31m"
|
|
Green = "\033[32m"
|
|
Yellow = "\033[33m"
|
|
)
|
|
|
|
const (
|
|
formatFlag = "format"
|
|
)
|
|
|
|
func initCompatibilityCmd() {
|
|
compatibilityCmd.Flags().String(formatFlag, "csv", "format of input test suite file")
|
|
}
|
|
|
|
func runCompatibilityCmd(cmd *cobra.Command, args []string) error {
|
|
if len(args) != 1 {
|
|
return fmt.Errorf("expected exactly one arg, got: %v", args)
|
|
}
|
|
|
|
var testStruct TestsStructure
|
|
if err := json.Unmarshal(testStructData, &testStruct); err != nil {
|
|
return fmt.Errorf("failed to parse tests struct: %w", err)
|
|
}
|
|
|
|
testsMap, err := parseSuite(args[0], viper.GetString(formatFlag))
|
|
if err != nil {
|
|
return fmt.Errorf("parse tests: %w", err)
|
|
}
|
|
|
|
for _, group := range testStruct.Groups {
|
|
if group.Skip {
|
|
cmd.Println(fmt.Sprintf("%s; skip: %s", group.Name, group.SkipReason))
|
|
continue
|
|
}
|
|
|
|
printResult(cmd, group, testsMap)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func printResult(cmd *cobra.Command, group Group, testsMap map[string]bool) {
|
|
ln := float64(len(group.Tests))
|
|
pass := 0.0
|
|
|
|
for _, test := range group.Tests {
|
|
if testsMap[test] {
|
|
pass++
|
|
}
|
|
}
|
|
|
|
color := Red
|
|
rate := pass / ln
|
|
if rate > 0.9 {
|
|
color = Green
|
|
} else if rate > 0.5 {
|
|
color = Yellow
|
|
}
|
|
|
|
cmd.Println(fmt.Sprintf("%s%s: %d/%d \u001B[0m", color, group.Name, int(pass), int(ln)))
|
|
}
|
|
|
|
func parseSuite(filePath string, format string) (map[string]bool, error) {
|
|
switch format {
|
|
case "csv":
|
|
return parseSuiteCSV(filePath)
|
|
case "json":
|
|
return parseSuiteJSON(filePath)
|
|
default:
|
|
return nil, fmt.Errorf("unknown format: %s", format)
|
|
}
|
|
}
|
|
|
|
func parseSuiteCSV(filePath string) (map[string]bool, error) {
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read suite file: %w", err)
|
|
}
|
|
|
|
rr := csv.NewReader(bytes.NewReader(data))
|
|
records, err := rr.ReadAll()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse suite file: %w", err)
|
|
}
|
|
|
|
indexName := -1
|
|
indexStatus := -1
|
|
tests := make(map[string]bool)
|
|
|
|
for i, recs := range records {
|
|
if i == 0 {
|
|
for j, rec := range recs {
|
|
if rec == "Name" {
|
|
indexName = j
|
|
} else if rec == "Status" {
|
|
indexStatus = j
|
|
}
|
|
}
|
|
|
|
if indexName == -1 || indexStatus == -1 {
|
|
return nil, fmt.Errorf("invalid csv format, couldn't find 'Name' and 'Status' fields")
|
|
}
|
|
}
|
|
|
|
tests[recs[indexName]] = recs[indexStatus] == "passed"
|
|
}
|
|
|
|
return tests, nil
|
|
}
|
|
|
|
type SuiteNode struct {
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
Children []SuiteNode `json:"children"`
|
|
}
|
|
|
|
func parseSuiteJSON(filePath string) (map[string]bool, error) {
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read suite file: %w", err)
|
|
}
|
|
|
|
var suiteNode SuiteNode
|
|
if err = json.Unmarshal(data, &suiteNode); err != nil {
|
|
return nil, fmt.Errorf("failed to parse suite file: %w", err)
|
|
}
|
|
|
|
tests := make(map[string]bool)
|
|
parseSuiteNode(suiteNode, tests)
|
|
|
|
return tests, nil
|
|
}
|
|
|
|
func parseSuiteNode(node SuiteNode, res map[string]bool) {
|
|
if node.Status != "" {
|
|
res[node.Name] = node.Status == "passed"
|
|
return
|
|
}
|
|
|
|
for _, child := range node.Children {
|
|
parseSuiteNode(child, res)
|
|
}
|
|
}
|