github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/internal/testing/service.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  	"fmt"
    10  
    11  	"google.golang.org/grpc"
    12  
    13  	"go.chromium.org/tast/core/internal/logging"
    14  )
    15  
    16  // Service contains information about a gRPC service exported for remote tests.
    17  type Service struct {
    18  	// Register is a function called by the framework to register a gRPC service
    19  	// to grpc.Server. This should be a simple function that constructs a gRPC
    20  	// service implementation and calls pb.Register*Server.
    21  	Register func(srv *grpc.Server, s *ServiceState)
    22  	// Vars contains the names of runtime variables used by the service.
    23  	Vars []string
    24  	// GuaranteeCompatibility indicates that the service needs to strictly adhere to
    25  	// the backward and forward compatibility guarantees when evolving proto definition
    26  	// and implementation.
    27  	// Once the flag is marked as True, it cannot be change to False in subsequent
    28  	// versions.
    29  	// The service will be exposed to non-Tast test harness clients.
    30  	GuaranteeCompatibility bool
    31  }
    32  
    33  // ServiceRoot is the root of service state data.
    34  type ServiceRoot struct {
    35  	// svc is the registered service instance.
    36  	service *Service
    37  	// vars has the runtime variables.
    38  	vars map[string]string
    39  }
    40  
    41  // NewServiceRoot creates a new ServiceRoot object.
    42  func NewServiceRoot(svc *Service, vars map[string]string) *ServiceRoot {
    43  	return &ServiceRoot{service: svc, vars: vars}
    44  }
    45  
    46  // ServiceState holds state relevant to a gRPC service.
    47  type ServiceState struct {
    48  	// ctx is a service-scoped context. It can be used to emit logs with
    49  	// testing.ContextLog. It is canceled on gRPC server shutdown.
    50  	ctx context.Context
    51  
    52  	root *ServiceRoot
    53  }
    54  
    55  // NewServiceState creates a new ServiceState.
    56  func NewServiceState(ctx context.Context, root *ServiceRoot) *ServiceState {
    57  	return &ServiceState{
    58  		ctx:  ctx,
    59  		root: root,
    60  	}
    61  }
    62  
    63  // Log formats its arguments using default formatting and logs them.
    64  // Logs are sent to the currently connected remote bundle.
    65  func (s *ServiceState) Log(args ...interface{}) {
    66  	logging.Info(s.ctx, args...)
    67  }
    68  
    69  // Logf is similar to Log but formats its arguments using fmt.Sprintf.
    70  // Logs are sent to the currently connected remote bundle.
    71  func (s *ServiceState) Logf(format string, args ...interface{}) {
    72  	logging.Infof(s.ctx, format, args...)
    73  }
    74  
    75  // ServiceContext returns a service-scoped context. A service-scoped context is
    76  // canceled on gRPC service shutdown, while a context passed to a gRPC method is
    77  // canceled on completion of the gRPC method call. Therefore a service-scoped
    78  // context can be used to run background operations that span across multiple
    79  // gRPC calls (e.g. starting a background subprocess).
    80  // A service-scoped context can also be used with testing.ContextLog to send
    81  // logs to the currently connected remote bundle.
    82  func (s *ServiceState) ServiceContext() context.Context {
    83  	return s.ctx
    84  }
    85  
    86  // Var returns the value for the named variable.
    87  // The variable must be declared in the service definition with Vars.
    88  // If a value was not supplied at runtime via the -var flag, false will be returned.
    89  func (s *ServiceState) Var(name string) (val string, ok bool) {
    90  	seen := false
    91  	for _, n := range s.root.service.Vars {
    92  		if n == name {
    93  			seen = true
    94  			break
    95  		}
    96  	}
    97  	if !seen {
    98  		panic(fmt.Sprintf("Variable %q was not registered in service", name))
    99  	}
   100  	val, ok = s.root.vars[name]
   101  	return val, ok
   102  }