test_translate_song.cxx 7.44 KB
Newer Older
1 2 3 4 5
/*
 * Unit tests for playlist_check_translate_song().
 */

#include "config.h"
6
#include "playlist/PlaylistSong.hxx"
7
#include "DetachedSong.hxx"
8 9
#include "SongLoader.hxx"
#include "client/Client.hxx"
10 11 12 13 14 15
#include "tag/TagBuilder.hxx"
#include "tag/Tag.hxx"
#include "util/Domain.hxx"
#include "fs/AllocatedPath.hxx"
#include "ls.hxx"
#include "Log.hxx"
Max Kellermann's avatar
Max Kellermann committed
16
#include "db/DatabaseSong.hxx"
17
#include "storage/plugins/LocalStorage.hxx"
18
#include "util/Error.hxx"
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
#include "Mapper.hxx"

#include <cppunit/TestFixture.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>

#include <string.h>
#include <stdio.h>

void
Log(const Domain &domain, gcc_unused LogLevel level, const char *msg)
{
	fprintf(stderr, "[%s] %s\n", domain.GetName(), msg);
}

bool
uri_supported_scheme(const char *uri)
{
	return memcmp(uri, "http://", 7) == 0;
}

41 42
static const char *const music_directory = "/music";
static Storage *storage;
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

static void
BuildTag(gcc_unused TagBuilder &tag)
{
}

template<typename... Args>
static void
BuildTag(TagBuilder &tag, TagType type, const char *value, Args&&... args)
{
	tag.AddItem(type, value);
	BuildTag(tag, std::forward<Args>(args)...);
}

template<typename... Args>
static Tag
MakeTag(Args&&... args)
{
	TagBuilder tag;
	BuildTag(tag, std::forward<Args>(args)...);
	return tag.Commit();
}

static Tag
MakeTag1a()
{
	return MakeTag(TAG_ARTIST, "artist_a1", TAG_TITLE, "title_a1",
		       TAG_ALBUM, "album_a1");
}

static Tag
MakeTag1b()
{
	return MakeTag(TAG_ARTIST, "artist_b1", TAG_TITLE, "title_b1",
		       TAG_COMMENT, "comment_b1");
}

static Tag
MakeTag1c()
{
	return MakeTag(TAG_ARTIST, "artist_b1", TAG_TITLE, "title_b1",
		       TAG_COMMENT, "comment_b1", TAG_ALBUM, "album_a1");
}

static Tag
MakeTag2a()
{
	return MakeTag(TAG_ARTIST, "artist_a2", TAG_TITLE, "title_a2",
		       TAG_ALBUM, "album_a2");
}

static Tag
MakeTag2b()
{
	return MakeTag(TAG_ARTIST, "artist_b2", TAG_TITLE, "title_b2",
		       TAG_COMMENT, "comment_b2");
}

static Tag
MakeTag2c()
{
	return MakeTag(TAG_ARTIST, "artist_b2", TAG_TITLE, "title_b2",
		       TAG_COMMENT, "comment_b2", TAG_ALBUM, "album_a2");
}

static const char *uri1 = "/foo/bar.ogg";
static const char *uri2 = "foo/bar.ogg";

DetachedSong *
112
DatabaseDetachSong(gcc_unused const Database &db,
113
		   gcc_unused const Storage &_storage,
114
		   const char *uri,
115
		   gcc_unused Error &error)
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
{
	if (strcmp(uri, uri2) == 0)
		return new DetachedSong(uri, MakeTag2a());

	return nullptr;
}

bool
DetachedSong::Update()
{
	if (strcmp(GetURI(), uri1) == 0) {
		SetTag(MakeTag1a());
		return true;
	}

	return false;
}

134 135 136 137 138 139
const Database *
Client::GetDatabase(gcc_unused Error &error) const
{
	return reinterpret_cast<const Database *>(this);
}

140 141 142
const Storage *
Client::GetStorage() const
{
143
	return ::storage;
144 145
}

146 147 148 149 150 151 152 153 154
bool
Client::AllowFile(gcc_unused Path path_fs, gcc_unused Error &error) const
{
	/* always return false, so a SongLoader with a non-nullptr
	   Client pointer will be regarded "insecure", while one with
	   client==nullptr will allow all files */
	return false;
}

155 156 157 158 159 160 161 162
static std::string
ToString(const Tag &tag)
{
	char buffer[64];
	sprintf(buffer, "%d", tag.time);

	std::string result = buffer;

163
	for (const auto &item : tag) {
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
		result.push_back('|');
		result.append(tag_item_names[item.type]);
		result.push_back('=');
		result.append(item.value);
	}

	return result;
}

static std::string
ToString(const DetachedSong &song)
{
	std::string result = song.GetURI();
	result.push_back('|');

	char buffer[64];

	if (song.GetLastModified() > 0) {
		sprintf(buffer, "%lu", (unsigned long)song.GetLastModified());
		result.append(buffer);
	}

	result.push_back('|');

	if (song.GetStartMS() > 0) {
		sprintf(buffer, "%u", song.GetStartMS());
		result.append(buffer);
	}

	result.push_back('-');

	if (song.GetEndMS() > 0) {
		sprintf(buffer, "%u", song.GetEndMS());
		result.append(buffer);
	}

	result.push_back('|');

	result.append(ToString(song.GetTag()));

	return result;
}

class TranslateSongTest : public CppUnit::TestFixture {
	CPPUNIT_TEST_SUITE(TranslateSongTest);
	CPPUNIT_TEST(TestAbsoluteURI);
	CPPUNIT_TEST(TestInsecure);
	CPPUNIT_TEST(TestSecure);
	CPPUNIT_TEST(TestInDatabase);
	CPPUNIT_TEST(TestRelative);
	CPPUNIT_TEST_SUITE_END();

	void TestAbsoluteURI() {
217 218
		DetachedSong song1("http://example.com/foo.ogg");
		auto se = ToString(song1);
219
		const SongLoader loader(nullptr, nullptr);
220 221
		CPPUNIT_ASSERT(playlist_check_translate_song(song1, "/ignored",
							     loader));
222
		CPPUNIT_ASSERT_EQUAL(se, ToString(song1));
223 224 225 226
	}

	void TestInsecure() {
		/* illegal because secure=false */
227
		DetachedSong song1 (uri1);
228
		const SongLoader loader(*reinterpret_cast<const Client *>(1));
229 230
		CPPUNIT_ASSERT(!playlist_check_translate_song(song1, nullptr,
							      loader));
231 232 233
	}

	void TestSecure() {
234
		DetachedSong song1(uri1, MakeTag1b());
235 236
		auto s1 = ToString(song1);
		auto se = ToString(DetachedSong(uri1, MakeTag1c()));
237

238
		const SongLoader loader(nullptr, nullptr);
239 240
		CPPUNIT_ASSERT(playlist_check_translate_song(song1, "/ignored",
							     loader));
241
		CPPUNIT_ASSERT_EQUAL(se, ToString(song1));
242 243 244
	}

	void TestInDatabase() {
245
		const SongLoader loader(reinterpret_cast<const Database *>(1),
246
					storage);
247

248
		DetachedSong song1("doesntexist");
249 250
		CPPUNIT_ASSERT(!playlist_check_translate_song(song1, nullptr,
							      loader));
251

252 253
		DetachedSong song2(uri2, MakeTag2b());
		auto s1 = ToString(song2);
254
		auto se = ToString(DetachedSong(uri2, MakeTag2c()));
255 256
		CPPUNIT_ASSERT(playlist_check_translate_song(song2, nullptr,
							     loader));
257 258
		CPPUNIT_ASSERT_EQUAL(se, ToString(song2));

259 260
		DetachedSong song3("/music/foo/bar.ogg", MakeTag2b());
		s1 = ToString(song3);
261
		se = ToString(DetachedSong(uri2, MakeTag2c()));
262 263
		CPPUNIT_ASSERT(playlist_check_translate_song(song3, nullptr,
							     loader));
264
		CPPUNIT_ASSERT_EQUAL(se, ToString(song3));
265 266 267
	}

	void TestRelative() {
268
		const Database &db = *reinterpret_cast<const Database *>(1);
269
		const SongLoader secure_loader(&db, storage);
270
		const SongLoader insecure_loader(*reinterpret_cast<const Client *>(1),
271
						 &db, storage);
272

273
		/* map to music_directory */
274
		DetachedSong song1("bar.ogg", MakeTag2b());
275 276
		auto s1 = ToString(song1);
		auto se = ToString(DetachedSong(uri2, MakeTag2c()));
277 278
		CPPUNIT_ASSERT(playlist_check_translate_song(song1, "/music/foo",
							     insecure_loader));
279
		CPPUNIT_ASSERT_EQUAL(se, ToString(song1));
280 281

		/* illegal because secure=false */
282
		DetachedSong song2("bar.ogg", MakeTag2b());
283 284
		CPPUNIT_ASSERT(!playlist_check_translate_song(song1, "/foo",
							      insecure_loader));
285 286

		/* legal because secure=true */
287 288
		DetachedSong song3("bar.ogg", MakeTag1b());
		s1 = ToString(song3);
289
		se = ToString(DetachedSong(uri1, MakeTag1c()));
290 291
		CPPUNIT_ASSERT(playlist_check_translate_song(song3, "/foo",
							     secure_loader));
292
		CPPUNIT_ASSERT_EQUAL(se, ToString(song3));
293 294

		/* relative to http:// */
295 296
		DetachedSong song4("bar.ogg", MakeTag2a());
		s1 = ToString(song4);
297
		se = ToString(DetachedSong("http://example.com/foo/bar.ogg", MakeTag2a()));
298 299
		CPPUNIT_ASSERT(playlist_check_translate_song(song4, "http://example.com/foo",
							     insecure_loader));
300
		CPPUNIT_ASSERT_EQUAL(se, ToString(song4));
301 302 303 304 305 306 307 308
	}
};

CPPUNIT_TEST_SUITE_REGISTRATION(TranslateSongTest);

int
main(gcc_unused int argc, gcc_unused char **argv)
{
309 310
	storage = CreateLocalStorage(Path::FromFS(music_directory));

311 312 313 314 315
	CppUnit::TextUi::TestRunner runner;
	auto &registry = CppUnit::TestFactoryRegistry::getRegistry();
	runner.addTest(registry.makeTest());
	return runner.run() ? EXIT_SUCCESS : EXIT_FAILURE;
}