/[sudobot]/trunk/tests/framework/concurrent/Semaphore.test.ts
ViewVC logotype

Contents of /trunk/tests/framework/concurrent/Semaphore.test.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 575 - (show annotations)
Mon Jul 29 17:59:26 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 3964 byte(s)
chore: add trunk
1 import Condition from "@framework/concurrent/Condition";
2 import Semaphore from "@framework/concurrent/Semaphore";
3 import { setTimeout } from "timers/promises";
4 import { describe, expect, it, vi } from "vitest";
5
6 describe("Semaphore", () => {
7 describe("basic usage", () => {
8 it("can be acquired and released", async () => {
9 // Arrange
10 const semaphore = new Semaphore(1);
11
12 // Act
13 await semaphore.acquire();
14 semaphore.release();
15
16 // Assert
17 expect(semaphore.availablePermits).toBe(1);
18 });
19
20 it("can be acquired and released with extraneous releases", async () => {
21 // Arrange
22 const semaphore = new Semaphore({
23 ignoreExtraneousReleases: true,
24 maxPermits: 1
25 });
26
27 // Act
28 await semaphore.acquire();
29 const permitsAfterAcquire = semaphore.availablePermits;
30 semaphore.release();
31
32 // Assert
33 expect(semaphore.availablePermits).toBe(1);
34 expect(permitsAfterAcquire).toBe(0);
35 });
36
37 it("can be acquired and released with extraneous releases and ignore them", async () => {
38 // Arrange
39 const semaphore = new Semaphore({
40 ignoreExtraneousReleases: true,
41 maxPermits: 1
42 });
43
44 // Act
45 await semaphore.acquire();
46 const permitsAfterAcquire = semaphore.availablePermits;
47 semaphore.release();
48 const permitsAfterFirstRelease = semaphore.availablePermits;
49 semaphore.release();
50
51 // Assert
52 expect(semaphore.availablePermits).toBe(1);
53 expect(permitsAfterFirstRelease).toBe(1);
54 expect(permitsAfterAcquire).toBe(0);
55 });
56
57 it("can be acquired with extraneous releases and throws an error if opted-in", async () => {
58 // Arrange
59 const semaphore = new Semaphore({
60 ignoreExtraneousReleases: false,
61 maxPermits: 1
62 });
63
64 // Act
65 await semaphore.acquire();
66 semaphore.release();
67
68 // Assert
69 expect(() => semaphore.release()).toThrowError(
70 Semaphore.EXTRANEOUS_RELEASE_ERROR_MESSAGE
71 );
72 });
73 });
74
75 describe("race condition checks", () => {
76 it(
77 "can be acquired and released with multiple acquires",
78 {
79 repeats: 10
80 },
81 async () => {
82 // Arrange
83 const semaphore = new Semaphore(1);
84 const output: number[] = [];
85 const fn1 = async () => {
86 await semaphore.acquire();
87 await setTimeout(700 * Math.random());
88 output.push(1);
89 semaphore.release();
90 };
91
92 const fn2 = async () => {
93 await semaphore.acquire();
94 await setTimeout(240 * Math.random());
95 output.push(2);
96 semaphore.release();
97 };
98
99 // Act
100 await Promise.all([fn1(), fn2(), fn2(), fn1(), fn1()]);
101
102 // Assert
103 expect(semaphore.availablePermits).toBe(1);
104 expect(output).toStrictEqual([1, 2, 2, 1, 1]);
105 }
106 );
107 });
108
109 it("works with a condition", async () => {
110 const condition = new Condition();
111
112 condition.wait = vi.fn();
113 condition.signal = vi.fn();
114
115 // Arrange
116 const semaphore = new Semaphore({
117 maxPermits: 1,
118 condition
119 });
120
121 // Act
122 await semaphore.acquire();
123
124 // Assert
125 expect(semaphore.availablePermits).toBe(0);
126 expect(condition?.wait).toHaveBeenCalled();
127 });
128 });

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26