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
195 lines
5.5 KiB
Go
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
|
|
}
|