1// Copyright 2019 The SwiftShader Authors. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Package testlist provides utilities for handling test lists. 16package testlist 17 18import ( 19 "bytes" 20 "crypto/sha1" 21 "encoding/gob" 22 "encoding/hex" 23 "encoding/json" 24 "io/ioutil" 25 "path/filepath" 26 "sort" 27 "strings" 28 29 "../cause" 30) 31 32// API is an enumerator of graphics APIs. 33type API string 34 35// Graphics APIs. 36const ( 37 EGL = API("egl") 38 GLES2 = API("gles2") 39 GLES3 = API("gles3") 40 Vulkan = API("vulkan") 41) 42 43// Group is a list of tests to be run for a single API. 44type Group struct { 45 Name string 46 File string 47 API API 48 Tests []string 49} 50 51// Filter returns a new Group that contains only tests that match the predicate. 52func (g Group) Filter(pred func(string) bool) Group { 53 out := Group{ 54 Name: g.Name, 55 File: g.File, 56 API: g.API, 57 } 58 for _, test := range g.Tests { 59 if pred(test) { 60 out.Tests = append(out.Tests, test) 61 } 62 } 63 return out 64} 65 66// Lists is the full list of tests to be run. 67type Lists []Group 68 69// Filter returns a new Lists that contains only tests that match the predicate. 70func (l Lists) Filter(pred func(string) bool) Lists { 71 out := Lists{} 72 for _, group := range l { 73 filtered := group.Filter(pred) 74 if len(filtered.Tests) > 0 { 75 out = append(out, filtered) 76 } 77 } 78 return out 79} 80 81// Hash returns a SHA1 hash of the set of tests. 82func (l Lists) Hash() string { 83 h := sha1.New() 84 if err := gob.NewEncoder(h).Encode(l); err != nil { 85 panic(cause.Wrap(err, "Could not encode testlist to produce hash")) 86 } 87 return hex.EncodeToString(h.Sum(nil)) 88} 89 90// Load loads the test list json file and returns the full set of tests. 91func Load(root, jsonPath string) (Lists, error) { 92 root, err := filepath.Abs(root) 93 if err != nil { 94 return nil, cause.Wrap(err, "Couldn't get absolute path of '%s'", root) 95 } 96 97 jsonPath, err = filepath.Abs(jsonPath) 98 if err != nil { 99 return nil, cause.Wrap(err, "Couldn't get absolute path of '%s'", jsonPath) 100 } 101 102 i, err := ioutil.ReadFile(jsonPath) 103 if err != nil { 104 return nil, cause.Wrap(err, "Couldn't read test list from '%s'", jsonPath) 105 } 106 107 var jsonGroups []struct { 108 Name string 109 API string 110 TestFile string `json:"tests"` 111 } 112 if err := json.NewDecoder(bytes.NewReader(i)).Decode(&jsonGroups); err != nil { 113 return nil, cause.Wrap(err, "Couldn't parse '%s'", jsonPath) 114 } 115 116 dir := filepath.Dir(jsonPath) 117 118 out := make(Lists, len(jsonGroups)) 119 for i, jsonGroup := range jsonGroups { 120 path := filepath.Join(dir, jsonGroup.TestFile) 121 tests, err := ioutil.ReadFile(path) 122 if err != nil { 123 return nil, cause.Wrap(err, "Couldn't read '%s'", tests) 124 } 125 relPath, err := filepath.Rel(root, path) 126 if err != nil { 127 return nil, cause.Wrap(err, "Couldn't get relative path for '%s'", path) 128 } 129 group := Group{ 130 Name: jsonGroup.Name, 131 File: relPath, 132 API: API(jsonGroup.API), 133 } 134 for _, line := range strings.Split(string(tests), "\n") { 135 line = strings.TrimSpace(line) 136 if line != "" && !strings.HasPrefix(line, "#") { 137 group.Tests = append(group.Tests, line) 138 } 139 } 140 sort.Strings(group.Tests) 141 out[i] = group 142 } 143 144 return out, nil 145} 146 147// Status is an enumerator of test results. 148type Status string 149 150const ( 151 // Pass is the status of a successful test. 152 Pass = Status("PASS") 153 // Fail is the status of a failed test. 154 Fail = Status("FAIL") 155 // Timeout is the status of a test that failed to complete in the alloted 156 // time. 157 Timeout = Status("TIMEOUT") 158 // Crash is the status of a test that crashed. 159 Crash = Status("CRASH") 160 // Unimplemented is the status of a test that failed with UNIMPLEMENTED(). 161 Unimplemented = Status("UNIMPLEMENTED") 162 // Unsupported is the status of a test that failed with UNSUPPORTED(). 163 Unsupported = Status("UNSUPPORTED") 164 // Unreachable is the status of a test that failed with UNREACHABLE(). 165 Unreachable = Status("UNREACHABLE") 166 // Assert is the status of a test that failed with ASSERT() or ASSERT_MSG(). 167 Assert = Status("ASSERT") 168 // Abort is the status of a test that failed with ABORT(). 169 Abort = Status("ABORT") 170 // NotSupported is the status of a test feature not supported by the driver. 171 NotSupported = Status("NOT_SUPPORTED") 172 // CompatibilityWarning is the status passing test with a warning. 173 CompatibilityWarning = Status("COMPATIBILITY_WARNING") 174 // QualityWarning is the status passing test with a warning. 175 QualityWarning = Status("QUALITY_WARNING") 176) 177 178// Statuses is the full list of status types 179var Statuses = []Status{ 180 Pass, 181 Fail, 182 Timeout, 183 Crash, 184 Unimplemented, 185 Unsupported, 186 Unreachable, 187 Assert, 188 Abort, 189 NotSupported, 190 CompatibilityWarning, 191 QualityWarning, 192} 193 194// Failing returns true if the task status requires fixing. 195func (s Status) Failing() bool { 196 switch s { 197 case Fail, Timeout, Crash, Unimplemented, Unreachable, Assert, Abort: 198 return true 199 case Unsupported: 200 // This may seem surprising that this should be a failure, however these 201 // should not be reached, as dEQP should not be using features that are 202 // not advertised. 203 return true 204 default: 205 return false 206 } 207} 208 209// Passing returns true if the task status is considered a pass. 210func (s Status) Passing() bool { 211 switch s { 212 case Pass, CompatibilityWarning, QualityWarning: 213 return true 214 default: 215 return false 216 } 217} 218 219// FilePathWithStatus returns the path to the test list file with the status 220// appended before the file extension. 221func FilePathWithStatus(listPath string, status Status) string { 222 ext := filepath.Ext(listPath) 223 name := listPath[:len(listPath)-len(ext)] 224 return name + "-" + string(status) + ext 225} 226