github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/internal/testing/fixt.go (about)

     1  // Copyright 2020 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  	"context"
     9  	"regexp"
    10  	"time"
    11  
    12  	"go.chromium.org/tast/core/errors"
    13  	"go.chromium.org/tast/core/internal/protocol"
    14  )
    15  
    16  // Fixture represents fixtures to register to the framework.
    17  type Fixture struct {
    18  	// Name is the name of the fixture.
    19  	// Tests and fixtures use the name to specify the fixture.
    20  	// The name must be camelCase starting with a lowercase letter and containing only digits and letters.
    21  	Name string
    22  
    23  	// Desc is the description of the fixture.
    24  	Desc string
    25  
    26  	// Contacts is a list of email addresses of persons and groups who are familiar with the
    27  	// fixture. At least one personal email address of an active committer should be specified so
    28  	// that we can file bugs or ask for code review.
    29  	Contacts []string
    30  
    31  	// Impl is the implementation of the fixture.
    32  	Impl FixtureImpl
    33  
    34  	// Parent specifies the parent fixture name, or empty if it has no parent.
    35  	Parent string
    36  
    37  	// SetUpTimeout is the timeout applied to SetUp.
    38  	// Even if fixtures are nested, the timeout is applied only to this stage.
    39  	// This timeout is by default 0.
    40  	SetUpTimeout time.Duration
    41  
    42  	// ResetTimeout is the timeout applied to Reset.
    43  	// Even if fixtures are nested, the timeout is applied only to this stage.
    44  	// This timeout is by default 0.
    45  	ResetTimeout time.Duration
    46  
    47  	// PreTestTimeout is the timeout applied to PreTest.
    48  	// Even if fixtures are nested, the timeout is applied only to this stage.
    49  	// This timeout is by default 0.
    50  	PreTestTimeout time.Duration
    51  
    52  	// PostTestTimeout is the timeout applied to PostTest.
    53  	// Even if fixtures are nested, the timeout is applied only to this stage.
    54  	// This timeout is by default 0.
    55  	PostTestTimeout time.Duration
    56  
    57  	// TearDownTimeout is the timeout applied to TearDown.
    58  	// Even if fixtures are nested, the timeout is applied only to this stage.
    59  	// This timeout is by default 0.
    60  	TearDownTimeout time.Duration
    61  
    62  	// ServiceDeps contains a list of RPC service names in local test bundles that this remote fixture
    63  	// will access. This field is valid only for remote fixtures.
    64  	ServiceDeps []string
    65  
    66  	// Vars contains the names of runtime variables used to pass out-of-band data to tests.
    67  	// Values are supplied using "tast run -var=name=value", and tests can access values via State.Var.
    68  	Vars []string
    69  
    70  	// Data contains paths of data files needed by the fixture, relative to a
    71  	// "data" subdirectory within the directory in which the fixture is registered.
    72  	Data []string
    73  
    74  	// PrivateAttr contains freeform text private attributres describing the fixture.
    75  	PrivateAttr []string
    76  
    77  	// TODO(oka): Add Param fields.
    78  }
    79  
    80  func (f *Fixture) instantiate(pkg string) (*FixtureInstance, error) {
    81  	if err := validateFixture(f); err != nil {
    82  		return nil, err
    83  	}
    84  	return &FixtureInstance{
    85  		Pkg:             pkg,
    86  		Name:            f.Name,
    87  		Desc:            f.Desc,
    88  		Contacts:        append([]string(nil), f.Contacts...),
    89  		Impl:            f.Impl,
    90  		Parent:          f.Parent,
    91  		SetUpTimeout:    f.SetUpTimeout,
    92  		ResetTimeout:    f.ResetTimeout,
    93  		PreTestTimeout:  f.PreTestTimeout,
    94  		PostTestTimeout: f.PostTestTimeout,
    95  		TearDownTimeout: f.TearDownTimeout,
    96  		ServiceDeps:     append([]string(nil), f.ServiceDeps...),
    97  		Data:            append([]string(nil), f.Data...),
    98  		Vars:            append([]string(nil), f.Vars...),
    99  		PrivateAttr:     append([]string(nil), f.PrivateAttr...),
   100  	}, nil
   101  }
   102  
   103  // FixtureInstance represents a fixture instance registered to the framework.
   104  //
   105  // FixtureInstance is to Fixture what TestInstance is to Test.
   106  type FixtureInstance struct {
   107  	// Pkg is the package from which the fixture is registered.
   108  	Pkg string
   109  
   110  	// Following fields are copied from Fixture.
   111  	Name            string
   112  	Desc            string
   113  	Contacts        []string
   114  	Impl            FixtureImpl
   115  	Parent          string
   116  	SetUpTimeout    time.Duration
   117  	ResetTimeout    time.Duration
   118  	PreTestTimeout  time.Duration
   119  	PostTestTimeout time.Duration
   120  	TearDownTimeout time.Duration
   121  	Data            []string
   122  	ServiceDeps     []string
   123  	Vars            []string
   124  	PrivateAttr     []string
   125  
   126  	// Bundle is the name of the test bundle this test belongs to.
   127  	// This field is empty initially, and later set when the test is added
   128  	// to testing.Registry.
   129  	Bundle string
   130  }
   131  
   132  // Constraints returns EntityConstraints for this fixture.
   133  func (f *FixtureInstance) Constraints() *EntityConstraints {
   134  	return &EntityConstraints{
   135  		allVars: append([]string(nil), f.Vars...),
   136  		allData: append([]string(nil), f.Data...),
   137  	}
   138  }
   139  
   140  // EntityProto returns a protocol buffer message representation of f.
   141  func (f *FixtureInstance) EntityProto() *protocol.Entity {
   142  	return &protocol.Entity{
   143  		Type:        protocol.EntityType_FIXTURE,
   144  		Package:     f.Pkg,
   145  		Name:        f.Name,
   146  		Description: f.Desc,
   147  		Fixture:     f.Parent,
   148  		Dependencies: &protocol.EntityDependencies{
   149  			DataFiles: append([]string(nil), f.Data...),
   150  			Services:  append([]string(nil), f.ServiceDeps...),
   151  		},
   152  		Contacts: &protocol.EntityContacts{
   153  			Emails: append([]string(nil), f.Contacts...),
   154  		},
   155  		LegacyData: &protocol.EntityLegacyData{
   156  			Variables: append([]string(nil), f.Vars...),
   157  			Bundle:    f.Bundle,
   158  		},
   159  	}
   160  }
   161  
   162  // fixtureNameRegexp defines the valid fixture name pattern.
   163  var fixtureNameRegexp = regexp.MustCompile(`^[a-z][A-Za-z0-9]*$`)
   164  
   165  // validateFixture validates a user-supplied Fixture metadata.
   166  func validateFixture(f *Fixture) error {
   167  	if !fixtureNameRegexp.MatchString(f.Name) {
   168  		return errors.Errorf("invalid fixture name: %q", f.Name)
   169  	}
   170  	return nil
   171  }
   172  
   173  // FixtureImpl provides implementation of the fixture registered to the framework.
   174  type FixtureImpl interface {
   175  	// SetUp is called by the framework to set up the environment with possibly heavy-weight
   176  	// operations.
   177  	//
   178  	// The context and state passed to SetUp are associated with the fixture metadata. For example,
   179  	// testing.ContextOutDir(ctx) and s.OutDir() return the output directory allocated for the
   180  	// fixture itself. testing.ContextSoftwareDeps(ctx) fails since fixtures can't declare software
   181  	// dependencies.
   182  	//
   183  	// TODO(oka): Consider updating ContextSoftwareDeps API so that it's meaningful for fixture
   184  	// scoped contexts. For tests, ContextSoftwareDeps returns the dependencies the test declares,
   185  	// and utility methods (e.g. arc.New) use it to check that tests declare proper software
   186  	// dependencies. For fixtures, it is uncertain what ContextSoftwareDeps should return. One
   187  	// might think it could return the intersection of the software deps of the tests depending on
   188  	// the fixture, but it doesn't work considering arc.New checks OR condition of the software
   189  	// deps. Still, fixtures can call functions like arc.New that calls ContextSoftwareDeps. It
   190  	// indicates that we need to reconsider ContextSoftwareDeps API.
   191  	//
   192  	// The return value is made available to the direct children of this fixture in the entity
   193  	// graph, as long as this fixture and a child live in the same process.
   194  	// If any resource is associated with the value (e.g. Chrome browser connection), it must not
   195  	// be released in Reset, PreTest and PostTest because descendant fixtures may cache it or
   196  	// construct subresources derived from it (e.g. Chrome tab connection).
   197  	//
   198  	// SetUp is called in descending order (parents to children) when fixtures are nested.
   199  	//
   200  	// SetUp can be called multiple times. This happens when this fixture's TearDown is called
   201  	// before completing all tests depending on it in the following cases:
   202  	//  - This fixture's Reset requested it by returning an error.
   203  	//  - Ascendant fixtures' Reset requested it by returning an error.
   204  	// In any case, TearDown is called in a pair with a successful SetUp call.
   205  	//
   206  	// Errors in this method are reported as errors of the fixture itself, rather than tests
   207  	// depending on it.
   208  	//
   209  	// If one or more errors are reported in SetUp by s.Error or s.Fatal, all remaining tests
   210  	// depending on this fixture are marked failed without actually running. TearDown is not called
   211  	// in this case. If s.Fatal is called, SetUp immediately aborts.
   212  	//
   213  	// This method is the best place to do a heavy-weight setup of the system environment, e.g.
   214  	// restarting a Chrome session.
   215  	//
   216  	// Note that SetUpTimeout is by default 0. Change it to have a valid context.
   217  	SetUp(ctx context.Context, s *FixtState) interface{}
   218  
   219  	// Reset is called by the framework after each test (except for the last one) to do a
   220  	// light-weight reset of the environment to the original state.
   221  	//
   222  	// The context passed to Reset is associated with the fixture metadata. See SetUp for details.
   223  	//
   224  	// If Reset returns a non-nil error, the framework tears down and re-sets up the fixture to
   225  	// recover. To be accurate, the following methods are called in order:
   226  	//  - Descendant fixtures' TearDown in ascending order
   227  	//  - This fixture's TearDown
   228  	//  - This fixture's SetUp
   229  	//  - Descendant fixtures' SetUp in descending order
   230  	// Consequently, errors Reset returns don't affect ascendant fixtures.
   231  	//
   232  	// Returning an error from Reset is valid when light-weight reset doesn't restore the
   233  	// condition the fixture declares.
   234  	//
   235  	// Reset is called in descending order (parents to children) when fixtures are nested.
   236  	//
   237  	// This method is the best place to do a light-weight cleanup of the system environment to the
   238  	// original one when the fixture was set up, e.g. closing open Chrome tabs.
   239  	//
   240  	// Note that ResetTimeout is by default 0. Change it to have a valid context.
   241  	Reset(ctx context.Context) error
   242  
   243  	// PreTest is called by the framework before each test to do a light-weight set up for the test.
   244  	//
   245  	// The context and state passed to PreTest are associated with the test metadata. For example,
   246  	// testing.ContextOutDir(ctx) and s.OutDir() return the output directory allocated to the test.
   247  	//
   248  	// PreTest is called in descending order (parents to children) when fixtures are nested.
   249  	//
   250  	// s.Error or s.Fatal can be used to report errors in PreTest. When s.Fatal is called PreTest
   251  	// immediately aborts. The error is marked as the test failure.
   252  	//
   253  	// This method is always called if fixture is successfully reset by SetUp or Reset.
   254  	//
   255  	// In any case, PostTest is called in a pair with a successful PreTest call.
   256  	//
   257  	// If errors are reported in PreTest, it is reported as the test failure.
   258  	//
   259  	// If errors are reported in PreTest, the test and PostTest are not run.
   260  	//
   261  	// This method is the best place to do a setup for the test runs next. e.g. redirect logs to a
   262  	// file in the test's output directory.
   263  	//
   264  	// Note that PreTestTimeout is by default 0. Change it to have a valid context.
   265  	PreTest(ctx context.Context, s *FixtTestState)
   266  
   267  	// PostTest is called by the framework after each test to tear down changes PreTest made.
   268  	//
   269  	// The context and state passed to PostTest are associated with the test metadata. For example,
   270  	// testing.ContextOutDir(ctx) and s.OutDir() return the output directory allocated to the test.
   271  	//
   272  	// PostTest is called in ascending order (children to parent) when fixtures are nested.
   273  	//
   274  	// s.Error or s.Fatal can be used to report errors in PostTest. When s.Fatal is called PostTest
   275  	// immediately aborts. The error is marked as the test failure.
   276  	//
   277  	// This method is always called if PreTest succeeds.
   278  	//
   279  	// PostTest is always called in a pair with a successful PreTest call.
   280  	//
   281  	// If errors are reported in PostTest, it is reported as the test failure.
   282  	//
   283  	// The errors PostTest reports don't affect the order of the fixture methods the framework
   284  	// calls.
   285  	//
   286  	// This method is the best place to tear down changes PreTest made. e.g. close log files in the
   287  	// test output directory.
   288  	//
   289  	// Note that PostTestTimeout is by default 0. Change it to have a valid context.
   290  	PostTest(ctx context.Context, s *FixtTestState)
   291  
   292  	// TearDown is called by the framework to tear down the environment SetUp set up.
   293  	//
   294  	// The context and state passed to TearDown are associated with the fixture metadata. See SetUp
   295  	// for details.
   296  	//
   297  	// TearDown is called in an ascending order (children to parents) when fixtures are nested.
   298  	//
   299  	// TearDown is always called in a pair with a successful SetUp call.
   300  	//
   301  	// Errors in this method are reported as errors of the fixture itself, rather than tests
   302  	// depending on it.
   303  	//
   304  	// Errors in TearDown doesn't affect the order of the fixture methods the framework calls.
   305  	// That is, even if this fixture's TearDown reports errors, its ascendants' TearDown are still
   306  	// called.
   307  	//
   308  	// This method is the best place to tear down changes SetUp made. e.g. Unenroll enterprise
   309  	// enrollment.
   310  	// Changes that shouldn't hinder healthy execution of succeeding tests are not necessarily to
   311  	// be teared down. e.g. Chrome session can be left open.
   312  	//
   313  	// Note that TearDownTimeout is by default 0. Change it to have a valid context.
   314  	TearDown(ctx context.Context, s *FixtState)
   315  }