github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/shared/util.go (about)

     1  // Copyright 2017 The WPT Dashboard Project. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package shared
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"reflect"
    11  	"regexp"
    12  	"strings"
    13  
    14  	mapset "github.com/deckarep/golang-set"
    15  )
    16  
    17  // ExperimentalLabel is the implicit label present for runs marked 'experimental'.
    18  const ExperimentalLabel = "experimental"
    19  
    20  // LatestSHA is a helper for the 'latest' keyword/special case.
    21  const LatestSHA = "latest"
    22  
    23  // StableLabel is the implicit label present for runs marked 'stable'.
    24  const StableLabel = "stable"
    25  
    26  // BetaLabel is the implicit label present for runs marked 'beta'.
    27  const BetaLabel = "beta"
    28  
    29  // MasterLabel is the implicit label present for runs marked 'master',
    30  // i.e. run from the master branch.
    31  const MasterLabel = "master"
    32  
    33  // PRBaseLabel is the implicit label for running just the affected tests on a
    34  // PR but without the changes (i.e. against the base branch).
    35  const PRBaseLabel = "pr_base"
    36  
    37  // PRHeadLabel is the implicit label for running just the affected tests on the
    38  // head of a PR (with the changes).
    39  const PRHeadLabel = "pr_head"
    40  
    41  // UserLabelPrefix is a prefix used to denote a label for a user's GitHub handle,
    42  // prefixed because usernames are essentially user input.
    43  const UserLabelPrefix = "user:"
    44  
    45  // WPTRepoOwner is the owner (username) for the GitHub wpt repo.
    46  const WPTRepoOwner = "web-platform-tests"
    47  
    48  // WPTRepoName is the repo name for the GitHub wpt repo.
    49  const WPTRepoName = "wpt"
    50  
    51  // GetUserLabel prefixes the given username with the prefix for using as a label.
    52  func GetUserLabel(username string) string {
    53  	return UserLabelPrefix + username
    54  }
    55  
    56  // ProductChannelToLabel maps known product-specific channel names
    57  // to the wpt.fyi model's equivalent.
    58  func ProductChannelToLabel(channel string) string {
    59  	switch channel {
    60  	case "release", StableLabel:
    61  		return StableLabel
    62  	case BetaLabel:
    63  		return BetaLabel
    64  	case "dev", "nightly", "preview", ExperimentalLabel:
    65  		return ExperimentalLabel
    66  	}
    67  	return ""
    68  }
    69  
    70  // GetDefaultProducts returns the default set of products to show on wpt.fyi
    71  func GetDefaultProducts() ProductSpecs {
    72  	browserNames := GetDefaultBrowserNames()
    73  	products := make(ProductSpecs, len(browserNames))
    74  	for i, name := range browserNames {
    75  		products[i] = ProductSpec{}
    76  		products[i].BrowserName = name
    77  	}
    78  	return products
    79  }
    80  
    81  // ToStringSlice converts a set to a typed string slice.
    82  func ToStringSlice(set mapset.Set) []string {
    83  	if set == nil {
    84  		return nil
    85  	}
    86  	slice := set.ToSlice()
    87  	result := make([]string, len(slice))
    88  	for i, item := range slice {
    89  		result[i] = item.(string)
    90  	}
    91  	return result
    92  }
    93  
    94  // IsLatest returns whether a SHA[0:10] is empty or "latest", both
    95  // of which are treated as looking up the latest run for each browser.
    96  func IsLatest(sha string) bool {
    97  	return sha == "" || sha == "latest"
    98  }
    99  
   100  // NewSetFromStringSlice is a helper for the inability to cast []string to []interface{}
   101  func NewSetFromStringSlice(items []string) mapset.Set {
   102  	set := mapset.NewSet()
   103  	if items == nil {
   104  		return set
   105  	}
   106  	for _, i := range items {
   107  		set.Add(i)
   108  	}
   109  	return set
   110  }
   111  
   112  // StringSliceContains returns true if the given slice contains the given string.
   113  func StringSliceContains(ss []string, s string) bool {
   114  	for _, i := range ss {
   115  		if i == s {
   116  			return true
   117  		}
   118  	}
   119  	return false
   120  }
   121  
   122  // MapStringKeys returns the keys in the given string-keyed map.
   123  func MapStringKeys(m interface{}) ([]string, error) {
   124  	mapType := reflect.ValueOf(m)
   125  	if mapType.Kind() != reflect.Map {
   126  		return nil, errors.New("Interface is not a map type")
   127  	}
   128  	keys := mapType.MapKeys()
   129  	strKeys := make([]string, len(keys))
   130  	for i, key := range keys {
   131  		var ok bool
   132  		if strKeys[i], ok = key.Interface().(string); !ok {
   133  			return nil, fmt.Errorf("Key %v was not a string type", key)
   134  		}
   135  	}
   136  	return strKeys, nil
   137  }
   138  
   139  // GetResultsURL constructs the URL to the result of a single test file in the
   140  // given run.
   141  func GetResultsURL(run TestRun, testFile string) (resultsURL string) {
   142  	resultsURL = run.ResultsURL
   143  	if testFile != "" && testFile != "/" {
   144  		// Assumes that result files are under a directory named SHA[0:10].
   145  		resultsBase := strings.SplitAfter(resultsURL, "/"+run.Revision)[0]
   146  		resultsPieces := strings.Split(resultsURL, "/")
   147  		re := regexp.MustCompile("(-summary_v2)?\\.json\\.gz$")
   148  		product := re.ReplaceAllString(resultsPieces[len(resultsPieces)-1], "")
   149  		resultsURL = fmt.Sprintf("%s/%s/%s", resultsBase, product, testFile)
   150  	}
   151  	return resultsURL
   152  }
   153  
   154  // CropString conditionally crops a string to the given length, if it is longer.
   155  // Returns the original string otherwise.
   156  func CropString(s string, i int) string {
   157  	if len(s) <= i {
   158  		return s
   159  	}
   160  	return s[:i]
   161  }
   162  
   163  // GetSharedPath gets the longest path shared between the given paths.
   164  func GetSharedPath(paths ...string) string {
   165  	var parts []string
   166  	for _, path := range paths {
   167  		if parts == nil {
   168  			parts = strings.Split(path, "/")
   169  		} else {
   170  			otherParts := strings.Split(path, "/")
   171  			for i, part := range parts {
   172  				if part == otherParts[i] {
   173  					continue
   174  				}
   175  				// Crop to the matching parts, append empty last-part
   176  				// so that we have a trailing slash.
   177  				parts = append(parts[:i], "")
   178  				break
   179  			}
   180  		}
   181  	}
   182  	return strings.Join(parts, "/")
   183  }