github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/cli/test_history.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/evergreen-ci/evergreen"
     9  	"github.com/evergreen-ci/evergreen/model"
    10  	"github.com/evergreen-ci/evergreen/service"
    11  	"github.com/evergreen-ci/evergreen/util"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  var ()
    16  
    17  const (
    18  	prettyStringFormat = "%-25s %-15s %-40s%-40s %-40s %-40s \n"
    19  	timeFormat         = "2006-01-02T15:04:05"
    20  	csvFormat          = "csv"
    21  	prettyFormat       = "pretty"
    22  	// jsonFormat   = "json" // not used
    23  )
    24  
    25  // TestHistoryCommand represents the test-history command in the CLI
    26  type TestHistoryCommand struct {
    27  	GlobalOpts *Options `no-flag:"true"`
    28  
    29  	Project        string   `long:"project" short:"p" description:"project identifier, defaults to user's default project"`
    30  	Tasks          []string `long:"task" description:"task name"`
    31  	Tests          []string `long:"test" description:"test name"`
    32  	Variants       []string `long:"variant" short:"v" description:"variant name"`
    33  	TaskStatuses   []string `long:"task-status" description:"task status, either fail, pass, sysfail, or timeout "`
    34  	TestStatuses   []string `long:"test-status" description:"test status, either fail, silentfail, pass, skip, or timeout "`
    35  	BeforeRevision string   `long:"before-revision" description:"find tests that finished before a full revision hash (40 characters) (inclusive)"`
    36  	AfterRevision  string   `long:"after-revision" description:"find tests that finished after a full revision hash (40 characters) (exclusive)"`
    37  	// TODO EVG-1540 for user specific timezones.
    38  	BeforeDate string `long:"before-date" description:"find tests that finished before a date in format YYYY-MM-DDTHH:MM:SS in UTC"`
    39  	AfterDate  string `long:"after-date" description:"find tests that finish after a date in format YYYY-MM-DDTHH:MM:SS in UTC"`
    40  	Earliest   bool   `long:"earliest" description:"sort test history from the earliest revisions to latest"`
    41  	Filepath   string `long:"filepath" description:"path to directory where file is to be saved, only used with json or csv format"`
    42  	Format     string `long:"format" description:"format to export test history, options are 'json', 'csv', 'pretty', default pretty to stdout"`
    43  	Limit      int    `long:"limit" description:"number of tasks to include the request. defaults to no limit, but you must specify either a limit or before/after revisions."`
    44  }
    45  
    46  // createUrlQuery returns a string url query parameter with relevant url parameters.
    47  func createUrlQuery(testHistoryParameters model.TestHistoryParameters) string {
    48  	queryString := fmt.Sprintf("testStatuses=%v&taskStatuses=%v", strings.Join(testHistoryParameters.TestStatuses, ","),
    49  		strings.Join(testHistoryParameters.TaskStatuses, ","))
    50  
    51  	if len(testHistoryParameters.TaskNames) > 0 {
    52  		queryString += fmt.Sprintf("&tasks=%v", strings.Join(testHistoryParameters.TaskNames, ","))
    53  	}
    54  
    55  	if len(testHistoryParameters.TestNames) > 0 {
    56  		queryString += fmt.Sprintf("&tests=%v", strings.Join(testHistoryParameters.TestNames, ","))
    57  	}
    58  
    59  	if len(testHistoryParameters.BuildVariants) > 0 {
    60  		queryString += fmt.Sprintf("&variants=%v", strings.Join(testHistoryParameters.BuildVariants, ","))
    61  	}
    62  
    63  	if testHistoryParameters.BeforeRevision != "" {
    64  		queryString += fmt.Sprintf("&beforeRevision=%v", testHistoryParameters.BeforeRevision)
    65  	}
    66  
    67  	if testHistoryParameters.AfterRevision != "" {
    68  		queryString += fmt.Sprintf("&afterRevision=%v", testHistoryParameters.AfterRevision)
    69  	}
    70  	if !util.IsZeroTime(testHistoryParameters.BeforeDate) {
    71  		queryString += fmt.Sprintf("&beforeDate=%v", testHistoryParameters.BeforeDate.Format(time.RFC3339))
    72  	}
    73  	if !util.IsZeroTime(testHistoryParameters.AfterDate) {
    74  		queryString += fmt.Sprintf("&afterDate=%v", testHistoryParameters.AfterDate.Format(time.RFC3339))
    75  	}
    76  
    77  	if testHistoryParameters.Limit != 0 {
    78  		queryString += fmt.Sprintf("&limit=%v", testHistoryParameters.Limit)
    79  	}
    80  
    81  	return queryString
    82  }
    83  
    84  // Execute transfers the fields from a TestHistoryCommand to a TestHistoryParameter
    85  // and validates them. It then gets the test history from the api endpoint
    86  func (thc *TestHistoryCommand) Execute(_ []string) error {
    87  	_, rc, _, err := getAPIClients(thc.GlobalOpts)
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	// convert the test and tasks statuses to the correct evergreen statuses
    93  	testStatuses := []string{}
    94  	for _, s := range thc.TestStatuses {
    95  		switch s {
    96  		case "pass":
    97  			testStatuses = append(testStatuses, evergreen.TestSucceededStatus)
    98  		case "fail":
    99  			testStatuses = append(testStatuses, evergreen.TestFailedStatus)
   100  		case "silentfail":
   101  			testStatuses = append(testStatuses, evergreen.TestSilentlyFailedStatus)
   102  		case "skip":
   103  			testStatuses = append(testStatuses, evergreen.TestSkippedStatus)
   104  		case "timeout":
   105  			testStatuses = append(testStatuses, model.TaskTimeout)
   106  		}
   107  	}
   108  
   109  	taskStatuses := []string{}
   110  	for _, s := range thc.TaskStatuses {
   111  		switch s {
   112  		case "pass":
   113  			taskStatuses = append(taskStatuses, evergreen.TaskSucceeded)
   114  		case "fail":
   115  			taskStatuses = append(taskStatuses, evergreen.TaskFailed)
   116  		case "sysfail":
   117  			taskStatuses = append(taskStatuses, model.TaskSystemFailure)
   118  		case "timeout":
   119  			taskStatuses = append(taskStatuses, model.TaskTimeout)
   120  		}
   121  	}
   122  
   123  	sort := -1
   124  	if thc.Earliest {
   125  		sort = 1
   126  	}
   127  
   128  	if thc.AfterRevision != "" && len(thc.AfterRevision) != 40 {
   129  		return errors.Errorf("after revision must be a 40 character revision")
   130  	}
   131  
   132  	if thc.BeforeRevision != "" && len(thc.BeforeRevision) != 40 {
   133  		return errors.Errorf("before revision must be a 40 character revision")
   134  	}
   135  
   136  	if thc.Format == "" {
   137  		thc.Format = prettyFormat
   138  	}
   139  	beforeDate := time.Time{}
   140  	if thc.BeforeDate != "" {
   141  		beforeDate, err = time.Parse(timeFormat, thc.BeforeDate)
   142  		if err != nil {
   143  			return errors.Errorf("before date should have format YYYY-MM-DDTHH:MM:SS, error: %v", err)
   144  		}
   145  	}
   146  	afterDate := time.Time{}
   147  	if thc.AfterDate != "" {
   148  		afterDate, err = time.Parse(timeFormat, thc.AfterDate)
   149  		if err != nil {
   150  			return errors.Errorf("after date should have format YYYY-MM-DDTHH:MM:SS, error: %v", err)
   151  		}
   152  	}
   153  
   154  	// create a test history parameter struct and validate it
   155  	testHistoryParameters := model.TestHistoryParameters{
   156  		Project:        thc.Project,
   157  		TaskNames:      thc.Tasks,
   158  		TestNames:      thc.Tests,
   159  		BuildVariants:  thc.Variants,
   160  		TaskStatuses:   taskStatuses,
   161  		TestStatuses:   testStatuses,
   162  		BeforeRevision: thc.BeforeRevision,
   163  		AfterRevision:  thc.AfterRevision,
   164  		BeforeDate:     beforeDate,
   165  		AfterDate:      afterDate,
   166  		Sort:           sort,
   167  		Limit:          thc.Limit,
   168  	}
   169  
   170  	if err := testHistoryParameters.SetDefaultsAndValidate(); err != nil {
   171  		return err
   172  	}
   173  	isCSV := false
   174  	if thc.Format == csvFormat {
   175  		isCSV = true
   176  	}
   177  	body, err := rc.GetTestHistory(testHistoryParameters.Project, createUrlQuery(testHistoryParameters), isCSV)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	defer body.Close()
   182  
   183  	if thc.Format == prettyFormat {
   184  		results := []service.RestTestHistoryResult{}
   185  
   186  		if err := util.ReadJSONInto(body, &results); err != nil {
   187  			return err
   188  		}
   189  
   190  		fmt.Printf(prettyStringFormat, "Start Time", "Duration(ms)", "Variant", "Task Name", "Test File", "URL")
   191  		for _, thr := range results {
   192  			if !util.IsZeroTime(thr.StartTime) {
   193  				formattedStart := thr.StartTime.Format(time.ANSIC)
   194  				fmt.Printf(prettyStringFormat, formattedStart, thr.DurationMS, thr.BuildVariant,
   195  					thr.TaskName, thr.TestFile, thr.Url)
   196  			}
   197  		}
   198  		return nil
   199  	}
   200  
   201  	return WriteToFile(body, thc.Filepath)
   202  
   203  }