516 lines
15 KiB
Go
516 lines
15 KiB
Go
package graphvent
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
"fmt"
|
|
"os"
|
|
"runtime/pprof"
|
|
badger "github.com/dgraph-io/badger/v3"
|
|
)
|
|
|
|
type GraphTester testing.T
|
|
const listner_timeout = 50 * time.Millisecond
|
|
|
|
func (t * GraphTester) WaitForValue(listener chan GraphSignal, signal_type string, source GraphNode, timeout time.Duration, str string) GraphSignal {
|
|
timeout_channel := time.After(timeout)
|
|
for true {
|
|
select {
|
|
case signal := <- listener:
|
|
if signal.Type() == signal_type {
|
|
Log.Logf("test", "SIGNAL_TYPE_FOUND: %s - %s %+v\n", signal.Type(), signal.Source(), listener)
|
|
if signal.Source() == source.ID() {
|
|
return signal
|
|
}
|
|
}
|
|
case <-timeout_channel:
|
|
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
|
t.Fatal(str)
|
|
return nil
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t * GraphTester) CheckForValue(listener chan GraphSignal, str string) GraphSignal {
|
|
timeout := time.After(listner_timeout)
|
|
select {
|
|
case signal := <- listener:
|
|
return signal
|
|
case <-timeout:
|
|
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
|
t.Fatal(str)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (t * GraphTester) CheckForNone(listener chan GraphSignal, str string) {
|
|
timeout := time.After(listner_timeout)
|
|
select {
|
|
case sig := <- listener:
|
|
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
|
t.Fatal(fmt.Sprintf("%s : %+v", str, sig))
|
|
case <-timeout:
|
|
}
|
|
}
|
|
|
|
func TestNewEventWithResource(t *testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger1"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
name := "Test Resource"
|
|
description := "A resource for testing"
|
|
children := []Resource{}
|
|
|
|
test_resource, _ := NewResource(name, description, children)
|
|
root_event, err := NewEvent(db, "root_event", "", []Resource{test_resource})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
res := FindResource(root_event, test_resource.ID())
|
|
if res == nil {
|
|
t.Fatal("Failed to find Resource in EventManager after adding")
|
|
}
|
|
|
|
if res.Name() != name || res.Description() != description {
|
|
t.Fatal("Name/description of returned resource did not match added resource")
|
|
}
|
|
}
|
|
|
|
func TestDoubleResourceAdd(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger2"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
test_resource, _ := NewResource("", "", []Resource{})
|
|
_, err = NewEvent(db, "", "", []Resource{test_resource, test_resource})
|
|
|
|
if err == nil {
|
|
t.Fatal("NewEvent didn't return an error")
|
|
}
|
|
}
|
|
|
|
func TestTieredResource(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger3"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r1, _ := NewResource("r1", "", []Resource{})
|
|
r2, err := NewResource("r2", "", []Resource{r1})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = NewEvent(db, "", "", []Resource{r2})
|
|
|
|
if err != nil {
|
|
t.Fatal("Failed to create event with tiered resources")
|
|
}
|
|
}
|
|
|
|
func TestResourceUpdate(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger4"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r1, err := NewResource("r1", "", []Resource{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r2, err := NewResource("r2", "", []Resource{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r3, err := NewResource("r3", "", []Resource{r1, r2})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r4, err := NewResource("r4", "", []Resource{r3})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = NewEvent(db, "", "", []Resource{r3, r4})
|
|
if err != nil {
|
|
t.Fatal("Failed to add initial tiered resources for test")
|
|
}
|
|
|
|
r1_l := r1.UpdateChannel()
|
|
r2_l := r2.UpdateChannel()
|
|
r3_l := r3.UpdateChannel()
|
|
r4_l := r4.UpdateChannel()
|
|
|
|
// Calling Update() on the parent with no other parents should only notify node listeners
|
|
SendUpdate(r3, NewSignal(nil, "test"))
|
|
(*GraphTester)(t).CheckForNone(r1_l, "Update on r1 after updating r3")
|
|
(*GraphTester)(t).CheckForNone(r2_l, "Update on r2 after updating r3")
|
|
(*GraphTester)(t).CheckForValue(r3_l, "No update on r3 after updating r3")
|
|
(*GraphTester)(t).CheckForValue(r4_l, "No update on r4 after updating r3")
|
|
|
|
// Calling Update() on a child should notify listeners of the parent and child, but not siblings
|
|
SendUpdate(r2, NewSignal(nil, "test"))
|
|
(*GraphTester)(t).CheckForNone(r1_l, "Update on r1 after updating r2")
|
|
(*GraphTester)(t).CheckForValue(r2_l, "No update on r2 after updating r2")
|
|
(*GraphTester)(t).CheckForValue(r3_l, "No update on r3 after updating r2")
|
|
(*GraphTester)(t).CheckForValue(r4_l, "No update on r4 after updating r2")
|
|
|
|
// Calling Update() on a child should notify listeners of the parent and child, but not siblings
|
|
SendUpdate(r1, NewSignal(nil, "test"))
|
|
(*GraphTester)(t).CheckForValue(r1_l, "No update on r1 after updating r1")
|
|
(*GraphTester)(t).CheckForNone(r2_l, "Update on r2 after updating r1")
|
|
(*GraphTester)(t).CheckForValue(r3_l, "No update on r3 after updating r1")
|
|
(*GraphTester)(t).CheckForValue(r4_l, "No update on r4 after updating r1")
|
|
}
|
|
|
|
func TestAddEvent(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger5"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r1, _ := NewResource("r1", "", []Resource{})
|
|
r2, _ := NewResource("r2", "", []Resource{r1})
|
|
root_event, _ := NewEvent(db, "", "", []Resource{r2})
|
|
|
|
name := "Test Event"
|
|
description := "A test event"
|
|
resources := []Resource{r2}
|
|
new_event, _ := NewEvent(db, name, description, resources)
|
|
|
|
err = LinkEvent(root_event, new_event, nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to add new_event to root_event: %s", err)
|
|
}
|
|
|
|
res := FindChild(root_event, new_event.ID())
|
|
if res == nil {
|
|
t.Fatalf("Failed to find new_event in event_manager: %s", err)
|
|
}
|
|
|
|
if res.Name() != name || res.Description() != description {
|
|
t.Fatal("Event found in event_manager didn't match added")
|
|
}
|
|
|
|
res_required := res.Resources()
|
|
if len(res_required) < 1 {
|
|
t.Fatal("Event found in event_manager didn't match added")
|
|
} else if res_required[0].ID() != r2.ID() {
|
|
t.Fatal("Event found in event_manager didn't match added")
|
|
}
|
|
}
|
|
|
|
func TestLockResource(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger6"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r1, err := NewResource("r1", "", []Resource{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r2, err := NewResource("r2", "", []Resource{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r3, err := NewResource("r3", "", []Resource{r1, r2})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r4, err := NewResource("r4", "", []Resource{r1, r2})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root_event, err := NewEvent(db, "", "", []Resource{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
test_event, err := NewEvent(db, "", "", []Resource{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r1_l := r1.UpdateChannel()
|
|
rel := root_event.UpdateChannel()
|
|
|
|
err = LockResource(r3, root_event)
|
|
if err != nil {
|
|
t.Fatal("Failed to lock r3")
|
|
}
|
|
SendUpdate(r3, NewDownSignal(r3, "locked"))
|
|
|
|
(*GraphTester)(t).WaitForValue(r1_l, "locked", r3, time.Second, "Wasn't notified of r1 lock on r1 after r3 lock")
|
|
(*GraphTester)(t).WaitForValue(rel, "locked", r3, time.Second, "Wasn't notified of r1 lock on rel after r3 lock")
|
|
|
|
err = LockResource(r3, root_event)
|
|
if err == nil {
|
|
t.Fatal("Locked r3 after locking r3")
|
|
}
|
|
|
|
err = LockResource(r4, root_event)
|
|
if err == nil {
|
|
t.Fatal("Locked r4 after locking r3")
|
|
}
|
|
|
|
err = LockResource(r1, root_event)
|
|
if err == nil {
|
|
t.Fatal("Locked r1 after locking r3")
|
|
}
|
|
|
|
err = UnlockResource(r3, test_event)
|
|
if err == nil {
|
|
t.Fatal("Unlocked r3 with event that didn't lock it")
|
|
}
|
|
|
|
err = UnlockResource(r3, root_event)
|
|
if err != nil {
|
|
t.Fatal("Failed to unlock r3")
|
|
}
|
|
SendUpdate(r3, NewDownSignal(r3, "unlocked"))
|
|
(*GraphTester)(t).WaitForValue(r1_l, "unlocked", r3, time.Second * 2, "Wasn't notified of r1 unlock on r1 after r3 unlock")
|
|
|
|
err = LockResource(r4, root_event)
|
|
if err != nil {
|
|
t.Fatal("Failed to lock r4 after unlocking r3")
|
|
}
|
|
SendUpdate(r4, NewDownSignal(r4, "locked"))
|
|
(*GraphTester)(t).WaitForValue(r1_l, "locked", r4, time.Second * 2, "Wasn't notified of r1 lock on r1 after r4 lock")
|
|
(*GraphTester)(t).WaitForValue(rel, "locked", r4, time.Second * 2, "Wasn't notified of r1 lock on r1 after r4 lock")
|
|
|
|
err = UnlockResource(r4, root_event)
|
|
if err != nil {
|
|
t.Fatal("Failed to unlock r4")
|
|
}
|
|
SendUpdate(r4, NewDownSignal(r4, "unlocked"))
|
|
(*GraphTester)(t).WaitForValue(r1_l, "unlocked", r4, time.Second * 2, "Wasn't notified of r1 unlock on r1 after r4 lock")
|
|
}
|
|
|
|
func TestAddToEventQueue(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger7"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
queue, _ := NewEventQueue("q", "", []Resource{})
|
|
event_1, _ := NewEvent(db, "1", "", []Resource{})
|
|
event_2, _ := NewEvent(db, "2", "", []Resource{})
|
|
|
|
err = LinkEvent(queue, event_1, nil)
|
|
if err == nil {
|
|
t.Fatal("suceeded in added nil info to queue")
|
|
}
|
|
|
|
err = LinkEvent(queue, event_1, &EventQueueInfo{priority: 0})
|
|
if err != nil {
|
|
t.Fatal("failed to add valid event + info to queue")
|
|
}
|
|
|
|
err = LinkEvent(queue, event_2, &EventQueueInfo{priority: 1})
|
|
if err != nil {
|
|
t.Fatal("failed to add valid event + info to queue")
|
|
}
|
|
}
|
|
|
|
func TestStartBaseEvent(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger8"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
event_1, _ := NewEvent(db, "TestStartBaseEvent event_1", "", []Resource{})
|
|
r := event_1.DoneResource()
|
|
|
|
e_l := event_1.UpdateChannel()
|
|
r_l := r.UpdateChannel()
|
|
(*GraphTester)(t).CheckForNone(e_l, "Update on event_1 before starting")
|
|
(*GraphTester)(t).CheckForNone(r_l, "Update on r_1 before starting")
|
|
|
|
if r.Owner().ID() != event_1.ID() {
|
|
t.Fatal("r is not owned by event_1")
|
|
}
|
|
|
|
err = LockResources(event_1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = RunEvent(event_1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Check that the update channels for the event and resource have updates
|
|
(*GraphTester)(t).WaitForValue(e_l, "event_start", event_1, 1*time.Second, "No event_start on e_l")
|
|
(*GraphTester)(t).WaitForValue(e_l, "event_done", event_1, 1*time.Second, "No event_start on e_l")
|
|
(*GraphTester)(t).WaitForValue(r_l, "unlocked", event_1, 1*time.Second, "No unlocked on r_l")
|
|
|
|
if r.Owner() != nil {
|
|
t.Fatal("r still owned after event completed")
|
|
}
|
|
}
|
|
|
|
func TestAbortEventQueue(t * testing.T) {
|
|
r1, _ := NewResource("r1", "", []Resource{})
|
|
root_event, _ := NewEventQueue("root_event", "", []Resource{})
|
|
r := root_event.DoneResource()
|
|
|
|
LockResource(r1, root_event)
|
|
|
|
// Now that the event is constructed with a queue and 3 basic events
|
|
// start the queue and check that all the events are executed
|
|
go func() {
|
|
time.Sleep(100 * time.Millisecond)
|
|
abort_signal := NewDownSignal(nil, "abort")
|
|
SendUpdate(root_event, abort_signal)
|
|
}()
|
|
|
|
err := LockResources(root_event)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = RunEvent(root_event)
|
|
if err == nil {
|
|
t.Fatal("root_event completed without error")
|
|
}
|
|
|
|
if r.Owner() == nil {
|
|
t.Fatal("root event was finished after starting")
|
|
}
|
|
}
|
|
|
|
func TestDelegateLock(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
test_resource, _ := NewResource("test_resource", "", []Resource{})
|
|
root_event, _ := NewEventQueue("root_event", "", []Resource{test_resource})
|
|
test_event, _ := NewEvent(db, "test_event", "", []Resource{test_resource})
|
|
err = LinkEvent(root_event, test_event, NewEventQueueInfo(1))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = LockResources(root_event)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
test_listener := test_event.UpdateChannel()
|
|
|
|
go func() {
|
|
(*GraphTester)(t).WaitForValue(test_listener, "event_done", test_event, 250 * time.Millisecond, "No event_done for test_event")
|
|
if test_resource.Owner().ID() != root_event.ID() {
|
|
t.Fatal("Lock failed to pass back to root_event")
|
|
}
|
|
abort_signal := NewDownSignal(nil, "cancel")
|
|
SendUpdate(root_event, abort_signal)
|
|
}()
|
|
|
|
err = RunEvent(root_event)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestStartWithoutLocking(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger9"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
test_resource, _ := NewResource("test_resource", "", []Resource{})
|
|
root_event, _ := NewEvent(db, "root_event", "", []Resource{test_resource})
|
|
|
|
err = RunEvent(root_event)
|
|
if err == nil {
|
|
t.Fatal("Event ran without error without locking resources")
|
|
}
|
|
}
|
|
|
|
func TestStartEventQueue(t * testing.T) {
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger10"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root_event, _ := NewEventQueue("root_event", "", []Resource{})
|
|
r := root_event.DoneResource()
|
|
rel := root_event.UpdateChannel();
|
|
res_1, _ := NewResource("test_resource_1", "", []Resource{})
|
|
res_2, _ := NewResource("test_resource_2", "", []Resource{})
|
|
|
|
|
|
e1, _ := NewEvent(db, "e1", "", []Resource{res_1, res_2})
|
|
e1_l := e1.UpdateChannel()
|
|
e1_r := e1.DoneResource()
|
|
e1_info := NewEventQueueInfo(1)
|
|
err = LinkEvent(root_event, e1, e1_info)
|
|
if err != nil {
|
|
t.Fatal("Failed to add e1 to root_event")
|
|
}
|
|
(*GraphTester)(t).WaitForValue(rel, "child_added", root_event, time.Second, "No update on root_event after adding e1")
|
|
|
|
e2, _ := NewEvent(db, "e2", "", []Resource{res_1})
|
|
e2_l := e2.UpdateChannel()
|
|
e2_r := e2.DoneResource()
|
|
e2_info := NewEventQueueInfo(2)
|
|
err = LinkEvent(root_event, e2, e2_info)
|
|
if err != nil {
|
|
t.Fatal("Failed to add e2 to root_event")
|
|
}
|
|
(*GraphTester)(t).WaitForValue(rel, "child_added", root_event, time.Second, "No update on root_event after adding e2")
|
|
|
|
e3, _ := NewEvent(db, "e3", "", []Resource{res_2})
|
|
e3_l := e3.UpdateChannel()
|
|
e3_r := e3.DoneResource()
|
|
e3_info := NewEventQueueInfo(3)
|
|
err = LinkEvent(root_event, e3, e3_info)
|
|
if err != nil {
|
|
t.Fatal("Failed to add e3 to root_event")
|
|
}
|
|
(*GraphTester)(t).WaitForValue(rel, "child_added", root_event, time.Second, "No update on root_event after adding e3")
|
|
|
|
// Abort the event after 5 seconds just in case
|
|
go func() {
|
|
time.Sleep(5 * time.Second)
|
|
if r.Owner() != nil {
|
|
abort_signal := NewDownSignal(nil, "abort")
|
|
SendUpdate(root_event, abort_signal)
|
|
}
|
|
}()
|
|
|
|
// Now that a root_event is constructed with a queue and 3 basic events
|
|
// start the queue and check that all the events are executed
|
|
go func() {
|
|
(*GraphTester)(t).WaitForValue(e1_l, "event_done", e1, time.Second, "No event_done for e3")
|
|
(*GraphTester)(t).WaitForValue(e2_l, "event_done", e2, time.Second, "No event_done for e3")
|
|
(*GraphTester)(t).WaitForValue(e3_l, "event_done", e3, time.Second, "No event_done for e3")
|
|
signal := NewDownSignal(nil, "cancel")
|
|
SendUpdate(root_event, signal)
|
|
}()
|
|
|
|
err = LockResources(root_event)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = RunEvent(root_event)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if r.Owner() != nil {
|
|
t.Fatal("root event was not finished after starting")
|
|
}
|
|
|
|
if e1_r.Owner() != nil {
|
|
t.Fatal(fmt.Sprintf("e1 was not completed: %s", e1_r.Owner()))
|
|
}
|
|
|
|
if e2_r.Owner() != nil {
|
|
t.Fatal(fmt.Sprintf("e2 was not completed"))
|
|
}
|
|
|
|
if e3_r.Owner() != nil {
|
|
t.Fatal("e3 was not completed")
|
|
}
|
|
}
|
|
|