/* Целые кадры, переполнение, ошибочные аргументы и конкурентные producers/consumer. */ #include "../src/audio_frame_ring.h" #include #include #include #include class AudioFrameRingTest : public QObject { Q_OBJECT private slots: void initTestCase() { debug_config_init(); debug_enable_console(1); debug_set_level(DEBUG_LEVEL_WARN); } void invalidGeometry() { for (int capacity : {-1, 0, 1}) for (int slot : {-1, 0, 1}) { if (capacity > 0 && slot > 0) continue; QVERIFY_EXCEPTION_THROWN((void)AudioFrameRing(capacity, slot), std::invalid_argument); } } void overflowDropsOnlyOldest() { AudioFrameRing ring(2, 1); for (uint8_t value : {1, 2, 3}) QVERIFY(ring.push(&value, 1)); QCOMPARE(ring.size(), 2); QCOMPARE(ring.capacity(), 2); uint8_t value = 0; QCOMPARE(ring.pop(&value, 1), 1); QCOMPARE(value, uint8_t(2)); QCOMPARE(ring.pop(&value, 1), 1); QCOMPARE(value, uint8_t(3)); QCOMPARE(ring.pop(nullptr, 0), 0); } void rejectsArgumentsWithoutConsumingFrame() { AudioFrameRing ring(3, 4); uint8_t data[] = {1, 2, 3, 4}; QVERIFY(ring.push(data, 4)); QVERIFY(!ring.push(nullptr, 1)); QVERIFY(!ring.push(data, 0)); QVERIFY(!ring.push(data, -1)); QVERIFY(!ring.push(data, 5)); QCOMPARE(ring.pop(nullptr, 4), -1); uint8_t output[] = {9, 9, 9, 9}; QCOMPARE(ring.pop(output, 3), -1); QCOMPARE(output[0], uint8_t(9)); QCOMPARE(ring.size(), 1); QCOMPARE(ring.pop(output, 4), 4); QCOMPARE(memcmp(output, data, 4), 0); } void preservesLengthBeyondUint16() { AudioFrameRing ring(2, 70000); const std::vector data(65536, 0x5a); std::vector output(data.size() + 1, 0xa5); QVERIFY(ring.push(data.data(), int(data.size()))); QCOMPARE(ring.pop(output.data(), int(data.size())), int(data.size())); QCOMPARE(memcmp(data.data(), output.data(), data.size()), 0); QCOMPARE(output.back(), uint8_t(0xa5)); } void clearAfterWrappedQueue() { AudioFrameRing ring(3, 1); uint8_t value = 7; for (int i = 0; i < 8; ++i) QVERIFY(ring.push(&value, 1)); ring.clear(); QCOMPARE(ring.size(), 0); QCOMPARE(ring.pop(&value, 1), 0); value = 9; QVERIFY(ring.push(&value, 1)); value = 0; QCOMPARE(ring.pop(&value, 1), 1); QCOMPARE(value, uint8_t(9)); } void concurrentFrameIntegrity() { constexpr int producers = 4, perProducer = 1000, total = producers * perProducer; AudioFrameRing ring(total, sizeof(uint32_t)); std::atomic done{0}; std::atomic failed{false}; std::vector threads; const auto join = qScopeGuard([&] { for (auto& thread : threads) if (thread.joinable()) thread.join(); }); for (int producer = 0; producer < producers; ++producer) threads.emplace_back([&, producer] { for (uint32_t i = 0; i < perProducer; ++i) { const uint32_t value = producer * perProducer + i; if (!ring.push(reinterpret_cast(&value), sizeof(value))) failed = true; } ++done; }); std::vector seen(total, false); int received = 0; while (done.load() < producers || ring.size()) { uint32_t value = 0; const int length = ring.pop(reinterpret_cast(&value), sizeof(value)); if (!length) { std::this_thread::yield(); continue; } QCOMPARE(length, int(sizeof(value))); QVERIFY(value < total); QVERIFY(!seen[value]); seen[value] = true; ++received; } QVERIFY(!failed.load()); QCOMPARE(received, total); } }; QTEST_GUILESS_MAIN(AudioFrameRingTest) #include "test_audio_frame_ring.moc"