...

Source file src/edge-infra.dev/test/f2/fctx/context.go

Documentation: edge-infra.dev/test/f2/fctx

     1  // Package fctx provides utilities for working with f2 test contexts.
     2  //
     3  // This package is implemented based on the idea that nil references and panic()
     4  // should not happen in test code being exercised by the framework. It includes
     5  // helpers for acessing context values in type-safe ways without extra boilerplate
     6  // per access.
     7  package fctx
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"reflect"
    13  	"testing"
    14  	"time"
    15  )
    16  
    17  func init() {
    18  	// TODO: allow providing random seed
    19  }
    20  
    21  // ErrNotFound occurs when the expected value does not exist in the context.
    22  var ErrNotFound = errors.New("not found in context")
    23  
    24  // Context is the f2.Framework test context. It carries test configuration
    25  // and state throughout the various stages of test execution. Individual tests
    26  // and framework functions can pass state in a loosely coupled way by using
    27  // WithValue, like a standard context.Context.
    28  type Context struct {
    29  	RunID string
    30  
    31  	context.Context
    32  }
    33  
    34  // InnerContext returns the wrapped [context.Context]
    35  func (c Context) InnerContext() context.Context {
    36  	return c.Context
    37  }
    38  
    39  // WithInnerContext updates the wrapped [context.Context]
    40  func (c Context) WithInnerContext(ctx context.Context) Context {
    41  	c.Context = ctx
    42  	return c
    43  }
    44  
    45  // Assert is a helper method for creating an assert instance in a test.
    46  // func (c Context) Assert(t *testing.T) *assert.Assertions {
    47  // 	return assert.New(t)
    48  // }
    49  
    50  // Require is a helper method for creating an require instance in a test.
    51  // Require is an assertion instance that immediately ends the test if an
    52  // assertion fails.
    53  // func (c Context) Require(t *testing.T) *require.Assertions {
    54  // 	return require.New(t)
    55  // }
    56  
    57  // WithCancel is equivalent to context.WithCancel while preserving the generic
    58  // Context implementation. It should be used when mutating f2.Context.
    59  func WithCancel(c Context) (Context, context.CancelFunc) {
    60  	ctx, cancel := context.WithCancel(c.InnerContext())
    61  	return c.WithInnerContext(ctx), cancel
    62  }
    63  
    64  // WithValue is equivalent to context.WithValue while preserving the generic
    65  // Context implementation. It should be used when mutating f2.Context.
    66  func WithValue(parent Context, k, v any) Context {
    67  	return parent.WithInnerContext(context.WithValue(parent.InnerContext(), k, v))
    68  }
    69  
    70  // WithDeadline is equivalent to context.WithDeadline while preserving the generic
    71  // Context implementation. It should be used when mutating f2.Context.
    72  func WithDeadline(parent Context, d time.Time) (Context, context.CancelFunc) {
    73  	ctx, cancel := context.WithDeadline(parent.InnerContext(), d)
    74  	return parent.WithInnerContext(ctx), cancel
    75  }
    76  
    77  // WithTimeout is equivalent to context.WithTimeout while preserving the generic
    78  // Context implementation. It should be used when mutating f2.Context.
    79  func WithTimeout(parent Context, timeout time.Duration) (Context, context.CancelFunc) {
    80  	ctx, cancel := context.WithTimeout(parent.InnerContext(), timeout)
    81  	return parent.WithInnerContext(ctx), cancel
    82  }
    83  
    84  // ValueInto is a type-safe helper for storing a value in a context by its type.
    85  // Only one instance of each type can be stored. Typically used for storing
    86  // framework extensions in context, since there should only be one of those per
    87  // instance of the framework.
    88  func ValueInto[V any](parent Context, v *V) Context {
    89  	return WithValue(parent, reflect.TypeOf((*V)(nil)), v)
    90  }
    91  
    92  // ValueFrom allows retrieving a type-safe value that was stored using ValueInto[T]
    93  // Only one instance of each type can be stored. Typically used for storing
    94  // framework extensions in context, since there should only be one of those per
    95  // instance of the framework.
    96  func ValueFrom[V any](ctx context.Context) *V {
    97  	v, _ := ctx.Value(reflect.TypeOf((*V)(nil))).(*V)
    98  	return v
    99  }
   100  
   101  // ValueFromT is a testing variant of ValueFrom that will automatically fail the
   102  // test if the value being retrieved doesn't exist in ctx.
   103  func ValueFromT[V any](ctx context.Context, t *testing.T) *V {
   104  	v := ValueFrom[V](ctx)
   105  	if v == nil {
   106  		t.Fatalf("%v: %T", ErrNotFound, v)
   107  	}
   108  	return v
   109  }
   110  

View as plain text