github.com/bazelbuild/rules_webtesting@v0.2.0/go/wtl/environment/environment.go (about)

     1  // Copyright 2016 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package environment provides an interface for defining how browser environments
    16  // are managed.
    17  package environment
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  
    23  	"github.com/bazelbuild/rules_webtesting/go/errors"
    24  	"github.com/bazelbuild/rules_webtesting/go/healthreporter"
    25  	"github.com/bazelbuild/rules_webtesting/go/metadata"
    26  	"github.com/bazelbuild/rules_webtesting/go/metadata/capabilities"
    27  	"github.com/bazelbuild/rules_webtesting/go/wtl/diagnostics"
    28  )
    29  
    30  // Env allows web_test environments to be started for controlling a browser
    31  // using Selenium WebDriver.
    32  type Env interface {
    33  	healthreporter.HealthReporter
    34  	// SetUp is called once at the beginning of the test run, and should start a
    35  	// WebDriver server. It is not necessary that the environment be healthy when
    36  	// this returns.
    37  	SetUp(ctx context.Context) error
    38  	// StartSession is called for each new WebDriver session, before the new
    39  	// session command is sent to the WebDriver server. caps is the capabilities
    40  	// sent to the proxy from the client, and the return value is the capabilities
    41  	// that should be actually sent to the WebDriver server new session command.
    42  	StartSession(ctx context.Context, id int, caps *capabilities.Capabilities) (*capabilities.Capabilities, error)
    43  	// StopSession is called for each new WebDriver session, before the delete
    44  	// session command is sent to the WebDriver server.
    45  	StopSession(ctx context.Context, id int) error
    46  	// TearDown is called at the end of the test run.
    47  	TearDown(ctx context.Context) error
    48  	// WDAddress returns the address of the WebDriver server.
    49  	WDAddress(context.Context) string
    50  }
    51  
    52  // Base is a partial implementation of Env useful as the base struct for
    53  // implementations of Env.
    54  type Base struct {
    55  	*metadata.Metadata
    56  	diagnostics.Diagnostics
    57  	name string
    58  
    59  	mu      sync.RWMutex
    60  	started bool
    61  	stopped bool
    62  }
    63  
    64  // NewBase creates a new Base environment with the given component name.
    65  func NewBase(name string, m *metadata.Metadata, d diagnostics.Diagnostics) (*Base, error) {
    66  	return &Base{
    67  		name:        name,
    68  		Metadata:    m,
    69  		Diagnostics: d,
    70  	}, nil
    71  }
    72  
    73  // SetUp starts the Environment.
    74  func (b *Base) SetUp(ctx context.Context) error {
    75  	b.mu.Lock()
    76  	defer b.mu.Unlock()
    77  	if b.started {
    78  		return errors.NewPermanent(b.Name(), "cannot be started; it has already been started once")
    79  	}
    80  	b.started = true
    81  	return nil
    82  }
    83  
    84  // StartSession merges the passed in caps with b.Metadata.Capabilities and returns the merged
    85  // capabilities that should be used when calling new session on the WebDriver
    86  // server.
    87  func (b *Base) StartSession(ctx context.Context, id int, caps *capabilities.Capabilities) (*capabilities.Capabilities, error) {
    88  	if err := b.Healthy(ctx); err != nil {
    89  		return nil, err
    90  	}
    91  	return caps.MergeOver(b.Metadata.Capabilities).Resolve(b.Metadata.Resolver())
    92  }
    93  
    94  // StopSession is a no-op implementation of Env.StopSession.
    95  func (b *Base) StopSession(ctx context.Context, _ int) error {
    96  	return b.Healthy(ctx)
    97  }
    98  
    99  // TearDown is a no-op implementation of Env.TearDown.
   100  func (b *Base) TearDown(ctx context.Context) error {
   101  	b.mu.Lock()
   102  	defer b.mu.Unlock()
   103  	if !b.started {
   104  		return errors.NewPermanent(b.Name(), "cannot be stopped; it was never started")
   105  	}
   106  	if b.stopped {
   107  		return errors.NewPermanent(b.Name(), "cannot be stopped; it was already stopped once")
   108  	}
   109  	b.stopped = true
   110  	return nil
   111  }
   112  
   113  // Healthy returns nil (i.e., healthy) iff the environment has been started and
   114  // has not yet been stopped.
   115  func (b *Base) Healthy(context.Context) error {
   116  	b.mu.RLock()
   117  	defer b.mu.RUnlock()
   118  	if !b.started {
   119  		return errors.New(b.Name(), "has not been started")
   120  	}
   121  	if b.stopped {
   122  		return errors.NewPermanent(b.Name(), "has been stopped")
   123  	}
   124  	return nil
   125  }
   126  
   127  // WDAddress returns the empty string.
   128  func (*Base) WDAddress(context.Context) string {
   129  	return ""
   130  }
   131  
   132  // Name returns the name of this environment to be used in error messages.
   133  func (b *Base) Name() string {
   134  	return b.name
   135  }