github.com/smithx10/nomad@v0.9.1-rc1/e2e/cli/command/test_decoder.go (about) 1 package command 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "strings" 8 "text/tabwriter" 9 "time" 10 11 "github.com/fatih/color" 12 hclog "github.com/hashicorp/go-hclog" 13 ) 14 15 type EventDecoder struct { 16 r io.Reader 17 18 dec *json.Decoder 19 report *TestReport 20 } 21 22 type TestReport struct { 23 Events []*TestEvent 24 Suites map[string]*TestSuite 25 TotalSuites int 26 TotalFailedSuites int 27 TotalCases int 28 TotalFailedCases int 29 TotalTests int 30 TotalFailedTests int 31 Elapsed float64 32 Output []string 33 } 34 35 type TestEvent struct { 36 Time time.Time // encodes as an RFC3339-format string 37 Action string 38 Package string 39 Test string 40 Elapsed float64 // seconds 41 Output string 42 43 suiteName string 44 caseName string 45 testName string 46 } 47 48 type TestSuite struct { 49 Name string 50 Cases map[string]*TestCase 51 Failed int 52 Elapsed float64 53 Output []string 54 } 55 56 type TestCase struct { 57 Name string 58 Tests map[string]*Test 59 Failed int 60 Elapsed float64 61 Output []string 62 } 63 64 type Test struct { 65 Name string 66 Output []string 67 Failed bool 68 Elapsed float64 69 } 70 71 func NewDecoder(r io.Reader) *EventDecoder { 72 return &EventDecoder{ 73 r: r, 74 dec: json.NewDecoder(r), 75 report: &TestReport{ 76 Suites: map[string]*TestSuite{}, 77 Events: []*TestEvent{}, 78 }, 79 } 80 } 81 82 func (d *EventDecoder) Decode(logger hclog.Logger) (*TestReport, error) { 83 for d.dec.More() { 84 var e TestEvent 85 err := d.dec.Decode(&e) 86 if err != nil { 87 return nil, err 88 } 89 90 d.report.record(&e) 91 if logger != nil && e.Output != "" { 92 logger.Debug(strings.TrimRight(e.Output, "\n")) 93 } 94 } 95 return d.report, nil 96 } 97 98 func (r *TestReport) record(event *TestEvent) { 99 if !strings.HasPrefix(event.Test, "TestE2E") { 100 return 101 } 102 parts := strings.Split(event.Test, "/") 103 switch len(parts) { 104 case 1: 105 r.recordRoot(event) 106 case 2: 107 event.suiteName = parts[1] 108 r.recordSuite(event) 109 case 3: 110 event.suiteName = parts[1] 111 event.caseName = parts[2] 112 r.recordCase(event, r.Suites[event.suiteName]) 113 case 4: 114 event.suiteName = parts[1] 115 event.caseName = parts[2] 116 event.testName = strings.Join(parts[3:], "/") 117 suite := r.Suites[event.suiteName] 118 r.recordTest(event, suite, suite.Cases[event.caseName]) 119 } 120 r.Events = append(r.Events, event) 121 122 } 123 124 func (r *TestReport) recordRoot(event *TestEvent) { 125 switch event.Action { 126 case "run": 127 case "output": 128 r.Output = append(r.Output, event.Output) 129 case "pass", "fail": 130 r.Elapsed = event.Elapsed 131 } 132 } 133 func (r *TestReport) recordSuite(event *TestEvent) { 134 switch event.Action { 135 case "run": 136 r.Suites[event.suiteName] = &TestSuite{ 137 Name: event.suiteName, 138 Cases: map[string]*TestCase{}, 139 } 140 r.TotalSuites += 1 141 case "output": 142 r.Suites[event.suiteName].Output = append(r.Suites[event.suiteName].Output, event.Output) 143 case "pass": 144 r.Suites[event.suiteName].Elapsed = event.Elapsed 145 case "fail": 146 r.Suites[event.suiteName].Elapsed = event.Elapsed 147 r.TotalFailedSuites += 1 148 } 149 } 150 func (r *TestReport) recordCase(event *TestEvent, suite *TestSuite) { 151 switch event.Action { 152 case "run": 153 suite.Cases[event.caseName] = &TestCase{ 154 Name: event.caseName, 155 Tests: map[string]*Test{}, 156 } 157 r.TotalCases += 1 158 case "output": 159 suite.Cases[event.caseName].Output = append(suite.Cases[event.caseName].Output, event.Output) 160 case "pass": 161 suite.Cases[event.caseName].Elapsed = event.Elapsed 162 case "fail": 163 suite.Cases[event.caseName].Elapsed = event.Elapsed 164 suite.Failed += 1 165 r.TotalFailedCases += 1 166 } 167 } 168 func (r *TestReport) recordTest(event *TestEvent, suite *TestSuite, c *TestCase) { 169 switch event.Action { 170 case "run": 171 c.Tests[event.testName] = &Test{ 172 Name: event.testName, 173 } 174 r.TotalTests += 1 175 case "output": 176 c.Tests[event.testName].Output = append(c.Tests[event.testName].Output, event.Output) 177 case "pass": 178 c.Tests[event.testName].Elapsed = event.Elapsed 179 case "fail": 180 c.Tests[event.testName].Elapsed = event.Elapsed 181 c.Tests[event.testName].Failed = true 182 c.Failed += 1 183 r.TotalFailedTests += 1 184 } 185 } 186 187 func (r *TestReport) Summary() string { 188 green := color.New(color.FgGreen).SprintFunc() 189 red := color.New(color.FgRed).SprintFunc() 190 191 sb := strings.Builder{} 192 sb.WriteString( 193 fmt.Sprintf("Summary: %v/%v suites failed | %v/%v cases failed | %v/%v tests failed\n", 194 r.TotalFailedSuites, r.TotalSuites, 195 r.TotalFailedCases, r.TotalCases, 196 r.TotalFailedTests, r.TotalTests)) 197 198 sb.WriteString("Details:\n") 199 w := tabwriter.NewWriter(&sb, 0, 0, 1, ' ', tabwriter.AlignRight) 200 for sname, suite := range r.Suites { 201 status := red("FAIL") 202 if suite.Failed == 0 { 203 status = green("PASS") 204 } 205 206 fmt.Fprintf(w, "[%s]\t%s\t\t\t (%vs)\n", status, sname, suite.Elapsed) 207 for cname, c := range suite.Cases { 208 status := red("FAIL") 209 if c.Failed == 0 { 210 status = green("PASS") 211 } 212 fmt.Fprintf(w, "[%s]\t↳\t%s\t\t (%vs)\n", status, cname, c.Elapsed) 213 for tname, test := range c.Tests { 214 status := red("FAIL") 215 if !test.Failed { 216 status = green("PASS") 217 } 218 fmt.Fprintf(w, "[%s]\t\t↳\t%s\t (%vs)\n", status, tname, test.Elapsed) 219 if test.Failed { 220 for _, line := range test.Output[2:] { 221 fmt.Fprintf(w, "\t\t\t%s\n", strings.Replace(strings.TrimSpace(line), "\t", " ", -1)) 222 } 223 fmt.Fprintln(w, "\t\t\t----------") 224 } 225 226 } 227 } 228 } 229 230 w.Flush() 231 return sb.String() 232 }