ocarina/tests/core/queue.c

425 lines
12 KiB
C
Raw Normal View History

/*
* Copyright 2014 (c) Anna Schumaker.
*/
#include <core/filter.h>
#include <core/queue.h>
#include <core/random.h>
#include <core/tags/tags.h>
#include <tests/test.h>
unsigned int count_added = 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;
static void queue_op_added(struct queue *queue, unsigned int pos)
{
count_added++;
}
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_added = queue_op_added,
.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;
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()
{
struct queue q;
__test_init_core();
queue_init(&q, 0, NULL);
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_init(&q, Q_ENABLED | Q_RANDOM, &test_ops);
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);
}
static void test_flags()
{
struct queue q;
queue_init(&q, 0, &test_ops);
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 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);
/* 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_remove_all() */
track = track_get(0);
ex_length -= track->tr_length * (N / 13);
ex_size -= (N / 13);
queue_remove_all(&q, track);
test_equal(q.q_length, ex_length);
test_equal(queue_size(&q), ex_size);
/* queue_remove() */
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);
queue_remove(&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);
queue_selected(&q, 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;
random_seed(0);
queue_init(&q, Q_ENABLED | Q_RANDOM, &test_ops);
/* 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:
* <val>: 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 = { <>, 1, [2], 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 } */
test_equal(queue_next(&q)->tr_track, 2);
test_equal(queue_size(&q), 12);
/* select = 6, q = { <1>, 3, 4, 5, 6, 7, (8), 9, 10, 11, 12, 13 } */
test_equal(queue_selected(&q, 6)->tr_track, 8);
test_equal(queue_size(&q), 11);
/* rand() = 2, q = { 1, 3, 4, 5, 6, <7>, 9, 10, [11], 12, 13 } */
test_equal(queue_next(&q)->tr_track, 11);
test_equal(queue_size(&q), 10);
/* select = 7, q = { 1, 3, 4, 5, 6, 7, 9, (<10>), 12, 13 } */
test_equal(queue_selected(&q, 7)->tr_track, 10);
test_equal(queue_size(&q), 9);
/* rand() = 4, q = { 1, [3], 4, 5, 6, 7, <9>, 12, 13 } */
test_equal(queue_next(&q)->tr_track, 3);
test_equal(queue_size(&q), 8);
/* select = 2, q = { <1>, 4, (5), 6, 7, 9, 12, 13 } */
test_equal(queue_selected(&q, 2)->tr_track, 5);
test_equal(queue_size(&q), 7);
/* rand() = 5, q = { 1, <4>, 6, 7, 9, 12, [13] } */
test_equal(queue_next(&q)->tr_track, 13);
test_equal(queue_size(&q), 6);
/* select = 1, q = { 1, (4), 6, 7, 9, <12> } */
test_equal(queue_selected(&q, 1)->tr_track, 4);
test_equal(queue_size(&q), 5);
/* rand() = 2, q = { <1>, 6, [7], 9, 12 } */
test_equal(queue_next(&q)->tr_track, 7);
test_equal(queue_size(&q), 4);
/* rand() = 1, q = { 1, <6>, [9], 12 } */
test_equal(queue_next(&q)->tr_track, 9);
test_equal(queue_size(&q), 3);
/* select = 1, q = { 1, (<6>), 12 } */
test_equal(queue_selected(&q, 1)->tr_track, 6);
test_equal(queue_size(&q), 2);
/* rand() = 1, q = { <1>, [12] } */
test_equal(queue_next(&q)->tr_track, 12);
test_equal(queue_size(&q), 1);
/* select = 0, q = { <1> } */
test_equal(queue_selected(&q, 0)->tr_track, 1);
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[] = { 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;
unsigned int i;
struct queue q;
queue_init(&q, Q_SAVE_SORT | Q_ADD_FRONT, &test_ops);
for (i = 0; i < 13; i++) {
track = track_get(i);
track->tr_count = (i < 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_dbe.dbe_index, i, 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_dbe.dbe_index, 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_dbe.dbe_index, 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_dbe.dbe_index, 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_dbe.dbe_index, 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_dbe.dbe_index, 12 - 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);
__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),
);