blob: 130513972950220632f316749a027ae2b61afe83 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package concurrency
import (
"testing"
)
// createsBranch creates a branch of a simple execution tree used for
// testing the state implementation. This branch emulates an execution
// in which two threads compete for a mutex.
func createBranch(i, j TID) []*choice {
choices := make([]*choice, 0)
set := resourceSet{"mutex": struct{}{}}
{
c := newChoice()
c.next = i
ti := &transition{
tid: i,
clock: newClock(),
enabled: true,
kind: tMutexLock,
readSet: set,
writeSet: set,
}
ti.clock[i] = 1
c.transitions[i] = ti
tj := &transition{
tid: j,
clock: newClock(),
enabled: true,
kind: tMutexLock,
readSet: set,
writeSet: set,
}
tj.clock[j] = 1
c.transitions[j] = tj
choices = append(choices, c)
}
{
c := newChoice()
c.next = i
ti := &transition{
tid: i,
clock: newClock(),
enabled: true,
kind: tMutexUnlock,
readSet: set,
writeSet: set,
}
ti.clock[i] = 2
c.transitions[i] = ti
tj := &transition{
tid: j,
clock: newClock(),
enabled: false,
kind: tMutexLock,
readSet: set,
writeSet: set,
}
tj.clock[j] = 1
c.transitions[j] = tj
choices = append(choices, c)
}
{
c := newChoice()
c.next = j
tj := &transition{
tid: j,
clock: newClock(),
enabled: true,
kind: tMutexLock,
readSet: set,
writeSet: set,
}
tj.clock[j] = 1
c.transitions[j] = tj
choices = append(choices, c)
}
{
c := newChoice()
c.next = j
tj := &transition{
tid: j,
clock: newClock(),
enabled: true,
kind: tMutexUnlock,
readSet: set,
writeSet: set,
}
tj.clock[i] = 2
tj.clock[j] = 2
c.transitions[j] = tj
choices = append(choices, c)
}
return choices
}
// TestCommon checks common operation of the state implementation.
func TestCommon(t *testing.T) {
leftBranch := createBranch(0, 1)
rightBranch := createBranch(1, 0)
root := newState(nil, 0)
seeds := &stack{}
if err := root.addBranch(leftBranch, seeds); err != nil {
t.Fatalf("addBranch() failed: %v", err)
}
// Check a new exploration seed has been identified.
if seeds.Length() != 1 {
t.Fatalf("Unexpected number of seeds: expected %v, got %v", 1, seeds.Length())
}
if err := root.addBranch(rightBranch, seeds); err != nil {
t.Fatalf("addBranch() failed: %v", err)
}
// Check no new exploration seeds have been identified.
if seeds.Length() != 1 {
t.Fatalf("Unexpected number of seeds: expected %v, got %v", 1, seeds.Length())
}
// Check exploration status have been correctly updated.
if !root.explored {
t.Fatalf("Unexpected exploration status: expected %v, got %v", true, root.explored)
}
// Check compation of explored children subtrees.
if len(root.children) != 0 {
t.Fatalf("Unexpected number of children: expected %v, got %v", true, root.explored)
}
}
// TestDivergence checks the various types of execution divergence.
func TestDivergence(t *testing.T) {
// Emulate a missing transition.
{
leftBranch := createBranch(0, 1)
rightBranch := createBranch(1, 0)
root := newState(nil, 0)
seeds := &stack{}
if err := root.addBranch(leftBranch, seeds); err != nil {
t.Fatalf("addBranch() failed: %v", err)
}
delete(rightBranch[0].transitions, 0)
if err := root.addBranch(rightBranch, seeds); err == nil {
t.Fatalf("addBranch() did not fail")
}
}
// Emulate an extra transition.
{
leftBranch := createBranch(0, 1)
rightBranch := createBranch(1, 0)
root := newState(nil, 0)
seeds := &stack{}
if err := root.addBranch(leftBranch, seeds); err != nil {
t.Fatalf("addBranch() failed: %v", err)
}
rightBranch[0].transitions[2] = &transition{}
if err := root.addBranch(rightBranch, seeds); err == nil {
t.Fatalf("addBranch() did not fail")
}
}
// Emulate divergent transition enabledness.
{
leftBranch := createBranch(0, 1)
rightBranch := createBranch(1, 0)
root := newState(nil, 0)
seeds := &stack{}
if err := root.addBranch(leftBranch, seeds); err != nil {
t.Fatalf("addBranch() failed: %v", err)
}
rightBranch[0].transitions[0].enabled = false
if err := root.addBranch(rightBranch, seeds); err == nil {
t.Fatalf("addBranch() did not fail")
}
}
// Emulate divergent transition clock.
{
leftBranch := createBranch(0, 1)
rightBranch := createBranch(1, 0)
root := newState(nil, 0)
seeds := &stack{}
if err := root.addBranch(leftBranch, seeds); err != nil {
t.Fatalf("addBranch() failed: %v", err)
}
rightBranch[0].transitions[1].clock[0]++
if err := root.addBranch(rightBranch, seeds); err == nil {
t.Fatalf("addBranch() did not fail")
}
}
// Emulate divergent transition read set.
{
leftBranch := createBranch(0, 1)
rightBranch := createBranch(1, 0)
root := newState(nil, 0)
seeds := &stack{}
if err := root.addBranch(leftBranch, seeds); err != nil {
t.Fatalf("addBranch() failed: %v", err)
}
delete(rightBranch[0].transitions[1].readSet, "mutex")
if err := root.addBranch(rightBranch, seeds); err == nil {
t.Fatalf("addBranch() did not fail")
}
}
// Emulate divergent transition write set.
{
leftBranch := createBranch(0, 1)
rightBranch := createBranch(1, 0)
root := newState(nil, 0)
seeds := &stack{}
if err := root.addBranch(leftBranch, seeds); err != nil {
t.Fatalf("addBranch() failed: %v", err)
}
delete(rightBranch[0].transitions[1].writeSet, "mutex")
if err := root.addBranch(rightBranch, seeds); err == nil {
t.Fatalf("addBranch() did not fail")
}
}
}