Skip to content

Commit 33d63a1

Browse files
committed
Improve audio file IO tests by removing time-dependent sleeps
- Use std::condition_variable in MockAudioFileIO for deterministic synchronization - Replace fixed msleep calls with wait-on-event logic - Improve test reliability and execution speed
1 parent 319b4cb commit 33d63a1

1 file changed

Lines changed: 56 additions & 13 deletions

File tree

src/unit_tests/audio_file_io_test/audio_file_io_test.cpp

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <QtTest>
2222
#include <mutex>
23+
#include <condition_variable>
2324

2425
namespace noteahead {
2526

@@ -28,6 +29,7 @@ class MockAudioFileIO : public AudioFileReader
2829
public:
2930
bool open(const std::string &, Mode mode, Info & info) override
3031
{
32+
std::lock_guard<std::mutex> lock(m_mutex);
3133
m_mode = mode;
3234
if (mode == Mode::Read) {
3335
info = m_info;
@@ -37,10 +39,16 @@ class MockAudioFileIO : public AudioFileReader
3739
}
3840
m_isOpen = true;
3941
m_pos = 0;
42+
m_cv.notify_all();
4043
return true;
4144
}
4245

43-
void close() override { m_isOpen = false; }
46+
void close() override
47+
{
48+
std::lock_guard<std::mutex> lock(m_mutex);
49+
m_isOpen = false;
50+
m_cv.notify_all();
51+
}
4452

4553
int64_t readFloat(std::span<float>) override { return 0; }
4654
int64_t readDouble(std::span<double>) override { return 0; }
@@ -53,6 +61,8 @@ class MockAudioFileIO : public AudioFileReader
5361
if (toRead == 0) return 0;
5462
std::copy(m_data.begin() + m_pos, m_data.begin() + m_pos + toRead, data.begin());
5563
m_pos += toRead;
64+
m_readCount++;
65+
m_cv.notify_all();
5666
return static_cast<int64_t>(toRead / static_cast<size_t>(m_info.channels));
5767
}
5868

@@ -64,13 +74,17 @@ class MockAudioFileIO : public AudioFileReader
6474
if (!m_isOpen || m_mode != Mode::Write) return 0;
6575
m_data.insert(m_data.end(), data.begin(), data.end());
6676
m_info.frames += static_cast<int64_t>(data.size() / static_cast<size_t>(m_info.channels));
77+
m_writeCount++;
78+
m_cv.notify_all();
6779
return static_cast<int64_t>(data.size() / static_cast<size_t>(m_info.channels));
6880
}
6981

7082
bool seek(int64_t frames, int) override
7183
{
7284
std::lock_guard<std::mutex> lock(m_mutex);
7385
m_pos = static_cast<size_t>(frames * m_info.channels);
86+
m_seekCount++;
87+
m_cv.notify_all();
7488
return true;
7589
}
7690

@@ -80,18 +94,46 @@ class MockAudioFileIO : public AudioFileReader
8094
// Helper for testing
8195
void setData(const std::vector<int32_t> & data, const Info & info)
8296
{
97+
std::lock_guard<std::mutex> lock(m_mutex);
8398
m_data = data;
8499
m_info = info;
85100
}
86-
const std::vector<int32_t> & data() const { return m_data; }
101+
102+
const std::vector<int32_t> & data() const
103+
{
104+
std::lock_guard<std::mutex> lock(m_mutex);
105+
return m_data;
106+
}
107+
108+
bool waitForWriteSize(size_t expectedSize, std::chrono::milliseconds timeout)
109+
{
110+
std::unique_lock<std::mutex> lock(m_mutex);
111+
return m_cv.wait_for(lock, timeout, [this, expectedSize] { return m_data.size() >= expectedSize; });
112+
}
113+
114+
bool waitForReadCount(int expectedCount, std::chrono::milliseconds timeout)
115+
{
116+
std::unique_lock<std::mutex> lock(m_mutex);
117+
return m_cv.wait_for(lock, timeout, [this, expectedCount] { return m_readCount >= expectedCount; });
118+
}
119+
120+
bool waitForSeekCount(int expectedCount, std::chrono::milliseconds timeout)
121+
{
122+
std::unique_lock<std::mutex> lock(m_mutex);
123+
return m_cv.wait_for(lock, timeout, [this, expectedCount] { return m_seekCount >= expectedCount; });
124+
}
87125

88126
private:
89127
std::vector<int32_t> m_data;
90128
Info m_info;
91129
bool m_isOpen = false;
92130
Mode m_mode = Mode::Read;
93131
size_t m_pos = 0;
132+
int m_readCount = 0;
133+
int m_writeCount = 0;
134+
int m_seekCount = 0;
94135
mutable std::mutex m_mutex;
136+
std::condition_variable m_cv;
95137
};
96138

97139
void AudioFileIoTest::testRecordingAndStreaming()
@@ -114,15 +156,10 @@ void AudioFileIoTest::testRecordingAndStreaming()
114156
testData[i] = static_cast<int32_t>(i);
115157
}
116158

117-
// Push in smaller chunks to ensure ring buffer doesn't overflow
118159
QVERIFY(recorder.push(testData.data(), testData.size()));
119160

120161
// Wait for background thread to process the ring buffer
121-
int attempts = 0;
122-
while (mockWriterPtr->data().size() < 1000 && attempts < 100) {
123-
QThread::msleep(10);
124-
attempts++;
125-
}
162+
QVERIFY(mockWriterPtr->waitForWriteSize(1000, std::chrono::milliseconds(1000)));
126163

127164
recorder.stop();
128165
recordedData = mockWriterPtr->data();
@@ -132,6 +169,7 @@ void AudioFileIoTest::testRecordingAndStreaming()
132169

133170
// 2. Stream it back and verify using Mock
134171
auto mockReader = std::make_unique<MockAudioFileIO>();
172+
auto mockReaderPtr = mockReader.get();
135173
AudioFileReader::Info info;
136174
info.samplerate = sampleRate;
137175
info.channels = channels;
@@ -145,16 +183,17 @@ void AudioFileIoTest::testRecordingAndStreaming()
145183
QCOMPARE(streamer.sampleRate(), static_cast<int>(sampleRate));
146184
QCOMPARE(streamer.channels(), static_cast<int>(channels));
147185

148-
// Wait for disk read thread to fill ring buffer
149-
QThread::msleep(100);
186+
// Wait for disk read thread to fill ring buffer (at least one read operation)
187+
QVERIFY(mockReaderPtr->waitForReadCount(1, std::chrono::milliseconds(1000)));
150188

151189
std::vector<int32_t> readData(1000);
152190
size_t totalRead = 0;
153191
int attempts = 0;
154192
while (totalRead < 1000 && attempts < 100) {
155193
size_t read = streamer.pop(readData.data() + totalRead, readData.size() - totalRead);
156194
if (read == 0) {
157-
QThread::msleep(10);
195+
// If the ring buffer was empty, we might need to wait for another read from mock
196+
mockReaderPtr->waitForReadCount(attempts + 2, std::chrono::milliseconds(100));
158197
attempts++;
159198
continue;
160199
}
@@ -176,6 +215,7 @@ void AudioFileIoTest::testPosition()
176215
const size_t bufferSize = 4096;
177216

178217
auto mockReader = std::make_unique<MockAudioFileIO>();
218+
auto mockReaderPtr = mockReader.get();
179219
AudioFileReader::Info info;
180220
info.samplerate = sampleRate;
181221
info.channels = channels;
@@ -186,13 +226,16 @@ void AudioFileIoTest::testPosition()
186226
{
187227
AudioFileStreamer streamer(std::move(mockReader));
188228
streamer.start("dummy", bufferSize);
189-
QThread::msleep(100);
229+
230+
// Wait for initial fill
231+
QVERIFY(mockReaderPtr->waitForReadCount(1, std::chrono::milliseconds(1000)));
190232

191233
streamer.setPosition(0.5);
192234
QCOMPARE(streamer.position(), 0.5);
235+
QVERIFY(mockReaderPtr->waitForSeekCount(1, std::chrono::milliseconds(1000)));
193236

194237
// Wait for background thread to fill ring buffer from new position
195-
QThread::msleep(100);
238+
QVERIFY(mockReaderPtr->waitForReadCount(2, std::chrono::milliseconds(1000)));
196239

197240
std::vector<int32_t> dummy(100);
198241
size_t read = streamer.pop(dummy.data(), dummy.size());

0 commit comments

Comments
 (0)