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  }