~james/core

core/spindle/engine/slot_test.go -rw-r--r-- 3.5 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 17 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package engine

import (
	"context"
	"errors"
	"testing"
	"time"

	"tangled.org/core/spindle/models"
)

func TestSemaphoreSlotterDisabledDoesNotBlock(t *testing.T) {
	t.Parallel()

	slotter := NewSemaphoreSlotter(0)

	for range 10 {
		slot, err := slotter.AcquireWorkflowSlot(context.Background(), zeroWorkflowID(), nil, Wait)
		if err != nil {
			t.Fatalf("AcquireWorkflowSlot() error = %v", err)
		}
		slot.Release()
	}
}

func TestSemaphoreSlotterBlocksUntilRelease(t *testing.T) {
	t.Parallel()

	slotter := NewSemaphoreSlotter(1)

	first, err := slotter.AcquireWorkflowSlot(context.Background(), zeroWorkflowID(), nil, Wait)
	if err != nil {
		t.Fatalf("first AcquireWorkflowSlot() error = %v", err)
	}
	releasedFirst := false
	defer func() {
		if !releasedFirst {
			first.Release()
		}
	}()

	acquired := make(chan WorkflowSlot, 1)
	errs := make(chan error, 1)
	go func() {
		slot, err := slotter.AcquireWorkflowSlot(context.Background(), zeroWorkflowID(), nil, Wait)
		if err != nil {
			errs <- err
			return
		}
		acquired <- slot
	}()

	assertNotAcquired(t, acquired, errs)

	first.Release()
	releasedFirst = true

	second := waitForSlot(t, acquired, errs)
	second.Release()
}

func TestSemaphoreSlotterHonorsContextCancellation(t *testing.T) {
	t.Parallel()

	slotter := NewSemaphoreSlotter(1)

	first, err := slotter.AcquireWorkflowSlot(context.Background(), zeroWorkflowID(), nil, Wait)
	if err != nil {
		t.Fatalf("first AcquireWorkflowSlot() error = %v", err)
	}
	defer first.Release()

	ctx, cancel := context.WithCancel(context.Background())
	cancel()

	_, err = slotter.AcquireWorkflowSlot(ctx, zeroWorkflowID(), nil, Wait)
	if !errors.Is(err, context.Canceled) {
		t.Fatalf("AcquireWorkflowSlot() error = %v, want context.Canceled", err)
	}
}

func TestSemaphoreSlotterTryDisabledDoesNotReject(t *testing.T) {
	t.Parallel()

	slotter := NewSemaphoreSlotter(0)

	for range 10 {
		slot, err := slotter.AcquireWorkflowSlot(context.Background(), zeroWorkflowID(), nil, NoWait)
		if err != nil {
			t.Fatalf("AcquireWorkflowSlot(NoWait) error = %v", err)
		}
		slot.Release()
	}
}

func TestSemaphoreSlotterTryRejectsWhenFull(t *testing.T) {
	t.Parallel()

	slotter := NewSemaphoreSlotter(1)

	first, err := slotter.AcquireWorkflowSlot(context.Background(), zeroWorkflowID(), nil, NoWait)
	if err != nil {
		t.Fatalf("first AcquireWorkflowSlot(NoWait) error = %v", err)
	}

	if _, err := slotter.AcquireWorkflowSlot(context.Background(), zeroWorkflowID(), nil, NoWait); !errors.Is(err, ErrNoWorkflowSlots) {
		t.Fatalf("AcquireWorkflowSlot(NoWait) error = %v, want ErrNoWorkflowSlots", err)
	}

	first.Release()

	second, err := slotter.AcquireWorkflowSlot(context.Background(), zeroWorkflowID(), nil, NoWait)
	if err != nil {
		t.Fatalf("AcquireWorkflowSlot(NoWait) after release error = %v", err)
	}
	second.Release()
}

func assertNotAcquired(t *testing.T, acquired <-chan WorkflowSlot, errs <-chan error) {
	t.Helper()

	select {
	case slot := <-acquired:
		slot.Release()
		t.Fatal("AcquireWorkflowSlot() acquired a slot before one was released")
	case err := <-errs:
		t.Fatalf("AcquireWorkflowSlot() returned unexpected error: %v", err)
	case <-time.After(25 * time.Millisecond):
	}
}

func waitForSlot(t *testing.T, acquired <-chan WorkflowSlot, errs <-chan error) WorkflowSlot {
	t.Helper()

	select {
	case slot := <-acquired:
		return slot
	case err := <-errs:
		t.Fatalf("AcquireWorkflowSlot() returned error: %v", err)
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for slot acquisition")
	}

	return nil
}

func zeroWorkflowID() models.WorkflowId {
	return models.WorkflowId{}
}