github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/testing/testcheck/testcheck.go (about) 1 // Copyright 2023 The ChromiumOS Authors 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 testcheck provides common functions to check test definitions. 6 package testcheck 7 8 import ( 9 "fmt" 10 "strings" 11 gotesting "testing" 12 "time" 13 14 "go.chromium.org/tast/core/internal/testing" 15 ) 16 17 // SetAllTestsforTest sets all tests to use in this package. This is mainly used in unittest for testing purpose. 18 func SetAllTestsforTest(tests []*testing.TestInstance) func() { 19 allTests = func() []*testing.TestInstance { 20 return tests 21 } 22 return func() { 23 allTests = testing.GlobalRegistry().AllTests 24 } 25 } 26 27 // TestFilter defines the condition whether or not the test should be checked. 28 type TestFilter func(t *testing.TestInstance) bool 29 30 var allTests func() []*testing.TestInstance = testing.GlobalRegistry().AllTests 31 32 func getTests(t *gotesting.T, f TestFilter) []*testing.TestInstance { 33 var tests []*testing.TestInstance 34 for _, tst := range allTests() { 35 if f(tst) { 36 tests = append(tests, tst) 37 } 38 } 39 if len(tests) == 0 { 40 t.Fatalf("No tests matched") 41 } 42 return tests 43 } 44 45 // Glob returns a TestFilter which returns true for a test if the test name 46 // matches with the given glob pattern. 47 func Glob(t *gotesting.T, glob string) TestFilter { 48 re, err := testing.NewTestGlobRegexp(glob) 49 if err != nil { 50 t.Fatalf("Bad glob %q: %v", glob, err) 51 } 52 return func(t *testing.TestInstance) bool { 53 return re.MatchString(t.Name) 54 } 55 } 56 57 // Timeout checks that tests matched by f have timeout no less than minTimeout. 58 func Timeout(t *gotesting.T, f TestFilter, minTimeout time.Duration) { 59 for _, tst := range getTests(t, f) { 60 if tst.Timeout < minTimeout { 61 t.Errorf("%s: timeout is too short (%v < %v)", tst.Name, tst.Timeout, minTimeout) 62 } 63 } 64 } 65 66 // Attr checks that tests matched by f declare requiredAttr as Attr. 67 // requiredAttr is a list of items which the test's Attr must have. 68 // Each item is one or '|'-connected multiple attr names, and Attr must contain at least one of them. 69 func Attr(t *gotesting.T, f TestFilter, requiredAttr []string) { 70 for _, tst := range getTests(t, f) { 71 attr := make(map[string]struct{}) 72 for _, at := range tst.Attr { 73 attr[at] = struct{}{} 74 } 75 CheckLoop: 76 for _, at := range requiredAttr { 77 for _, item := range strings.Split(at, "|") { 78 if _, ok := attr[item]; ok { 79 continue CheckLoop 80 } 81 } 82 t.Errorf("%s: missing attribute %q", tst.Name, at) 83 } 84 } 85 } 86 87 // IfAttr checks that tests matched by f declare requiredAttr as Attr if all Attr in criteriaAttr are present. 88 // criteriaAttr is a list of items to apply to test's Attr. 89 // requiredAttr is a list of items which the test's Attr must have if criteriaAttr are matched. 90 // Each item is one or '|'-connected multiple attr names, and Attr must contain at least one of them. 91 // Example, criteriaAttr=["A", "B|C"], requiredAttr=["D", "E|F"] 92 // Any tests with Attr A and either B or C should define Attr D and either E or F. 93 func IfAttr(t *gotesting.T, f TestFilter, criteriaAttr, requiredAttr []string) { 94 TestLoop: 95 for _, tst := range getTests(t, f) { 96 attr := make(map[string]struct{}) 97 for _, at := range tst.Attr { 98 attr[at] = struct{}{} 99 } 100 CriteriaLoop: 101 for _, at := range criteriaAttr { 102 for _, item := range strings.Split(at, "|") { 103 if _, ok := attr[item]; ok { 104 continue CriteriaLoop 105 } 106 } 107 // Any one of the criteria is not met. 108 continue TestLoop 109 } 110 CheckLoop: 111 for _, at := range requiredAttr { 112 for _, item := range strings.Split(at, "|") { 113 if _, ok := attr[item]; ok { 114 continue CheckLoop 115 } 116 } 117 t.Errorf("%s: missing attribute %q", tst.Name, at) 118 } 119 } 120 } 121 122 // SoftwareDeps checks that tests matched by f declare requiredDeps as software dependencies. 123 // requiredDeps is a list of items which the test's SoftwareDeps needs to 124 // satisfy. Each item is one or '|'-connected multiple software feature names, 125 // and SoftwareDeps must contain at least one of them. 126 // TODO: b/225978622 -- support multi-dut in test check. 127 func SoftwareDeps(t *gotesting.T, f TestFilter, requiredDeps []string) { 128 for _, tst := range getTests(t, f) { 129 deps := make(map[string]struct{}) 130 for _, d := range tst.SoftwareDeps[""] { 131 deps[d] = struct{}{} 132 } 133 CheckLoop: 134 for _, d := range requiredDeps { 135 for _, item := range strings.Split(d, "|") { 136 if _, ok := deps[item]; ok { 137 continue CheckLoop 138 } 139 } 140 t.Errorf("%s: missing software dependency %q", tst.Name, d) 141 } 142 } 143 } 144 145 // EntityType represents a type of an entity, such as a test or a fixture. 146 type EntityType int 147 148 const ( 149 // Test represents that an entity is a test. 150 Test EntityType = iota 151 // Fixture represents that an entity is a fixture. 152 Fixture 153 ) 154 155 func (e EntityType) String() string { 156 switch e { 157 case Test: 158 return "test" 159 case Fixture: 160 return "fixture" 161 default: 162 return fmt.Sprintf("unknown(%d)", e) 163 } 164 } 165 166 // Entity represents a node in the dependency graph of tests and fixtures. 167 type Entity struct { 168 Type EntityType 169 Name string 170 Parent string 171 PrivateAttrs map[string]struct{} 172 Attrs map[string]struct{} 173 } 174 175 // HasPrivateAttr returns whether the node has a given private attribute. 176 func (e *Entity) HasPrivateAttr(name string) bool { 177 _, ok := e.PrivateAttrs[name] 178 return ok 179 } 180 181 // HasAttr returns whether the node has given Attr. 182 func (e *Entity) HasAttr(name string) bool { 183 _, ok := e.Attrs[name] 184 return ok 185 } 186 187 // Entities gives all dependency data of all tests. 188 func Entities() map[string]Entity { 189 stringSet := func(list []string) map[string]struct{} { 190 m := make(map[string]struct{}) 191 for _, v := range list { 192 m[v] = struct{}{} 193 } 194 return m 195 } 196 result := make(map[string]Entity) 197 for _, tst := range allTests() { 198 result[tst.Name] = Entity{ 199 Type: Test, 200 Name: tst.Name, 201 Parent: tst.Fixture, 202 PrivateAttrs: stringSet(tst.PrivateAttr), 203 Attrs: stringSet(tst.Attr), 204 } 205 } 206 for n, f := range testing.GlobalRegistry().AllFixtures() { 207 result[n] = Entity{ 208 Type: Fixture, 209 Name: n, 210 Parent: f.Parent, 211 PrivateAttrs: stringSet(f.PrivateAttr), 212 Attrs: nil, 213 } 214 } 215 return result 216 }