resource

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2022 License: MIT Imports: 5 Imported by: 2

README

Godog Resource Lock

Build Status Coverage Status GoDevDoc Time Tracker Code lines Comments

This library provides a simple way to manage sequential access to global resources in your godog tests.

This is useful if godog suite is running with concurrency option greater than 1.

Usage

Add *resource.Lock to your resource manager.

// StorageSteps manages an example global external storage.
type storageSteps struct {
	lock *resource.Lock
}

Register *resource.Lock hooks to scenario context.

func (s *storageSteps) Register(sc *godog.ScenarioContext) {
	s.lock.Register(sc)
...

Upgrade your step definitions to receive context if you don't have it already.

sc.Step(`write file "([^"])" with contents`, func(ctx context.Context, path string, contents string) error {

Acquire resource before using it in scenario step. This will block all other scenarios that will try to acquire this resource to wait until current scenario is finished.

func (s *storageSteps) acquireFile(ctx context.Context, path string) error {
	ok, err := s.lock.Acquire(ctx, path)
	if err != nil {
		return err
	}

	if !ok {
		return fmt.Errorf("could not acquire file for scenario: %s", path)
	}

	return nil
}

See complete instrumentation example.

Documentation

Overview

Package resource provides a helper to synchronize access to a shared resources from concurrent scenarios.

Index

Examples

Constants

View Source
const ErrMissingScenarioLock = sentinelError("missing scenario lock key in context")

ErrMissingScenarioLock is a sentinel error.

Variables

This section is empty.

Functions

This section is empty.

Types

type Lock

type Lock struct {
	// contains filtered or unexported fields
}

Lock keeps exclusive access to the scenario steps.

func NewLock

func NewLock(onRelease func(name string) error) *Lock

NewLock creates a new Lock.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/cucumber/godog"
	"github.com/godogx/resource"
)

// StorageSteps manages an example global external storage.
type storageSteps struct {
	lock *resource.Lock
}

func newStorageSteps() *storageSteps {
	return &storageSteps{
		lock: resource.NewLock(func(path string) error {
			// The onRelease hook can be used to clean up the state,
			// for example to delete files that were acquired during the scenario.
			return nil
		}),
	}
}

func (s *storageSteps) acquireFile(ctx context.Context, path string) error {
	ok, err := s.lock.Acquire(ctx, path)
	if err != nil {
		return err
	}

	if !ok {
		return fmt.Errorf("could not acquire file for scenario: %s", path)
	}

	return nil
}

func (s *storageSteps) Register(sc *godog.ScenarioContext) {
	s.lock.Register(sc)

	sc.Step(`write file "([^"])" with contents`, func(ctx context.Context, path string, contents string) error {
		// By acquiring the lock, we ensure no other scenario can read or write this file
		// while this scenario is running.

		// Since we're using path as resource name, scenarios that depend on other paths
		// won't be blocked by this lock.
		if err := s.acquireFile(ctx, path); err != nil {
			return err
		}
		// Write contents to file here.
		return nil
	})

	sc.Step(`file "([^"])" has contents`, func(ctx context.Context, path string, contents string) error {
		// If the lock is acquired within the same scenario, there is no block.
		if err := s.acquireFile(ctx, path); err != nil {
			return err
		}
		// Read and compare contents of the file here.
		return nil
	})
}

func main() {
	ss := newStorageSteps()
	suite := godog.TestSuite{
		ScenarioInitializer: ss.Register,
		Options: &godog.Options{
			Format:      "pretty",
			Paths:       []string{"features"},
			Strict:      true,
			Randomize:   time.Now().UTC().UnixNano(),
			Concurrency: 10,
		},
	}
	status := suite.Run()

	if status != 0 {
		log.Fatal("test failed")
	}
}
Output:

func (*Lock) Acquire

func (s *Lock) Acquire(ctx context.Context, name string) (bool, error)

Acquire acquires resource lock for the given key and returns true.

If the lock is already held by another context, it waits for the lock to be released. It returns false is the lock is already held by this context. This function fails if the context is missing current lock.

func (*Lock) IsLocked

func (s *Lock) IsLocked(ctx context.Context, name string) bool

IsLocked is true if resource is currently locked for another scenario.

func (*Lock) Register

func (s *Lock) Register(sc *godog.ScenarioContext)

Register adds hooks to scenario context.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL