From 898e0a2bc44e4b488f4c8ccaeef18d7e89a6869b Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 10 Mar 2025 14:13:52 +0100 Subject: [PATCH] test/playlist: unit tests for the playlist plugins Only "pls" for now. --- src/io/StringOutputStream.hxx | 27 ++++++++ test/meson.build | 1 + test/playlist/PlaylistUtil.cxx | 39 +++++++++++ test/playlist/PlaylistUtil.hxx | 15 +++++ test/playlist/TestPlsPlaylistPlugin.cxx | 88 +++++++++++++++++++++++++ test/playlist/meson.build | 19 ++++++ 6 files changed, 189 insertions(+) create mode 100644 src/io/StringOutputStream.hxx create mode 100644 test/playlist/PlaylistUtil.cxx create mode 100644 test/playlist/PlaylistUtil.hxx create mode 100644 test/playlist/TestPlsPlaylistPlugin.cxx create mode 100644 test/playlist/meson.build diff --git a/src/io/StringOutputStream.hxx b/src/io/StringOutputStream.hxx new file mode 100644 index 000000000..51a7e875b --- /dev/null +++ b/src/io/StringOutputStream.hxx @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BSD-2-Clause +// author: Max Kellermann + +#pragma once + +#include "OutputStream.hxx" +#include "util/SpanCast.hxx" + +#include + +class StringOutputStream final : public OutputStream { + std::string value; + +public: + const std::string &GetValue() const & noexcept { + return value; + } + + std::string &&GetValue() && noexcept { + return std::move(value); + } + + /* virtual methods from class OutputStream */ + void Write(std::span src) override { + value.append(ToStringView(src)); + } +}; diff --git a/test/meson.build b/test/meson.build index ba91336d9..a67b97301 100644 --- a/test/meson.build +++ b/test/meson.build @@ -6,6 +6,7 @@ subdir('util') subdir('net') subdir('time') subdir('tag') +subdir('playlist') executable( 'read_conf', diff --git a/test/playlist/PlaylistUtil.cxx b/test/playlist/PlaylistUtil.cxx new file mode 100644 index 000000000..38f088f26 --- /dev/null +++ b/test/playlist/PlaylistUtil.cxx @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright The Music Player Daemon Project + +#include "PlaylistUtil.hxx" +#include "playlist/PlaylistRegistry.hxx" +#include "playlist/SongEnumerator.hxx" +#include "song/DetachedSong.hxx" +#include "input/MemoryInputStream.hxx" +#include "input/Ptr.hxx" +#include "thread/Mutex.hxx" +#include "io/BufferedOutputStream.hxx" +#include "io/StringOutputStream.hxx" +#include "util/SpanCast.hxx" +#include "SongSave.hxx" + +std::unique_ptr +ParsePlaylist(const char *uri, std::string_view contents) +{ + Mutex mutex; + InputStreamPtr input{new MemoryInputStream{uri, mutex, AsBytes(contents)}}; + return playlist_list_open_stream(std::move(input), uri); +} + +std::string +ToString(SongEnumerator &e) +{ + StringOutputStream sos; + + WithBufferedOutputStream(sos, [&e](auto &bos){ + while (const auto song = e.NextSong()) { + bos.Write('\n'); + song_save(bos, *song); + } + + bos.Write('\n'); + }); + + return std::move(sos).GetValue(); +} diff --git a/test/playlist/PlaylistUtil.hxx b/test/playlist/PlaylistUtil.hxx new file mode 100644 index 000000000..39cc23ff2 --- /dev/null +++ b/test/playlist/PlaylistUtil.hxx @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright The Music Player Daemon Project + +#pragma once + +#include +#include + +class SongEnumerator; + +std::unique_ptr +ParsePlaylist(const char *uri, std::string_view contents); + +std::string +ToString(SongEnumerator &e); diff --git a/test/playlist/TestPlsPlaylistPlugin.cxx b/test/playlist/TestPlsPlaylistPlugin.cxx new file mode 100644 index 000000000..dfc874e0e --- /dev/null +++ b/test/playlist/TestPlsPlaylistPlugin.cxx @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright The Music Player Daemon Project + +#include "PlaylistUtil.hxx" +#include "playlist/PlaylistRegistry.hxx" +#include "playlist/SongEnumerator.hxx" +#include "config/Data.hxx" + +#include + +using std::string_view_literals::operator""sv; + +// from https://en.wikipedia.org/wiki/PLS_(file_format) +static constexpr auto pls1 = R"( +[playlist] +File1=https://e20.yesstreaming.net:8279/ +Title1=Here enter name of the station +NumberOfEntries=1 +)"sv; + +static constexpr auto expect1 = R"( +song_begin: https://e20.yesstreaming.net:8279/ +Title: Here enter name of the station +song_end + +)"sv; + +// from https://en.wikipedia.org/wiki/PLS_(file_format) +static constexpr auto pls2 = R"pls( +[playlist] + +File1=https://e20.yesstreaming.net:8279/ +Length1=-1 + +File2=example2.mp3 +Title2=Just some local audio that is 2mins long +Length2=120 + +File3=F:\Music\whatever.m4a +Title3=absolute path on Windows + +File4=%UserProfile%\Music\short.ogg +Title4=example for an Environment variable +Length4=5 + +NumberOfEntries=4 +Version=2 +)pls"sv; + +static constexpr auto expect2 = R"( +song_begin: https://e20.yesstreaming.net:8279/ +song_end + +song_begin: example2.mp3 +Time: 120 +Title: Just some local audio that is 2mins long +song_end + +song_begin: F:\Music\whatever.m4a +Title: absolute path on Windows +song_end + +song_begin: %UserProfile%\Music\short.ogg +Time: 5 +Title: example for an Environment variable +song_end + +)"sv; + +TEST(PlaylistPlugins, Pls) +{ + const ConfigData config; + ScopePlaylistPluginsInit playlist_plugins_init{config}; + + const char *uri = "dummy.pls"; + + { + const auto p = ParsePlaylist(uri, pls1); + ASSERT_TRUE(p); + EXPECT_EQ(ToString(*p), expect1); + } + + { + const auto p = ParsePlaylist(uri, pls2); + ASSERT_TRUE(p); + EXPECT_EQ(ToString(*p), expect2); + } +} diff --git a/test/playlist/meson.build b/test/playlist/meson.build new file mode 100644 index 000000000..98c110f56 --- /dev/null +++ b/test/playlist/meson.build @@ -0,0 +1,19 @@ +test( + 'TestPlaylistPlugins', + executable( + 'TestPlaylistPlugins', + 'PlaylistUtil.cxx', + 'TestPlsPlaylistPlugin.cxx', + '../../src/SongSave.cxx', + '../../src/TagSave.cxx', + '../../src/TagFile.cxx', + include_directories: inc, + dependencies: [ + gtest_dep, + playlist_glue_dep, + archive_glue_dep, + input_glue_dep, + decoder_glue_dep, + ], + ), +)