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 }