xref: /haiku/src/tests/kits/shared/MemoryRingIOTest.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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
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
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
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
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
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
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
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
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
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
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
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