#include #include #include #include #include #include #include #include #include "detail/playback_devices.hpp" #include "detail/metronome_player.hpp" BOOST_AUTO_TEST_CASE(sonus_updates_cannot_undo_concurrent_start_or_stop) { auto config = sonus::audio_config{}; config.no_device = true; auto created = sonus::engine::create(config); BOOST_REQUIRE(created); auto rendezvous = std::barrier{2}; auto failed = std::atomic{false}; auto updater = std::thread{[&] { for (auto index = 0; index < 1000; ++index) { for (auto phase = 0; phase < 2; ++phase) { rendezvous.arrive_and_wait(); if (!created->update_metronome({})) { failed.store(true); } rendezvous.arrive_and_wait(); } } }}; for (auto index = 0; index < 1000; ++index) { rendezvous.arrive_and_wait(); if (!created->start_metronome({})) { failed.store(true); } rendezvous.arrive_and_wait(); if (!created->metronome_active()) { failed.store(true); } rendezvous.arrive_and_wait(); created->stop_metronome(); rendezvous.arrive_and_wait(); if (created->metronome_active()) { failed.store(true); } } updater.join(); BOOST_TEST(!failed.load()); } BOOST_AUTO_TEST_CASE(sonus_device_selection_handles_empty_and_duplicate_devices) { auto devices = std::vector{ {.id = "0:1:speaker", .name = "speaker", .is_default = true}, {.id = "1:0:speaker", .name = "speaker", .is_default = false}, }; auto select = [&](auto id, auto name) { return sonus::detail::find_playback_device(devices, id, name); }; auto selected = select(devices[1].id, "speaker"); BOOST_REQUIRE(selected); BOOST_TEST(*selected == 1U); selected = select("stale-id", "speaker"); BOOST_REQUIRE(selected); BOOST_TEST(*selected == 0U); selected = select("", "speaker"); BOOST_REQUIRE(selected); BOOST_TEST(*selected == 0U); BOOST_TEST(!select("stale-id", "")); BOOST_TEST(!select("", "")); BOOST_TEST(!select("", "missing")); devices.clear(); BOOST_TEST(!select("0:1:speaker", "speaker")); } BOOST_AUTO_TEST_CASE(sonus_metronome_player_publication_overlaps_audio_rendering) { auto player = sonus::detail::metronome_player{}; auto done = std::atomic_bool{false}; auto valid = std::atomic_bool{true}; auto reader = std::thread{[&] { auto samples = std::vector(128U); while (!done.load()) { std::fill(samples.begin(), samples.end(), 0.0F); player.render(samples.data(), 64U, 2U); for (auto sample : samples) { if (!std::isfinite(sample) || sample < -1.0F || sample > 1.0F) { valid.store(false); } } } }}; for (auto index = std::uint32_t{}; index < 1000U; ++index) { player.configure({.bpm = 60U + index % 180U}, 48000U, true); player.stop(); } done.store(true); reader.join(); player.collect(); BOOST_TEST(valid.load()); BOOST_TEST(!player.active()); auto silence = std::vector(128U); player.render(silence.data(), 64U, 2U); BOOST_TEST(std::all_of(silence.begin(), silence.end(), [](auto sample) { return sample == 0.0F; })); }