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