mukan-ibc/cmd/build_test_matrix/main.go
Mukan Erkin Törük 6852832fe8
Some checks failed
CodeQL / Analyze (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Deploy to GitHub Pages / Deploy to GitHub Pages (push) Has been cancelled
Buf-Push / push (push) Has been cancelled
initial: sovereign Mukan Network fork
2026-05-11 03:18:28 +03:00

195 lines
5.5 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/fs"
"os"
"path/filepath"
"slices"
"sort"
"strings"
)
const (
testNamePrefix = "Test"
testFileNameSuffix = "_test.go"
e2eTestDirectory = "e2e"
// testEntryPointEnv specifies a single test function to run if provided.
testEntryPointEnv = "TEST_ENTRYPOINT"
// testExclusionsEnv is a comma separated list of test function names that will not be included
// in the results of this script.
testExclusionsEnv = "TEST_EXCLUSIONS"
// testNameEnv if provided returns a single test entry so that only one test is actually run.
testNameEnv = "TEST_NAME"
)
// GithubActionTestMatrix represents
type GithubActionTestMatrix struct {
Include []TestSuitePair `json:"include"`
}
type TestSuitePair struct {
Test string `json:"test"`
EntryPoint string `json:"entrypoint"`
}
func main() {
githubActionMatrix, err := getGithubActionMatrixForTests(e2eTestDirectory, getTestToRun(), getTestEntrypointToRun(), getExcludedTestFunctions())
if err != nil {
fmt.Printf("error generating github action json: %s", err)
os.Exit(1)
}
ghBytes, err := json.Marshal(githubActionMatrix)
if err != nil {
fmt.Printf("error marshalling github action json: %s", err)
os.Exit(1)
}
fmt.Println(string(ghBytes))
}
// getTestEntrypointToRun returns the specified test function to run if present, otherwise
// it returns an empty string which will result in running all test suites.
func getTestEntrypointToRun() string {
testSuite, ok := os.LookupEnv(testEntryPointEnv)
if !ok {
return ""
}
return testSuite
}
// getTestToRun returns the specified test function to run if present.
// If specified, only this test will be run.
func getTestToRun() string {
testName, ok := os.LookupEnv(testNameEnv)
if !ok {
return ""
}
return testName
}
// getExcludedTestFunctions returns a list of test functions that we don't want to run.
func getExcludedTestFunctions() []string {
exclusions, ok := os.LookupEnv(testExclusionsEnv)
if !ok {
return nil
}
return strings.Split(exclusions, ",")
}
// getGithubActionMatrixForTests returns a json string representing the contents that should go in the matrix
// field in a github action workflow. This string can be used with `fromJSON(str)` to dynamically build
// the workflow matrix to include all E2E tests under the e2eRootDirectory directory.
func getGithubActionMatrixForTests(e2eRootDirectory, testName string, suite string, excludedItems []string) (GithubActionTestMatrix, error) {
testSuiteMapping := map[string][]string{}
fset := token.NewFileSet()
err := filepath.Walk(e2eRootDirectory, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error walking e2e directory: %s", err)
}
// only look at test files
if !strings.HasSuffix(path, testFileNameSuffix) {
return nil
}
f, err := parser.ParseFile(fset, path, nil, 0)
if err != nil {
return fmt.Errorf("failed parsing file: %s", err)
}
suiteNameForFile, testCases, err := extractSuiteAndTestNames(f)
if err != nil {
return nil
}
if testName != "" && slices.Contains(testCases, testName) {
testCases = []string{testName}
}
if slices.Contains(excludedItems, suiteNameForFile) {
return nil
}
if suite == "" || suiteNameForFile == suite {
testSuiteMapping[suiteNameForFile] = testCases
}
return nil
})
if err != nil {
return GithubActionTestMatrix{}, err
}
gh := GithubActionTestMatrix{
Include: []TestSuitePair{},
}
for testSuiteName, testCases := range testSuiteMapping {
for _, testCaseName := range testCases {
gh.Include = append(gh.Include, TestSuitePair{
Test: testCaseName,
EntryPoint: testSuiteName,
})
}
}
if len(gh.Include) == 0 {
return GithubActionTestMatrix{}, errors.New("no test cases found")
}
// Sort the test cases by name so that the order is consistent.
sort.SliceStable(gh.Include, func(i, j int) bool {
return gh.Include[i].Test < gh.Include[j].Test
})
if testName != "" && len(gh.Include) != 1 {
return GithubActionTestMatrix{}, fmt.Errorf("expected exactly 1 test in the output matrix but got %d", len(gh.Include))
}
return gh, nil
}
// extractSuiteAndTestNames extracts the name of the test suite function as well
// as all tests associated with it in the same file.
func extractSuiteAndTestNames(file *ast.File) (string, []string, error) {
var suiteNameForFile string
var testCases []string
for _, d := range file.Decls {
if f, ok := d.(*ast.FuncDecl); ok {
functionName := f.Name.Name
if isTestSuiteMethod(f) {
if suiteNameForFile != "" {
return "", nil, fmt.Errorf("found a second test function: %s when %s was already found", f.Name.Name, suiteNameForFile)
}
suiteNameForFile = functionName
continue
}
if isTestFunction(f) {
testCases = append(testCases, functionName)
}
}
}
if suiteNameForFile == "" {
return "", nil, fmt.Errorf("file %s had no test suite test case", file.Name.Name)
}
return suiteNameForFile, testCases, nil
}
// isTestSuiteMethod returns true if the function is a test suite function.
// e.g. func TestFeeMiddlewareTestSuite(t *testing.T) { ... }
func isTestSuiteMethod(f *ast.FuncDecl) bool {
return strings.HasPrefix(f.Name.Name, testNamePrefix) && len(f.Type.Params.List) == 1
}
// isTestFunction returns true if the function name starts with "Test" and has no parameters.
// as test suite functions do not accept a *testing.T.
func isTestFunction(f *ast.FuncDecl) bool {
return strings.HasPrefix(f.Name.Name, testNamePrefix) && len(f.Type.Params.List) == 0
}