/* * Copyright 2014 (c) Anna Schumaker. */ #include #include #include unsigned int count_init = 0; unsigned int count_deinit = 0; unsigned int count_added = 0; unsigned int count_erase = 0; unsigned int count_deleted = 0; unsigned int count_cleared = 0; unsigned int count_flags = 0; unsigned int count_sort = 0; unsigned int count_updated = 0; bool can_erase = true; static void *queue_op_init(struct queue *queue, void *data) { count_init++; return GUINT_TO_POINTER(count_init); } static void queue_op_deinit(struct queue *queue) { count_deinit++; } static void queue_op_added(struct queue *queue, unsigned int pos) { count_added++; } static bool queue_op_erase(struct queue *queue, struct track *track) { count_erase++; return can_erase; } static void queue_op_removed(struct queue *queue, unsigned int pos) { count_deleted++; } static void queue_op_cleared(struct queue *queue, unsigned int n) { count_cleared++; } static void queue_op_save(struct queue *queue, enum queue_flags flag) { if (flag == Q_SAVE_FLAGS) count_flags++; else if (flag == Q_SAVE_SORT) count_sort++; } static void queue_op_updated(struct queue *queue, unsigned int pos) { count_updated++; } static const struct queue_ops test_ops = { .qop_init = queue_op_init, .qop_deinit = queue_op_deinit, .qop_added = queue_op_added, .qop_erase = queue_op_erase, .qop_removed = queue_op_removed, .qop_cleared = queue_op_cleared, .qop_save = queue_op_save, .qop_updated = queue_op_updated, }; static void __test_init_core() { struct library *library; 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(); } static void test_init() { struct queue q; struct queue_iter it; __test_init_core(); queue_init(&q, 0, NULL, NULL); test_equal(count_init, 0); test_equal(GPOINTER_TO_UINT(q.q_private), 0); test_equal(q.q_cur.it_pos, (unsigned int)-1); test_equal(q.q_flags, 0); test_equal(q.q_length, 0); test_equal((void *)q.q_sort, NULL); test_equal((void *)q.q_ops, NULL); test_equal((void *)queue_next(&q), (void *)NULL); queue_iter_init(&q, &it); g_assert_null(it.it_iter); g_assert_cmpuint(it.it_pos, ==, (unsigned int)-1); g_assert_null(queue_iter_val(&it)); queue_deinit(&q); test_equal(count_deinit, 0); queue_init(&q, Q_ENABLED | Q_RANDOM, &test_ops, NULL); test_equal(count_init, 1); test_equal(GPOINTER_TO_UINT(q.q_private), 1); test_equal(q.q_cur.it_pos, (unsigned int)-1); test_equal(q.q_flags, Q_ENABLED | Q_RANDOM); test_equal(q.q_length, 0); test_equal((void *)q.q_sort, NULL); test_equal((void *)q.q_ops, (void *)&test_ops); test_equal((void *)queue_next(&q), (void *)NULL); queue_deinit(&q); test_equal(count_deinit, 1); } static void test_flags() { struct queue q; queue_init(&q, 0, &test_ops, NULL); test_equal(q.q_flags, 0); test_equal(queue_has_flag(&q, Q_ENABLED), (bool)false); test_equal(queue_has_flag(&q, Q_RANDOM), (bool)false); test_equal(queue_has_flag(&q, Q_REPEAT), (bool)false); test_equal(queue_has_flag(&q, Q_NO_SORT), (bool)false); test_equal(queue_has_flag(&q, Q_SAVE_FLAGS), (bool)false); test_equal(queue_has_flag(&q, Q_SAVE_SORT), (bool)false); test_equal(queue_has_flag(&q, Q_ADD_FRONT), (bool)false); queue_set_flag(&q, Q_ENABLED); test_equal(q.q_flags, Q_ENABLED); test_equal(count_flags, 0); queue_unset_flag(&q, Q_ENABLED); test_equal(q.q_flags, 0); test_equal(count_flags, 0); queue_set_flag(&q, Q_SAVE_FLAGS); queue_set_flag(&q, Q_ENABLED); queue_set_flag(&q, Q_RANDOM); queue_set_flag(&q, Q_REPEAT); queue_set_flag(&q, Q_NO_SORT); queue_set_flag(&q, Q_ADD_FRONT); test_equal(queue_has_flag(&q, Q_ENABLED), (bool)true); test_equal(queue_has_flag(&q, Q_RANDOM), (bool)true); test_equal(queue_has_flag(&q, Q_REPEAT), (bool)true); test_equal(queue_has_flag(&q, Q_NO_SORT), (bool)true); test_equal(queue_has_flag(&q, Q_SAVE_FLAGS), (bool)true); test_equal(queue_has_flag(&q, Q_ADD_FRONT), (bool)true); test_equal(count_flags, 6); queue_unset_flag(&q, Q_ENABLED); queue_unset_flag(&q, Q_RANDOM); queue_unset_flag(&q, Q_REPEAT); queue_unset_flag(&q, Q_NO_SORT); queue_unset_flag(&q, Q_ADD_FRONT); queue_unset_flag(&q, Q_SAVE_FLAGS); test_equal(q.q_flags, 0); test_equal(count_flags, 11); } static void test_stress(unsigned int N) { unsigned int ex_length = 0; unsigned int ex_size = N; struct queue_iter it; struct track *track; unsigned int i; struct queue q; count_added = 0; count_deleted = 0; count_cleared = 0; count_updated = 0; queue_init(&q, 0, &test_ops, NULL); /* queue_add() */ for (i = 0; i < N; i++) { track = track_get(i % 13); ex_length += track->tr_length; test_loop_equal(queue_add(&q, track), i, i); test_loop_equal(count_added, i + 1, i); } test_loop_passed(); test_equal(q.q_length, ex_length); test_equal(queue_size(&q), ex_size); /* queue_iter_init() */ if (N > 0) { queue_iter_init(&q, &it); g_assert_nonnull(it.it_iter); g_assert_cmpuint(it.it_pos, ==, 0); } /* queue_for_each() */ i = 0; queue_for_each(&q, &it) { g_assert_cmpuint(it.it_pos, ==, i); g_assert(queue_iter_val(&it) == track_get(i % 13)); i++; } g_assert_cmpuint(i, ==, N); g_assert_cmpuint(queue_size(&q), ==, ex_size); /* queue_remove_all() and queue_has() */ track = track_get(0); ex_length -= track->tr_length * (N / 13); ex_size -= (N / 13); if (N > 0) test_equal(queue_has(&q, track), (bool)true); else test_equal(queue_has(&q, track), (bool)false); test_equal(queue_remove_all(&q, track), N / 13); test_equal(q.q_length, ex_length); test_equal(queue_size(&q), ex_size); test_equal(queue_has(&q, track), (bool)false); /* queue_erase() = false */ can_erase = false; for (i = 0; i < ex_size; i += 11) { queue_erase(&q, i); test_loop_equal(q.q_length, ex_length, i); test_loop_equal(queue_size(&q), ex_size, i); } test_loop_passed(); /* queue_remove() and queue_erase() == true */ can_erase = true; 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((void *)queue_at(&q, i), (void *)track, i); if (i % 2 == 0) queue_remove(&q, i); else queue_erase(&q, i); } test_loop_passed(); test_equal(q.q_length, ex_length); test_equal(queue_size(&q), ex_size); /* queue_updated() */ track = track_get(2); queue_updated(&q, track); test_equal(count_updated, N / 13); test_equal((void *)queue_next(&q), NULL); test_equal(queue_size(&q), ex_size); /* Tracks should not be removed. */ queue_set_flag(&q, Q_ENABLED); queue_set_flag(&q, Q_REPEAT); for (i = 0; i < ex_size; i++) { test_loop_equal((void *)queue_next(&q), (void *)track_get((i % 11) + 2), i); test_loop_equal(count_updated, (N / 13) + (2 * i) + 1, i); queue_selected(&q, i); test_loop_equal(count_updated, (N / 13) + (2 * i) + 2, i); test_loop_equal(queue_size(&q), ex_size, i); } test_loop_passed(); /* Tracks should be removed. */ queue_unset_flag(&q, Q_REPEAT); for (i = 0; i < ex_size / 2; i++) { track = queue_next(&q); ex_length -= track->tr_length; ex_size--; test_loop_equal((void *)track, (void *)track_get((i % 11) + 2), i); test_loop_equal(queue_size(&q), ex_size, i); test_loop_equal(q.q_length, ex_length, i); } test_loop_passed(); queue_clear(&q); test_equal(count_cleared, 1); test_equal(queue_size(&q), 0); test_equal(q.q_length, 0); queue_deinit(&q); test_equal(count_cleared, 2); test_equal((void *)q.q_sort, NULL); } 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() { unsigned int i; struct queue q; g_random_set_seed(0); queue_init(&q, Q_ENABLED | Q_RANDOM, &test_ops, NULL); /* Call next() on an empty queue. */ for (i = 0; i < 13; i++) { test_loop_equal((void *)queue_next(&q), NULL, i); test_loop_equal(queue_size(&q), 0, i); } test_loop_passed(); for (i = 0; i < 13; i++) queue_add(&q, track_get(i)); queue_sort(&q, COMPARE_TRACK, true); /* * 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() = 9, q = { <>, 1, 2, 3, 4, 5, 6, 7, 8, [9], 10, 11, 12, 13 } */ test_equal(queue_next(&q)->tr_track, 9); test_equal(queue_size(&q), 12); /* select = 6, q = { 1, 2, 3, 4, 5, 6, (7), <8>, 10, 11, 12, 13 } */ test_equal(queue_selected(&q, 6)->tr_track, 7); test_equal(queue_size(&q), 11); /* rand() = 10, q = { 1, 2, 3, 4, [5], <6>, 8, 10, 11, 12, 13 } */ test_equal(queue_next(&q)->tr_track, 5); test_equal(queue_size(&q), 10); /* select = 7, q = { 1, 2, 3, <4>, 6, 8, 10, (11), 12, 13 } */ test_equal(queue_selected(&q, 7)->tr_track, 11); test_equal(queue_size(&q), 9); /* rand() = 6, q = { 1, 2, 3, [4], 6, 8, <10>, 12, 13 } */ test_equal(queue_next(&q)->tr_track, 4); test_equal(queue_size(&q), 8); /* select = 2, q = { 1, 2, (<3>), 6, 8, 10, 12, 13 } */ test_equal(queue_selected(&q, 2)->tr_track, 3); test_equal(queue_size(&q), 7); /* rand() = 1, q = { 1, <2>, [6], 8, 10, 12, 13 } */ test_equal(queue_next(&q)->tr_track, 6); test_equal(queue_size(&q), 6); /* select = 1, q = { 1, (<2>), 8, 10, 12, 13 } */ test_equal(queue_selected(&q, 1)->tr_track, 2); test_equal(queue_size(&q), 5); /* rand() = 4, q = { <1>, 8, 10, 12, [13] } */ test_equal(queue_next(&q)->tr_track, 13); test_equal(queue_size(&q), 4); /* rand() = 1, q = { [1], 8, 10, <12> } */ test_equal(queue_next(&q)->tr_track, 1); test_equal(queue_size(&q), 3); /* select = 1, q = { <>, 8, (10), 12 } */ test_equal(queue_selected(&q, 1)->tr_track, 10); test_equal(queue_size(&q), 2); /* rand() = 1, q = { <8>, [12] } */ test_equal(queue_next(&q)->tr_track, 12); test_equal(queue_size(&q), 1); /* select = 0, q = { (<8>) } */ test_equal(queue_selected(&q, 0)->tr_track, 8); test_equal(queue_size(&q), 0); /* q = { } */ test_equal((void *)queue_next(&q), NULL); test_equal((void *)queue_selected(&q, 3), NULL); queue_deinit(&q); } static void test_sorting() { unsigned int ex_count[] = { 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6 }; unsigned int ex_title[] = { 7, 10, 9, 4, 3, 6, 2, 5, 12, 11, 13, 1, 8 }; unsigned int ex_co_ti[] = { 4, 3, 6, 2, 5, 1, 7, 10, 9, 12, 11, 13, 8 }; struct track *track; unsigned int i; struct queue q; queue_init(&q, Q_SAVE_SORT | Q_ADD_FRONT, &test_ops, NULL); for (i = 0; i < 13; i++) { track = track_get(i); track->tr_count = (track->tr_track <= 6) ? 4 : 2; queue_add(&q, track); } for (i = 0; i < 13; i++) { track = queue_at(&q, i); test_loop_not_equal((void *)track, NULL, i); test_loop_equal(track->tr_dbe.dbe_index, 12 - i, i); } test_loop_passed(); test_equal(count_sort, 0); queue_sort(&q, COMPARE_TRACK, true); for (i = 0; i < 13; i++) { track = queue_at(&q, i); test_loop_not_equal((void *)track, NULL, i); test_loop_equal(track->tr_track, i + 1, i); } test_loop_passed(); test_equal(count_sort, 1); queue_sort(&q, COMPARE_COUNT, true); for (i = 0; i < 13; i++) { track = queue_at(&q, i); test_loop_not_equal((void *)track, NULL, i); test_loop_equal(track->tr_track, ex_count[i], i); } test_loop_passed(); test_equal(count_sort, 2); queue_set_flag(&q, Q_NO_SORT); queue_sort(&q, COMPARE_TITLE, true); for (i = 0; i < 13; i++) { track = queue_at(&q, i); test_loop_not_equal((void *)track, NULL, i); test_loop_equal(track->tr_track, ex_count[i], i); } test_loop_passed(); test_equal(count_sort, 2); queue_unset_flag(&q, Q_NO_SORT); queue_sort(&q, COMPARE_TITLE, true); for (i = 0; i < 13; i++) { track = queue_at(&q, i); test_loop_not_equal((void *)track, NULL, i); test_loop_equal(track->tr_track, ex_title[i], i); } test_loop_passed(); test_equal(count_sort, 3); queue_sort(&q, COMPARE_COUNT, true); queue_sort(&q, COMPARE_TITLE, false); queue_sort(&q, COMPARE_COUNT, false); for (i = 0; i < 13; i++) { track = queue_at(&q, i); test_loop_not_equal((void *)track, NULL, i); test_loop_equal(track->tr_track, ex_co_ti[i], i); } test_loop_passed(); test_equal(count_sort, 6); queue_unset_flag(&q, Q_SAVE_SORT); queue_sort(&q, COMPARE_ARTIST, true); queue_sort(&q, COMPARE_ALBUM, false); queue_sort(&q, COMPARE_TRACK, false); queue_sort(&q, COMPARE_TRACK, false); for (i = 0; i < 13; i++) { track = queue_at(&q, i); test_loop_not_equal((void *)track, NULL, i); test_loop_equal(track->tr_track, 13 - i, i); } test_loop_passed(); test_equal(count_sort, 6); queue_deinit(&q); test_equal(q.q_length, 0); test_equal(queue_size(&q), 0); test_equal((void *)q.q_sort, NULL); } static void test_save_load() { struct file f = FILE_INIT("queue.q", 0, 0); struct queue q, r; unsigned int i; queue_init(&q, 0, &test_ops, NULL); queue_init(&r, 0, &test_ops, NULL); for (i = 0; i < 13; i++) queue_add(&q, track_get(i)); test_equal(file_exists(&f), (bool)false); test_equal(file_open(&f, OPEN_WRITE), (bool)true); queue_save_tracks(&q, &f); file_close(&f); test_equal(file_exists(&f), (bool)true); test_equal(file_open(&f, OPEN_READ), (bool)true); queue_load_tracks(&r, &f); file_close(&f); test_equal(queue_size(&r), 13); for (i = 0; i < 13; i++) { test_loop_equal(queue_has(&r, track_get(i)), (bool)true, 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), );