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 }