github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/internal/testing/registry.go (about) 1 // Copyright 2017 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 testing 6 7 import ( 8 "fmt" 9 "runtime" 10 "strings" 11 12 "github.com/google/go-cmp/cmp" 13 "github.com/google/go-cmp/cmp/cmpopts" 14 15 "go.chromium.org/tast/core/errors" 16 "go.chromium.org/tast/core/internal/packages" 17 ) 18 19 // Registry holds tests and services. 20 type Registry struct { 21 name string 22 errors []error 23 allTests []*TestInstance 24 testNames map[string]struct{} // names of registered tests 25 allServices []*Service 26 allPres map[string]Precondition 27 allFixtures map[string]*FixtureInstance 28 allVars map[string]Var // all registered global runtime variables 29 varRawValues map[string]string // raw values of global runtime variables 30 } 31 32 // NewRegistry returns a new test registry. 33 func NewRegistry(name string) *Registry { 34 return &Registry{ 35 name: name, 36 testNames: make(map[string]struct{}), 37 allPres: make(map[string]Precondition), 38 allFixtures: make(map[string]*FixtureInstance), 39 allVars: make(map[string]Var), 40 } 41 } 42 43 // Name returns the name of the registry. 44 func (r *Registry) Name() string { 45 return r.name 46 } 47 48 // Errors returns errors generated by registration method calls. 49 func (r *Registry) Errors() []error { 50 return append([]error(nil), r.errors...) 51 } 52 53 // RecordError records err as a registration error if it is not nil. 54 func (r *Registry) RecordError(err error) { 55 if err == nil { 56 return 57 } 58 file, line := userCaller() 59 r.errors = append(r.errors, errors.Wrapf(err, "%s:%d", file, line)) 60 } 61 62 // AddTest adds t to the registry. 63 func (r *Registry) AddTest(t *Test) { 64 r.RecordError(func() error { 65 tis, err := instantiate(t) 66 if err != nil { 67 return err 68 } 69 for _, ti := range tis { 70 r.AddTestInstance(ti) 71 } 72 return nil 73 }()) 74 } 75 76 // AddTestInstance adds t to the registry. 77 // In contrast to AddTest, AddTestInstance registers an instantiated test almost 78 // as-is. It just associates the test with this registry so that the test cannot 79 // be registered to other registries. 80 // AddTestInstance should be called only by unit tests. It must not be 81 // accessible by user code. 82 func (r *Registry) AddTestInstance(t *TestInstance) { 83 r.RecordError(func() error { 84 if _, ok := r.testNames[t.Name]; ok { 85 return fmt.Errorf("test %q already registered", t.Name) 86 } 87 if t.Bundle != "" { 88 return fmt.Errorf("TestInstance %q cannot be added to bundle %q since it is already added to bundle %q", t.Name, r.name, t.Bundle) 89 } 90 t.Bundle = r.name 91 // Ensure equality of preconditions with the same name. 92 if t.Pre != nil { 93 name := t.Pre.String() 94 if pre, ok := r.allPres[name]; !ok { 95 r.allPres[name] = t.Pre 96 } else if pre != t.Pre { 97 return fmt.Errorf("precondition %s has multiple instances", name) 98 } 99 } 100 r.allTests = append(r.allTests, t) 101 r.testNames[t.Name] = struct{}{} 102 return nil 103 }()) 104 } 105 106 // AddService adds s to the registry. 107 func (r *Registry) AddService(s *Service) { 108 r.allServices = append(r.allServices, s) 109 } 110 111 // AddFixture adds f to the registry. 112 func (r *Registry) AddFixture(f *Fixture, pkg string) { 113 r.RecordError(func() error { 114 fi, err := f.instantiate(pkg) 115 if err != nil { 116 return err 117 } 118 r.AddFixtureInstance(fi) 119 return nil 120 }()) 121 } 122 123 // AddFixtureInstance adds f to the registry. 124 // In contrast to AddFixture, AddFixtureInstance registers an instantiated 125 // fixture almost as-is. It just associates the fixture with this registry so 126 // that the fixture cannot be registered to other registries. 127 // AddFixtureInstance should be called only by unit tests. It must not be 128 // accessible by user code. 129 func (r *Registry) AddFixtureInstance(f *FixtureInstance) { 130 r.RecordError(func() error { 131 if _, ok := r.allFixtures[f.Name]; ok { 132 return fmt.Errorf("fixture %q already registered", f.Name) 133 } 134 if f.Bundle != "" { 135 return fmt.Errorf("FixtureInstance %q cannot be added to bundle %q since it is already added to bundle %q", f.Name, r.name, f.Bundle) 136 } 137 f.Bundle = r.name 138 r.allFixtures[f.Name] = f 139 return nil 140 }()) 141 } 142 143 // AddVar adds global variables to the registry. 144 func (r *Registry) AddVar(v Var) { 145 r.RecordError(func() error { 146 name := v.Name() 147 if _, ok := r.allVars[name]; ok { 148 return fmt.Errorf("global runtime variable %q has already been registered", v.Name()) 149 } 150 r.allVars[name] = v 151 return nil 152 }()) 153 } 154 155 // AllTests returns copies of all registered tests. 156 func (r *Registry) AllTests() []*TestInstance { 157 ts := make([]*TestInstance, len(r.allTests)) 158 for i, t := range r.allTests { 159 ts[i] = t.clone() 160 } 161 return ts 162 } 163 164 // AllServices returns copies of all registered services. 165 func (r *Registry) AllServices() []*Service { 166 return append(([]*Service)(nil), r.allServices...) 167 } 168 169 // AllFixtures returns copies of all registered fixtures. 170 func (r *Registry) AllFixtures() map[string]*FixtureInstance { 171 fs := make(map[string]*FixtureInstance) 172 for name, f := range r.allFixtures { 173 fs[name] = f 174 } 175 return fs 176 } 177 178 // AllVars returns copies of all registered all runtime variables. 179 func (r *Registry) AllVars() []Var { 180 var vars []Var 181 for _, f := range r.allVars { 182 vars = append(vars, f) 183 } 184 return vars 185 } 186 187 // userCaller finds the caller of a registration function. It ignores framework 188 // packages. 189 func userCaller() (file string, line int) { 190 for skip := 1; ; skip++ { 191 pc, file, line, _ := runtime.Caller(skip) 192 f := runtime.FuncForPC(pc) 193 name := packages.Normalize(f.Name()) 194 if strings.HasPrefix(name, packages.FrameworkPrefix+"internal/") || 195 strings.HasPrefix(name, packages.FrameworkPrefix+"testing.") { 196 continue 197 } 198 return file, line 199 } 200 } 201 202 // InitializeVars initializes all registered global variables. 203 func (r *Registry) InitializeVars(values map[string]string) error { 204 if r.varRawValues != nil { 205 if !cmp.Equal(values, r.varRawValues, cmpopts.EquateEmpty()) { 206 return errors.New("global runtime variables can only be initialized once") 207 } 208 return nil 209 } 210 // Save raw values for future comparison. 211 r.varRawValues = make(map[string]string) 212 for k, v := range values { 213 r.varRawValues[k] = v 214 } 215 // Set value for each variable. 216 for _, v := range r.allVars { 217 if stringValue, ok := values[v.Name()]; ok { 218 if err := v.Unmarshal(stringValue); err != nil { 219 return err 220 } 221 } 222 } 223 return nil 224 }