Skip to content

Commit 50c73d0

Browse files
Zaggy1024ADKaster
authored andcommitted
LibAudio: Add a test for creating and destructing a PlaybackStream
This will ensure that we don't leak any memory while playing back audio. There is an expectation value in the test that is only set to true when PulseAudio is present for the moment. When any new implementation is added for other libraries/platforms, we should hopefully get a CI failure due to unexpected success in creating the `PlaybackStream`. To ensure that we clean up our PulseAudio connection whenever audio output is not needed, add `PulseAudioContext::weak_instance()` to allow us to check whether an instance exists without creating one.
1 parent 515b255 commit 50c73d0

File tree

5 files changed

+58
-1
lines changed

5 files changed

+58
-1
lines changed

Meta/Lagom/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,11 @@ if (BUILD_LAGOM)
661661
# The FLAC tests need a special working directory to find the test files
662662
lagom_test(../../Tests/LibAudio/TestFLACSpec.cpp LIBS LibAudio WORKING_DIRECTORY "${FLAC_TEST_PATH}/..")
663663

664+
lagom_test(../../Tests/LibAudio/TestPlaybackStream.cpp LIBS LibAudio)
665+
if (HAVE_PULSEAUDIO)
666+
target_compile_definitions(TestPlaybackStream PRIVATE HAVE_PULSEAUDIO=1)
667+
endif()
668+
664669
# LibCore
665670
if ((LINUX OR APPLE) AND NOT EMSCRIPTEN)
666671
lagom_test(../../Tests/LibCore/TestLibCoreFileWatcher.cpp)

Tests/LibAudio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
set(TEST_SOURCES
22
TestFLACSpec.cpp
3+
TestPlaybackStream.cpp
34
)
45

56
foreach(source IN LISTS TEST_SOURCES)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include <AK/Math.h>
8+
#include <AK/MemoryStream.h>
9+
#include <AK/WeakPtr.h>
10+
#include <LibAudio/PlaybackStream.h>
11+
#include <LibTest/TestSuite.h>
12+
#include <unistd.h>
13+
14+
#if defined(HAVE_PULSEAUDIO)
15+
# include <LibAudio/PulseAudioWrappers.h>
16+
#endif
17+
18+
TEST_CASE(create_and_destroy_playback_stream)
19+
{
20+
bool has_implementation = false;
21+
#if defined(HAVE_PULSEAUDIO)
22+
has_implementation = true;
23+
#endif
24+
25+
{
26+
auto stream_result = Audio::PlaybackStream::create(Audio::OutputState::Playing, 44100, 2, 100, [](Bytes buffer, Audio::PcmSampleFormat format, size_t sample_count) -> ReadonlyBytes {
27+
VERIFY(format == Audio::PcmSampleFormat::Float32);
28+
FixedMemoryStream writing_stream { buffer };
29+
30+
for (size_t i = 0; i < sample_count; i++) {
31+
MUST(writing_stream.write_value(0.0f));
32+
MUST(writing_stream.write_value(0.0f));
33+
}
34+
35+
return buffer.trim(writing_stream.offset());
36+
});
37+
EXPECT_EQ(!stream_result.is_error(), has_implementation);
38+
usleep(10000);
39+
}
40+
41+
#if defined(HAVE_PULSEAUDIO)
42+
VERIFY(!Audio::PulseAudioContext::weak_instance());
43+
#endif
44+
}

Userland/Libraries/LibAudio/PulseAudioWrappers.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,15 @@
1111

1212
namespace Audio {
1313

14-
ErrorOr<NonnullRefPtr<PulseAudioContext>> PulseAudioContext::instance()
14+
WeakPtr<PulseAudioContext> PulseAudioContext::weak_instance()
1515
{
1616
// Use a weak pointer to allow the context to be shut down if we stop outputting audio.
1717
static WeakPtr<PulseAudioContext> the_instance;
18+
return the_instance;
19+
}
20+
21+
ErrorOr<NonnullRefPtr<PulseAudioContext>> PulseAudioContext::instance()
22+
{
1823
static Threading::Mutex instantiation_mutex;
1924
// Lock and unlock the mutex to ensure that the mutex is fully unlocked at application
2025
// exit.
@@ -25,6 +30,7 @@ ErrorOr<NonnullRefPtr<PulseAudioContext>> PulseAudioContext::instance()
2530

2631
auto instantiation_locker = Threading::MutexLocker(instantiation_mutex);
2732

33+
auto the_instance = weak_instance();
2834
RefPtr<PulseAudioContext> strong_instance_pointer = the_instance.strong_ref();
2935

3036
if (strong_instance_pointer == nullptr) {

Userland/Libraries/LibAudio/PulseAudioWrappers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class PulseAudioContext
4040
: public AtomicRefCounted<PulseAudioContext>
4141
, public Weakable<PulseAudioContext> {
4242
public:
43+
static AK::WeakPtr<PulseAudioContext> weak_instance();
4344
static ErrorOr<NonnullRefPtr<PulseAudioContext>> instance();
4445

4546
explicit PulseAudioContext(pa_threaded_mainloop*, pa_mainloop_api*, pa_context*);

0 commit comments

Comments
 (0)