1 /*
2 * Copyright 2022 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Leorize, leorize+oss@disroot.org
7 */
8
9
10 #include "MemoryRingIOTest.h"
11
12 #include <stdio.h>
13 #include <string.h>
14
15 #include <OS.h>
16
17 #include <cppunit/Test.h>
18 #include <cppunit/TestCaller.h>
19 #include <cppunit/TestSuite.h>
20 #include <TestUtils.h>
21 #include <ThreadedTestCaller.h>
22
23
24 #define BIG_PAYLOAD \
25 "a really long string that can fill the buffer multiple times"
26 #define FULL_PAYLOAD "16 characters x"
27 #define SMALL_PAYLOAD "shorter"
28
29
30 static void
ReadCheck(BMemoryRingIO & ring,const void * cmp,size_t size)31 ReadCheck(BMemoryRingIO& ring, const void* cmp, size_t size)
32 {
33 char* buffer = new char[size];
34 memset(buffer, 0, size);
35 size_t read;
36 CHK(ring.ReadExactly(buffer, size, &read) == B_OK);
37 CHK(read == size);
38 CHK(memcmp(buffer, cmp, size) == 0);
39 }
40
41
42 void
WriteTest()43 MemoryRingIOTest::WriteTest()
44 {
45 CHK(fRing.InitCheck() == B_OK);
46
47 CHK(fRing.WriteExactly(SMALL_PAYLOAD, sizeof(SMALL_PAYLOAD), NULL) == B_OK);
48 CHK(fRing.WriteExactly(FULL_PAYLOAD, sizeof(FULL_PAYLOAD), NULL) == B_OK);
49 CHK(fRing.WriteExactly(BIG_PAYLOAD, sizeof(BIG_PAYLOAD), NULL) == B_OK);
50 }
51
52
53 void
ReadTest()54 MemoryRingIOTest::ReadTest()
55 {
56 CHK(fRing.InitCheck() == B_OK);
57
58 ReadCheck(fRing, SMALL_PAYLOAD, sizeof(SMALL_PAYLOAD));
59 ReadCheck(fRing, FULL_PAYLOAD, sizeof(FULL_PAYLOAD));
60 ReadCheck(fRing, BIG_PAYLOAD, sizeof(BIG_PAYLOAD));
61 }
62
63
64 void
BusyWriterTest()65 MemoryRingIOTest::BusyWriterTest()
66 {
67 CHK(fRing.InitCheck() == B_OK);
68 CHK(fRing.BufferSize() < sizeof(BIG_PAYLOAD));
69
70 CHK(fRing.WriteExactly(BIG_PAYLOAD, sizeof(BIG_PAYLOAD), NULL)
71 == B_DEVICE_FULL);
72 }
73
74
75 void
BusyReaderTest()76 MemoryRingIOTest::BusyReaderTest()
77 {
78 CHK(fRing.InitCheck() == B_OK);
79
80 char buffer[100];
81 CHK(fRing.Read(buffer, sizeof(buffer)) == 0);
82 }
83
84
85 void
ReadWriteSingleTest()86 MemoryRingIOTest::ReadWriteSingleTest()
87 {
88 CHK(fRing.SetSize(sizeof(BIG_PAYLOAD)) == B_OK);
89 CHK(fRing.WriteExactly(BIG_PAYLOAD, sizeof(BIG_PAYLOAD)) == B_OK);
90 ReadCheck(fRing, BIG_PAYLOAD, sizeof(BIG_PAYLOAD));
91
92 CHK(fRing.SetSize(sizeof(FULL_PAYLOAD)) == B_OK);
93 // the size of FULL_PAYLOAD is a power of two, so our ring
94 // should be using the exact size.
95 CHK(fRing.BufferSize() == sizeof(FULL_PAYLOAD));
96 CHK(fRing.WriteExactly(FULL_PAYLOAD, sizeof(FULL_PAYLOAD)) == B_OK);
97 ReadCheck(fRing, FULL_PAYLOAD, sizeof(FULL_PAYLOAD));
98
99 CHK(fRing.SetSize(sizeof(SMALL_PAYLOAD)) == B_OK);
100 CHK(fRing.WriteExactly(SMALL_PAYLOAD, sizeof(SMALL_PAYLOAD)) == B_OK);
101 ReadCheck(fRing, SMALL_PAYLOAD, sizeof(SMALL_PAYLOAD));
102 }
103
104
105 void
InvalidResizeTest()106 MemoryRingIOTest::InvalidResizeTest()
107 {
108 CHK(fRing.SetSize(sizeof(FULL_PAYLOAD)) == B_OK);
109 CHK(fRing.WriteExactly(FULL_PAYLOAD, sizeof(FULL_PAYLOAD)) == B_OK);
110 CHK(fRing.SetSize(0) == B_BAD_VALUE);
111 }
112
113
114 void
TimeoutTest()115 MemoryRingIOTest::TimeoutTest()
116 {
117 fRing.Clear();
118 CHK(fRing.SetSize(0) == B_OK);
119 bigtime_t start = system_time();
120 const bigtime_t timeout = 100;
121
122 CHK(fRing.WaitForRead(timeout) == B_TIMED_OUT);
123 CHK(system_time() - start <= timeout + 10);
124
125 start = system_time();
126 CHK(fRing.WaitForWrite(timeout) == B_TIMED_OUT);
127 CHK(system_time() - start <= timeout + 10);
128 }
129
130
131 void
_DisableWriteOnFullBuffer()132 MemoryRingIOTest::_DisableWriteOnFullBuffer()
133 {
134 CHK(fRing.InitCheck() == B_OK);
135
136 while (fRing.SpaceAvailable() > 0)
137 fRing.WaitForRead();
138
139 /* snooze for sometime to ensure that the other thread entered
140 * WaitForWrite().
141 */
142 snooze(1000);
143 /* this should unblock the other thread */
144 fRing.SetWriteDisabled(true);
145 }
146
147
148 void
_DisableWriteOnEmptyBuffer()149 MemoryRingIOTest::_DisableWriteOnEmptyBuffer()
150 {
151 CHK(fRing.InitCheck() == B_OK);
152
153 while (fRing.BytesAvailable() > 0)
154 fRing.WaitForWrite();
155
156 /* snooze for sometime to ensure that the other thread entered
157 * WaitForRead().
158 */
159 snooze(1000);
160 /* this should unblock the other thread */
161 fRing.SetWriteDisabled(true);
162 }
163
164
165 /* static */ void
AddTests(BTestSuite & parent)166 MemoryRingIOTest::AddTests(BTestSuite& parent) {
167 CppUnit::TestSuite* suite = new CppUnit::TestSuite("MemoryRingIOTest");
168 BThreadedTestCaller<MemoryRingIOTest>* caller;
169
170 MemoryRingIOTest* big = new MemoryRingIOTest(sizeof(BIG_PAYLOAD));
171 caller = new BThreadedTestCaller<MemoryRingIOTest>(
172 "MemoryRingIOTest: RW threaded, big buffer", big);
173 caller->addThread("WR", &MemoryRingIOTest::WriteTest);
174 caller->addThread("RD", &MemoryRingIOTest::ReadTest);
175 suite->addTest(caller);
176
177 MemoryRingIOTest* full = new MemoryRingIOTest(sizeof(FULL_PAYLOAD));
178 caller = new BThreadedTestCaller<MemoryRingIOTest>(
179 "MemoryRingIOTest: RW threaded, medium buffer", full);
180 caller->addThread("WR", &MemoryRingIOTest::WriteTest);
181 caller->addThread("RD", &MemoryRingIOTest::ReadTest);
182 suite->addTest(caller);
183
184 MemoryRingIOTest* small = new MemoryRingIOTest(sizeof(SMALL_PAYLOAD));
185 caller = new BThreadedTestCaller<MemoryRingIOTest>(
186 "MemoryRingIOTest: RW threaded, small buffer", small);
187 caller->addThread("WR", &MemoryRingIOTest::WriteTest);
188 caller->addThread("RD", &MemoryRingIOTest::ReadTest);
189 suite->addTest(caller);
190
191 MemoryRingIOTest* endWrite = new MemoryRingIOTest(sizeof(FULL_PAYLOAD));
192 caller = new BThreadedTestCaller<MemoryRingIOTest>(
193 "MemoryRingIOTest: RW threaded, reader set end reached on writer wait",
194 endWrite);
195 caller->addThread("WR #1", &MemoryRingIOTest::BusyWriterTest);
196 caller->addThread("WR #2", &MemoryRingIOTest::BusyWriterTest);
197 caller->addThread("WR #3", &MemoryRingIOTest::BusyWriterTest);
198 caller->addThread("RD", &MemoryRingIOTest::_DisableWriteOnFullBuffer);
199 suite->addTest(caller);
200
201 MemoryRingIOTest* endRead = new MemoryRingIOTest(sizeof(FULL_PAYLOAD));
202 caller = new BThreadedTestCaller<MemoryRingIOTest>(
203 "MemoryRingIOTest: RW threaded, writer set end reached on reader wait",
204 endRead);
205 caller->addThread("RD #1", &MemoryRingIOTest::BusyReaderTest);
206 caller->addThread("RD #2", &MemoryRingIOTest::BusyReaderTest);
207 caller->addThread("RD #3", &MemoryRingIOTest::BusyReaderTest);
208 caller->addThread("WR", &MemoryRingIOTest::_DisableWriteOnEmptyBuffer);
209 suite->addTest(caller);
210
211 MemoryRingIOTest* single = new MemoryRingIOTest(0);
212 suite->addTest(new CppUnit::TestCaller<MemoryRingIOTest>(
213 "MemoryRingIOTest: RW single threaded with resizing",
214 &MemoryRingIOTest::ReadWriteSingleTest, single));
215 suite->addTest(new CppUnit::TestCaller<MemoryRingIOTest>(
216 "MemoryRingIOTest: Attempt to truncate buffer",
217 &MemoryRingIOTest::InvalidResizeTest, single));
218 suite->addTest(new CppUnit::TestCaller<MemoryRingIOTest>(
219 "MemoryRingIOTest: Wait timeout",
220 &MemoryRingIOTest::TimeoutTest, single));
221
222 parent.addTest("MemoryRingIOTest", suite);
223 }
224