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
30static void
31ReadCheck(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
42void
43MemoryRingIOTest::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
53void
54MemoryRingIOTest::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
64void
65MemoryRingIOTest::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
75void
76MemoryRingIOTest::BusyReaderTest()
77{
78	CHK(fRing.InitCheck() == B_OK);
79
80	char buffer[100];
81	CHK(fRing.Read(buffer, sizeof(buffer)) == 0);
82}
83
84
85void
86MemoryRingIOTest::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
105void
106MemoryRingIOTest::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
114void
115MemoryRingIOTest::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
131void
132MemoryRingIOTest::_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
148void
149MemoryRingIOTest::_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
166MemoryRingIOTest::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