/* * Copyright 2014 (c) Anna Schumaker. */ #include extern "C" { #include #include #include } #include "test.h" unsigned int count_added = 0; unsigned int count_deleted = 0; unsigned int count_updated = 0; static class TestNotifier : public QNotifier { public: TestNotifier() : QNotifier() {} void on_track_added(unsigned int i) { count_added++; } void on_track_removed(unsigned int i) { count_deleted++; } void on_track_updated(unsigned int i) { count_updated++; } } test_notifier; class TestQueue : public Queue { public: TestQueue() : Queue() { set_notifier(&test_notifier); } TestQueue(unsigned int f) : Queue(f) { set_notifier(&test_notifier); } ~TestQueue() {} unsigned int get_cur() { return _cur; } unsigned int get_flags() { return _flags; } QNotifier *get_notify() { return _notify; } std::vector get_sorder() { return _sort_order; }; }; static void __test_init_core() { struct library *library; filter_init(); tags_init(); library = library_find("tests/Music"); track_add(library, "tests/Music/Hyrule Symphony/01 - Title Theme.ogg"); track_add(library, "tests/Music/Hyrule Symphony/02 - Kokiri Forest.ogg"); track_add(library, "tests/Music/Hyrule Symphony/03 - Hyrule Field.ogg"); track_add(library, "tests/Music/Hyrule Symphony/04 - Hyrule Castle.ogg"); track_add(library, "tests/Music/Hyrule Symphony/05 - Lon Lon Ranch.ogg"); track_add(library, "tests/Music/Hyrule Symphony/06 - Kakariko Village.ogg"); track_add(library, "tests/Music/Hyrule Symphony/07 - Death Mountain.ogg"); track_add(library, "tests/Music/Hyrule Symphony/08 - Zora's Domain.ogg"); track_add(library, "tests/Music/Hyrule Symphony/09 - Gerudo Valley.ogg"); track_add(library, "tests/Music/Hyrule Symphony/10 - Ganondorf.ogg"); track_add(library, "tests/Music/Hyrule Symphony/11 - Princess Zelda.ogg"); track_add(library, "tests/Music/Hyrule Symphony/12 - Ocarina Medley.ogg"); track_add(library, "tests/Music/Hyrule Symphony/13 - The Legend of Zelda Medley.ogg"); } static void __test_deinit_core() { tags_deinit(); filter_deinit(); } static void test_init() { TestQueue q; unsigned int flags = Q_ENABLED | Q_RANDOM; test_equal(q.get_cur(), (unsigned int)-1); test_equal(q.get_flags(), 0); test_equal(q.length(), 0); test_equal(q.get_sorder().size(), (size_t)0); test_equal(q.next(), (struct track *)NULL); test_equal(q.get_notify(), &test_notifier); q = TestQueue(flags); test_equal(q.get_cur(), (unsigned int )-1); test_equal(q.get_flags(), flags); test_equal(q.length(), 0); test_equal(q.get_sorder().size(), 0); test_equal(q.next(), (struct track *)NULL); test_equal(q.get_notify(), &test_notifier); } static void test_flags() { TestQueue q(0); test_equal(q.get_flags(), 0); test_equal(q.has_flag(Q_ENABLED), false); test_equal(q.has_flag(Q_RANDOM), false); test_equal(q.has_flag(Q_REPEAT), false); test_equal(q.has_flag(Q_NO_SORT), false); q.set_flag(Q_ENABLED); test_equal(q.get_flags(), Q_ENABLED); q.unset_flag(Q_ENABLED); test_equal(q.get_flags(), 0); q.set_flag(Q_ENABLED); q.set_flag(Q_RANDOM); q.set_flag(Q_REPEAT); q.set_flag(Q_NO_SORT); test_equal(q.has_flag(Q_ENABLED), true); test_equal(q.has_flag(Q_RANDOM), true); test_equal(q.has_flag(Q_REPEAT), true); test_equal(q.has_flag(Q_NO_SORT), true); } static void test_stress(unsigned int N) { unsigned int ex_length = 0; unsigned int ex_size = N; struct track *track; unsigned int i; TestQueue q(0); count_added = 0; count_deleted = 0; count_updated = 0; __test_init_core(); /* Queue :: add() */ for (i = 0; i < N; i++) { track = track_get(i % 13); ex_length += track->tr_length; test_loop_equal(q.add(track), i, i); test_loop_equal(count_added, i + 1, i); } test_loop_passed(); test_equal(q.length(), ex_length); test_equal(q.size(), ex_size); /* Queue :: del(struct track *) */ track = track_get(0); ex_length -= track->tr_length * (N / 13); ex_size -= (N / 13); q.del(track); test_equal(q.length(), ex_length); test_equal(q.size(), ex_size); /* Queue :: del(unsigned int) */ track = track_get(1); ex_length -= track->tr_length * (N / 13); ex_size -= (N / 13); for (i = 0; i < ex_size; i += 11) { test_loop_equal(q[i], track, i); q.del(i); } test_loop_passed(); test_equal(q.length(), ex_length); test_equal(q.size(), ex_size); /* Queue :: updated(struct track *) */ track = track_get(2); q.updated(track); test_equal(count_updated, N / 13); test_equal(q.next(), NULL); test_equal(q.size(), ex_size); /* Tracks should not be removed. */ q.set_flag(Q_ENABLED); q.set_flag(Q_REPEAT); for (i = 0; i < ex_size; i++) { test_loop_equal(q.next(), track_get((i % 11) + 2), i); q.track_selected(i); test_loop_equal(q.size(), ex_size, i); } test_loop_passed(); /* Tracks should be removed. */ q.unset_flag(Q_REPEAT); for (i = 0; i < ex_size; i++) { test_loop_equal(q.next(), track_get((i % 11) + 2), i); test_loop_equal(q.size(), ex_size - (i + 1), i); } test_loop_passed(); test_equal(q.size(), 0); test_equal(q.length(), 0); } static void test_basics() { test_stress(13); } static void test_stress_0() { test_stress(0); } static void test_stress_100K() { test_stress(100009); } static void test_rand_select() { TestQueue q(Q_ENABLED | Q_RANDOM); unsigned int i; random_seed(0); /* Call next() on an empty queue. */ for (i = 0; i < 13; i++) { test_loop_equal(q.next(), NULL, i); test_loop_equal(q.size(), 0, i); } test_loop_passed(); for (i = 0; i < 13; i++) q.add(track_get(i)); /* * The comments below use the following notation: * : The value pointed to by q._cur. * (val): The value selected by q.track_selected(). * [val]: The value picked by q.next(). */ /* rand() = 2, q = { <>, 0, [1], 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 } */ test_equal(q.next()->tr_dbe.dbe_index, 1); /* select = 6, q = { <0>, 2, 3, 4, 5, 6, (7), 8, 9, 10, 11, 12 } */ q.track_selected(6); test_equal(q.size(), 11); /* rand() = 3, q = { 0, 2, 3, 4, 5, <6>, 8, 9, [10], 11, 12 } */ test_equal(q.next()->tr_dbe.dbe_index, 10); /* select = 7, q = { 0, 2, 3, 4, 5, 6, 8, (<9>), 11, 12 } */ q.track_selected(7); test_equal(q.size(), 9); /* rand() = 4, q = { 0, 2, 3, 4, 5, 6, <8>, [11], 12 } */ test_equal(q.next()->tr_dbe.dbe_index, 11); /* select = 2, q = { 0, 2, (3), 4, 5, 6, <8>, 12 } */ q.track_selected(2); test_equal(q.size(), 7); /* rand() = 1, q = { 0, <2>, [4], 5, 6, 8, 12 } */ test_equal(q.next()->tr_dbe.dbe_index, 4); /* select = 1, q = { 0, <2>, 5, 6, 8, (12) } */ q.track_selected(5); test_equal(q.size(), 5); /* rand() = 1, q = { [0], 2, 5, 6, <8>, } */ test_equal(q.next()->tr_dbe.dbe_index, 0); /* rand() = 1, q = { <>, [2], 5, 6, 8, } */ test_equal(q.next()->tr_dbe.dbe_index, 2); /* select = 1, q = { <>, 5, (6), 8, } */ q.track_selected(1); test_equal(q.size(), 2); /* rand() = 1, q = { <5>, [8], } */ test_equal(q.next()->tr_dbe.dbe_index, 8); /* select = 1, q = { <[5]> } */ q.track_selected(0); test_equal(q.size(), 0); /* q = { } */ test_equal(q.next(), NULL); } static void test_sorting() { unsigned int ex_count[] = { 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, 5 }; unsigned int ex_title[] = { 6, 9, 8, 3, 2, 5, 1, 4, 11, 10, 12, 0, 7 }; unsigned int ex_co_ti[] = { 3, 2, 5, 1, 4, 0, 6, 9, 8, 11, 10, 12, 7 }; struct track *track; TestQueue q(0); unsigned int i; for (i = 0; i < 13; i++) { track = track_get(i); track->tr_count = (i < 6) ? 4 : 2; q.add(track); } q.sort(COMPARE_COUNT, true); for (i = 0; i < 13; i++) { test_loop_equal(q[i]->tr_dbe.dbe_index, ex_count[i], i); } test_loop_passed(); q.set_flag(Q_NO_SORT); q.sort(COMPARE_TITLE, true); for (i = 0; i < 13; i++) { test_loop_equal(q[i]->tr_dbe.dbe_index, ex_count[i], i); } test_loop_passed(); q.unset_flag(Q_NO_SORT); q.sort(COMPARE_TITLE, true); for (i = 0; i < 13; i++) { test_loop_equal(q[i]->tr_dbe.dbe_index, ex_title[i], i); } test_loop_passed(); q.sort(COMPARE_COUNT, true); q.sort(COMPARE_TITLE, false); q.sort(COMPARE_COUNT, false); for (i = 0; i < 13; i++) { test_loop_equal(q[i]->tr_dbe.dbe_index, ex_co_ti[i], i); } test_loop_passed(); q.sort(COMPARE_ARTIST, true); q.sort(COMPARE_ALBUM, false); q.sort(COMPARE_TRACK, false); q.sort(COMPARE_TRACK, false); for (i = 0; i < 13; i++) { test_loop_equal(q[i]->tr_dbe.dbe_index, 12 - i, i); } test_loop_passed(); } static void test_save_load() { TestQueue q(Q_RANDOM), r(0); unsigned int i; struct file f; for (i = 0; i < 13; i++) q.add(track_get(i)); q.sort(COMPARE_TRACK, true); q.sort(COMPARE_TRACK, false); file_init(&f, "test.q", 0); file_open(&f, OPEN_WRITE); q.write(f); file_close(&f); file_open(&f, OPEN_READ); r.read(f); file_close(&f); test_equal(r.has_flag(Q_RANDOM), true); test_equal(r.length(), q.length()); test_equal(r.size(), 13); for (i = 0; i < 13; i++) { test_loop_equal(q[i]->tr_dbe.dbe_index, 12 - i, i); } test_loop_passed(); __test_deinit_core(); } DECLARE_UNIT_TESTS( UNIT_TEST("Queue Initialization", test_init), UNIT_TEST("Queue Flags", test_flags), UNIT_TEST("Queue Basics", test_basics), UNIT_TEST("Queue Stress (n = 0)", test_stress_0), UNIT_TEST("Queue Stress (n = 100,000)", test_stress_100K), UNIT_TEST("Queue Random Next and Selection", test_rand_select), UNIT_TEST("Queue Sorting", test_sorting), UNIT_TEST("Queue Save and Load", test_save_load), );