test_translate_song.cxx 7.5 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 163 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 217
static std::string
ToString(const Tag &tag)
{
	char buffer[64];
	sprintf(buffer, "%d", tag.time);

	std::string result = buffer;

	for (unsigned i = 0, n = tag.num_items; i != n; ++i) {
		const TagItem &item = *tag.items[i];
		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() {
218 219
		DetachedSong song1("http://example.com/foo.ogg");
		auto se = ToString(song1);
220
		const SongLoader loader(nullptr, nullptr);
221 222
		CPPUNIT_ASSERT(playlist_check_translate_song(song1, "/ignored",
							     loader));
223
		CPPUNIT_ASSERT_EQUAL(se, ToString(song1));
224 225 226 227
	}

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

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

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

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

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

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

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

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

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

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

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

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

CPPUNIT_TEST_SUITE_REGISTRATION(TranslateSongTest);

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

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