tracetest

package
v0.0.0-...-8299741 Latest Latest
Warning

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

Go to latest
Published: Feb 7, 2024 License: GPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package tracetest provides infrastructure for testing concurrent systems based on synchronous event tracing.

A serial system can be verified by checking that its execution produces expected serial stream of events. But concurrent systems cannot be verified by exactly this way because events are only partly-ordered with respect to each other by causality or so called happens-before relation.

However in a concurrent system one can decompose all events into serial streams in which events should be strictly ordered by causality with respect to each other. This decomposition in turn allows to verify that in every stream events happenned as expected.

Verification of events for all streams can be done by one *sequential* process:

  • if events A and B in different streams are unrelated to each other by causality, the sequence of checks models a particular possible flow of time. Notably since events are delivered synchronously and sender is blocked until receiver/checker explicitly confirms event has been processed, by checking either A then B, or B then A allows to check for a particular race-condition.

  • if events A and B in different streams are related to each other by causality (i.e. there is some happens-before relation for them) the sequence of checking should represent that ordering relation.

Basic package usage is as follows:

func TestSomething(t *testing.T) {
    tracetest.Verify(t, func(t *tracetest.T) {
        // setup tracing so that events of test system are collected and
        // synchronously delivered to t.RxEvent. This can be done with e.g.
        // package lab.nexedi.com/kirr/go123/tracing or by other similar means.
        ...

        // tell t to which stream an event should go.
        t.SetEventRouter(...)

        // run the system and verify it produces expected events

        // <code to start the system>
        t.Expect("<stream₁>", eventOk₁)
        t.Expect("<stream₂>", eventOk₂)
        ...

        // <code to further control/affect the system>
        t.Expect("<stream₃>", eventOk₃)
        t.Expect("<stream₄>", eventOk₄)
        ...
    })
}

See example_test.go for more details.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Run

func Run(t testing.TB, f func(t *T))

Run runs f under tracetest environment.

It is similar to Verify but f is ran only once. Run does not check for race conditions.

func Verify

func Verify(t *testing.T, f func(t *T))

Verify verifies a test system.

It runs f under T environment, catching race conditions, deadlocks and unexpected events. f is rerun several times and should not alter its behaviour from run to run.

Types

type T

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

T is similar to testing.T and represents tracetest test environment.

It is passed by Verify and Run to tested function.

Besides testing.TB it provides

.RxEvent	-- to where events should be synchronously delivered by the test
.SetEventRouter	-- to tell T to which stream an event should go
.Expect		-- to assert expectation of an event on a stream

func (*T) Expect

func (t *T) Expect(stream string, eventOK interface{})

Expect receives next event on stream and verifies it to be equal to eventOK.

If check is successful ACK is sent back to event producer. If check does not pass - fatal testing error is raised.

func (*T) FailNow

func (t *T) FailNow()

T overrides FailNow/Fatal/Fatalf to also cancel all in-progress sends.

func (*T) Fatal

func (t *T) Fatal(argv ...interface{})

func (*T) Fatalf

func (t *T) Fatalf(format string, argv ...interface{})

func (*T) RxEvent

func (t *T) RxEvent(event interface{})

RxEvent should be synchronously called from test system when an event occurs.

The sequential process of the test system where event originated should be paused until RxEvent returns. This requirement can be usually met via inserting t.RxEvent() call into the code that produces the event.

func (*T) SetEventRouter

func (t *T) SetEventRouter(routeEvent func(event interface{}) (stream string))

SetEventRouter tells t to which stream an event should go.

It should be called not more than once. Before SetEventRouter is called, all events go to "default" stream.

Jump to

Keyboard shortcuts

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