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  }