...

Source file src/edge-infra.dev/test/framework/integration/test_results_parser.go

Documentation: edge-infra.dev/test/framework/integration

     1  package integration
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"time"
     9  
    10  	"edge-infra.dev/test/framework"
    11  )
    12  
    13  type TestEvent struct {
    14  	Time    *time.Time // encodes as an RFC3339-format string
    15  	Action  string
    16  	Package string
    17  	Test    string
    18  	Elapsed *float64 // seconds
    19  	Output  string
    20  }
    21  
    22  type TestCase struct {
    23  	State    string
    24  	Output   strings.Builder
    25  	Duration *time.Duration
    26  }
    27  
    28  type TestPackage struct {
    29  	Duration    *time.Duration
    30  	State       string
    31  	TestCases   map[string]*TestCase
    32  	PackageName string
    33  }
    34  
    35  // Interface to parse test results from various data formats into a Go struct
    36  type TestResultsParser interface {
    37  	ParseTestResults(logs io.Reader, packageName string) (*TestPackage, error)
    38  }
    39  
    40  // Process the result of each test case in a TestPackage as though it were a locally-run test
    41  func ReportTestCases(f *framework.Framework, testPackage *TestPackage) {
    42  	f.Run(testPackage.PackageName, func() {
    43  		for testname, tc := range testPackage.TestCases {
    44  			// Ideally each level of test nesting would have their own s.Run call, because this would then
    45  			// mirror the actual pod test. Alternatively could just filter them out when parseJsonTestResults
    46  			f.Run(testname, func() {
    47  				f.Log(tc.Output.String())
    48  				switch tc.State {
    49  				case "skip":
    50  					f.Skip(testname, "skipped")
    51  				case "fail":
    52  					f.Fail("Failed")
    53  				case "pass": // do nothing
    54  				default: // Should we error if there was no pass/skip/fail event for test?
    55  				}
    56  			})
    57  		}
    58  	})
    59  }
    60  
    61  // Parse JSON-formatted test results
    62  type JSONTestResultsParser struct{}
    63  
    64  // Convert JSON-formatted test results to TestPackage Go struct
    65  func (p JSONTestResultsParser) ParseTestResults(logs io.Reader, packageName string) (*TestPackage, error) {
    66  	testcases := make(map[string]*TestCase)
    67  	// Returns the testcase with the given test name, creating a new empty test
    68  	// case if one does not exist
    69  	testCaseByName := func(name string) *TestCase {
    70  		if name == "" {
    71  			return nil
    72  		}
    73  		if _, ok := testcases[name]; !ok {
    74  			testcases[name] = &TestCase{}
    75  		}
    76  		return testcases[name]
    77  	}
    78  
    79  	dec := json.NewDecoder(logs)
    80  
    81  	var pkgDuration *time.Duration
    82  	var state string
    83  	for {
    84  		var e TestEvent
    85  		if err := dec.Decode(&e); err == io.EOF {
    86  			break
    87  		} else if err != nil {
    88  			return nil, fmt.Errorf("error decoding test output: %w", err)
    89  		}
    90  		switch s := e.Action; s {
    91  		case "run":
    92  			if c := testCaseByName(e.Test); c != nil {
    93  				c.State = s
    94  			}
    95  		case "output":
    96  			if c := testCaseByName(e.Test); c != nil {
    97  				c.Output.WriteString(e.Output)
    98  			}
    99  		case "skip":
   100  			if c := testCaseByName(e.Test); c != nil {
   101  				c.Output.WriteString(e.Output)
   102  				c.State, state = s, s
   103  				elapsed := time.Duration(*e.Elapsed * float64(time.Second))
   104  				c.Duration = &elapsed
   105  			}
   106  		case "fail", "pass":
   107  			elapsed := time.Duration(*e.Elapsed * float64(time.Second))
   108  			if c := testCaseByName(e.Test); c != nil {
   109  				c.State = s
   110  				c.Duration = &elapsed
   111  			} else {
   112  				state = s
   113  				pkgDuration = &elapsed
   114  			}
   115  		}
   116  	}
   117  	testpkg := TestPackage{Duration: pkgDuration, State: state, TestCases: testcases, PackageName: packageName}
   118  	return &testpkg, nil
   119  }
   120  

View as plain text