github.com/GoogleCloudPlatform/testgrid@v0.0.174/internal/result/results.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package result
    18  
    19  import (
    20  	"context"
    21  
    22  	configpb "github.com/GoogleCloudPlatform/testgrid/pb/config"
    23  	statepb "github.com/GoogleCloudPlatform/testgrid/pb/state"
    24  	statuspb "github.com/GoogleCloudPlatform/testgrid/pb/test_status"
    25  )
    26  
    27  const (
    28  	// IgnoreRunning maps RUNNING to NO_RESULT
    29  	IgnoreRunning = true
    30  	// ShowRunning maps RUNNING to UNKNOWN
    31  	ShowRunning = false
    32  )
    33  
    34  var (
    35  	statusSeverity = map[statuspb.TestStatus]int{
    36  		statuspb.TestStatus_NO_RESULT:         0,
    37  		statuspb.TestStatus_BUILD_PASSED:      1,
    38  		statuspb.TestStatus_PASS:              2,
    39  		statuspb.TestStatus_PASS_WITH_SKIPS:   3,
    40  		statuspb.TestStatus_PASS_WITH_ERRORS:  4,
    41  		statuspb.TestStatus_RUNNING:           5,
    42  		statuspb.TestStatus_CATEGORIZED_ABORT: 6,
    43  		statuspb.TestStatus_UNKNOWN:           7,
    44  		statuspb.TestStatus_CANCEL:            8,
    45  		statuspb.TestStatus_BLOCKED:           9,
    46  		statuspb.TestStatus_FLAKY:             10,
    47  		statuspb.TestStatus_TOOL_FAIL:         11,
    48  		statuspb.TestStatus_TIMED_OUT:         12,
    49  		statuspb.TestStatus_CATEGORIZED_FAIL:  13,
    50  		statuspb.TestStatus_BUILD_FAIL:        14,
    51  		statuspb.TestStatus_FAIL:              15,
    52  	}
    53  )
    54  
    55  // LTE returns if rowResult is less than or equal to compareTo.
    56  func LTE(rowResult, compareTo statuspb.TestStatus) bool {
    57  	return statusSeverity[rowResult] <= statusSeverity[compareTo]
    58  }
    59  
    60  // GTE returns if rowResult is greater than or equal to compareTo.
    61  func GTE(rowResult, compareTo statuspb.TestStatus) bool {
    62  	return statusSeverity[rowResult] >= statusSeverity[compareTo]
    63  }
    64  
    65  // Passing returns true if the test status is any passing status,
    66  // including PASS_WITH_SKIPS, BUILD_PASSED, and more.
    67  func Passing(rowResult statuspb.TestStatus) bool {
    68  	return GTE(rowResult, statuspb.TestStatus_BUILD_PASSED) && LTE(rowResult, statuspb.TestStatus_PASS_WITH_ERRORS)
    69  }
    70  
    71  // Failing returns true if the test status is any failing status,
    72  // including CATEGORIZED_FAILURE, BUILD_FAIL, and more.
    73  func Failing(rowResult statuspb.TestStatus) bool {
    74  	return GTE(rowResult, statuspb.TestStatus_TOOL_FAIL) && LTE(rowResult, statuspb.TestStatus_FAIL)
    75  }
    76  
    77  // Ignored returns true if the test status is any ignored status,
    78  // configured via Config > DashboardTab > DashboardTabStatusCustomizationOptions > IgnoredTestStatuses
    79  func Ignored(rowResult statuspb.TestStatus, opts *configpb.DashboardTabStatusCustomizationOptions) bool {
    80  	for _, ignoredStatus := range opts.GetIgnoredTestStatuses() {
    81  		if (ignoredStatus == configpb.DashboardTabStatusCustomizationOptions_CATEGORIZED_ABORT && rowResult == statuspb.TestStatus_CATEGORIZED_ABORT) ||
    82  			(ignoredStatus == configpb.DashboardTabStatusCustomizationOptions_UNKNOWN && rowResult == statuspb.TestStatus_UNKNOWN) ||
    83  			(ignoredStatus == configpb.DashboardTabStatusCustomizationOptions_CANCEL && rowResult == statuspb.TestStatus_CANCEL) ||
    84  			(ignoredStatus == configpb.DashboardTabStatusCustomizationOptions_BLOCKED && rowResult == statuspb.TestStatus_BLOCKED) {
    85  			return true
    86  		}
    87  	}
    88  	return false
    89  }
    90  
    91  // Coalesce reduces the result to PASS, NO_RESULT, FAIL, FLAKY or UNKNOWN.
    92  func Coalesce(result statuspb.TestStatus, ignoreRunning bool) statuspb.TestStatus {
    93  	// TODO(fejta): other result types, not used by k8s testgrid
    94  	if result == statuspb.TestStatus_NO_RESULT || result == statuspb.TestStatus_RUNNING && ignoreRunning {
    95  		return statuspb.TestStatus_NO_RESULT
    96  	}
    97  	if result == statuspb.TestStatus_FLAKY {
    98  		return result
    99  	}
   100  	if Failing(result) {
   101  		return statuspb.TestStatus_FAIL
   102  	}
   103  	if Passing(result) {
   104  		return statuspb.TestStatus_PASS
   105  	}
   106  	return statuspb.TestStatus_UNKNOWN
   107  }
   108  
   109  func iterSlow(ctx context.Context, results []int32) <-chan statuspb.TestStatus {
   110  	out := make(chan statuspb.TestStatus)
   111  	go func() {
   112  		defer close(out)
   113  		for i := 0; i+1 < len(results); i += 2 {
   114  			select { // Non-blocking check to see if we're done
   115  			case <-ctx.Done():
   116  				return
   117  			default:
   118  			}
   119  			result := statuspb.TestStatus(results[i])
   120  			count := results[i+1]
   121  			for count > 0 {
   122  				select { // Non-blocking check to see if we're done
   123  				case <-ctx.Done():
   124  					return
   125  				default:
   126  				}
   127  				select {
   128  				case <-ctx.Done():
   129  					return
   130  				case out <- result:
   131  					count--
   132  				}
   133  			}
   134  		}
   135  	}()
   136  	return out
   137  }
   138  
   139  // Iter returns a function that returns the result for each column, decoding the run-length-encoding.
   140  func Iter(results []int32) IterFunc {
   141  	return iterFast(results)
   142  }
   143  
   144  func iterFast(results []int32) IterFunc {
   145  	var i int
   146  	n := len(results)
   147  	var more int32
   148  	var status statuspb.TestStatus
   149  	return func() (statuspb.TestStatus, bool) {
   150  		for {
   151  			if more > 0 {
   152  				more--
   153  				return status, true
   154  			}
   155  			if i+1 >= n {
   156  				return statuspb.TestStatus_NO_RESULT, false
   157  			}
   158  			status = statuspb.TestStatus(results[i])
   159  			more = results[i+1]
   160  			i += 2
   161  		}
   162  	}
   163  }
   164  
   165  // IterFunc returns a result for each column (and then !ok).
   166  //
   167  // This decodes the run-length-encoding for a row's results.
   168  type IterFunc func() (result statuspb.TestStatus, ok bool)
   169  
   170  // Map returns a per-column result output channel for each row.
   171  func Map(rows []*statepb.Row) map[string]IterFunc {
   172  	iters := map[string]IterFunc{}
   173  	for _, r := range rows {
   174  		iters[r.Name] = Iter(r.Results)
   175  	}
   176  	return iters
   177  }