|
|
@@ -0,0 +1,237 @@
|
|
|
+using Invercargill;
|
|
|
+using Invercargill.DataStructures;
|
|
|
+
|
|
|
+void fifo_tests() {
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/basic_push_pop", () => {
|
|
|
+ var fifo = new Fifo<string>();
|
|
|
+
|
|
|
+ // Push items
|
|
|
+ fifo.push("first");
|
|
|
+ fifo.push("second");
|
|
|
+ fifo.push("third");
|
|
|
+
|
|
|
+ // Pop in insertion order (FIFO)
|
|
|
+ var first = fifo.pop();
|
|
|
+ var second = fifo.pop();
|
|
|
+ var third = fifo.pop();
|
|
|
+
|
|
|
+ assert_cmpstr("first", CompareOperator.EQ, first);
|
|
|
+ assert_cmpstr("second", CompareOperator.EQ, second);
|
|
|
+ assert_cmpstr("third", CompareOperator.EQ, third);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/peek functionality", () => {
|
|
|
+ var fifo = new Fifo<int>();
|
|
|
+
|
|
|
+ fifo.push(1);
|
|
|
+ fifo.push(2);
|
|
|
+ fifo.push(3);
|
|
|
+
|
|
|
+ // Peek should return front item without removing
|
|
|
+ var front = fifo.peek();
|
|
|
+ assert_cmpint(1, CompareOperator.EQ, front);
|
|
|
+
|
|
|
+ // Pop should still return 1
|
|
|
+ var popped = fifo.pop();
|
|
|
+ assert_cmpint(1, CompareOperator.EQ, popped);
|
|
|
+
|
|
|
+ // Peek now should return 2
|
|
|
+ var new_front = fifo.peek();
|
|
|
+ assert_cmpint(2, CompareOperator.EQ, new_front);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/empty_fifo_behavior", () => {
|
|
|
+ // Test peek on empty fifo
|
|
|
+ var fifo1 = new Fifo<string>();
|
|
|
+ try {
|
|
|
+ fifo1.peek();
|
|
|
+ assert_not_reached();
|
|
|
+ }
|
|
|
+ catch (SequenceError e) {
|
|
|
+ assert_cmpstr("No elements in queue", CompareOperator.EQ, e.message);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Test pop on empty fifo - need to unblock first
|
|
|
+ var fifo2 = new Fifo<string>();
|
|
|
+ fifo2.unblock();
|
|
|
+ try {
|
|
|
+ fifo2.pop();
|
|
|
+ assert_not_reached();
|
|
|
+ }
|
|
|
+ catch (SequenceError e) {
|
|
|
+ assert_cmpstr("No elements in unblocked queue to pop", CompareOperator.EQ, e.message);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/push_all", () => {
|
|
|
+ var fifo = new Fifo<int>();
|
|
|
+ var items = new Series<int>();
|
|
|
+ items.add(1);
|
|
|
+ items.add(2);
|
|
|
+ items.add(3);
|
|
|
+
|
|
|
+ // Push all items
|
|
|
+ fifo.push_all(items);
|
|
|
+
|
|
|
+ // Should pop in insertion order
|
|
|
+ assert_cmpint(1, CompareOperator.EQ, fifo.pop());
|
|
|
+ assert_cmpint(2, CompareOperator.EQ, fifo.pop());
|
|
|
+ assert_cmpint(3, CompareOperator.EQ, fifo.pop());
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/enumerable_behavior", () => {
|
|
|
+ var fifo = new Fifo<string>();
|
|
|
+
|
|
|
+ fifo.push("first");
|
|
|
+ fifo.push("second");
|
|
|
+ fifo.push("third");
|
|
|
+
|
|
|
+ // Iteration should return items in FIFO order
|
|
|
+ var tracker = fifo.get_tracker();
|
|
|
+ var items = new string[3];
|
|
|
+ var index = 0;
|
|
|
+
|
|
|
+ while (tracker.has_next()) {
|
|
|
+ items[index++] = tracker.get_next();
|
|
|
+ }
|
|
|
+
|
|
|
+ assert_cmpstr("first", CompareOperator.EQ, items[0]);
|
|
|
+ assert_cmpstr("second", CompareOperator.EQ, items[1]);
|
|
|
+ assert_cmpstr("third", CompareOperator.EQ, items[2]);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/count_and_iteration", () => {
|
|
|
+ var fifo = new Fifo<int>();
|
|
|
+
|
|
|
+ // Empty fifo
|
|
|
+ assert_cmpint(0, CompareOperator.EQ, fifo.count());
|
|
|
+
|
|
|
+ // Add items
|
|
|
+ fifo.push(1);
|
|
|
+ fifo.push(2);
|
|
|
+ fifo.push(3);
|
|
|
+
|
|
|
+ assert_cmpint(3, CompareOperator.EQ, fifo.count());
|
|
|
+
|
|
|
+ // Pop items
|
|
|
+ fifo.pop();
|
|
|
+ assert_cmpint(2, CompareOperator.EQ, fifo.count());
|
|
|
+
|
|
|
+ fifo.pop();
|
|
|
+ assert_cmpint(1, CompareOperator.EQ, fifo.count());
|
|
|
+
|
|
|
+ fifo.pop();
|
|
|
+ assert_cmpint(0, CompareOperator.EQ, fifo.count());
|
|
|
+
|
|
|
+ // Unblock to prevent blocking on any future operations
|
|
|
+ fifo.unblock();
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/blocking_behavior", () => {
|
|
|
+ var fifo = new Fifo<string>();
|
|
|
+ bool thread_started = false;
|
|
|
+ bool thread_finished = false;
|
|
|
+
|
|
|
+ // Start a thread that will wait for an item
|
|
|
+ Thread<void*> thread = new Thread<void*>("test-thread", () => {
|
|
|
+ thread_started = true;
|
|
|
+ var item = fifo.pop();
|
|
|
+ assert_cmpstr("test_item", CompareOperator.EQ, item);
|
|
|
+ thread_finished = true;
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+
|
|
|
+ // Wait for thread to start and block
|
|
|
+ while (!thread_started) {
|
|
|
+ Thread.usleep(1000); // 1ms
|
|
|
+ }
|
|
|
+ // Give additional time for thread to block on pop()
|
|
|
+ Thread.usleep(50000); // 50ms
|
|
|
+
|
|
|
+ // Push an item to unblock the thread
|
|
|
+ fifo.push("test_item");
|
|
|
+
|
|
|
+ // Wait for thread to finish
|
|
|
+ thread.join();
|
|
|
+ assert_true(thread_finished);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/unblock_behavior", () => {
|
|
|
+ var fifo = new Fifo<int>();
|
|
|
+ bool thread_started = false;
|
|
|
+ bool exception_thrown = false;
|
|
|
+
|
|
|
+ // Start a thread that will wait for an item
|
|
|
+ Thread<void*> thread = new Thread<void*>("test-thread", () => {
|
|
|
+ thread_started = true;
|
|
|
+ try {
|
|
|
+ var item = fifo.pop(); // This should throw after unblock
|
|
|
+ assert_not_reached();
|
|
|
+ }
|
|
|
+ catch (SequenceError e) {
|
|
|
+ assert_cmpstr("No elements in unblocked queue to pop", CompareOperator.EQ, e.message);
|
|
|
+ exception_thrown = true;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+
|
|
|
+ // Wait for thread to start and block
|
|
|
+ while (!thread_started) {
|
|
|
+ Thread.usleep(1000); // 1ms
|
|
|
+ }
|
|
|
+ // Give additional time for thread to block on pop()
|
|
|
+ Thread.usleep(50000); // 50ms
|
|
|
+
|
|
|
+ // Unblock the fifo
|
|
|
+ fifo.unblock();
|
|
|
+
|
|
|
+ // Wait for thread to finish
|
|
|
+ thread.join();
|
|
|
+ assert_true(exception_thrown);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/try_peek_and_try_pop", () => {
|
|
|
+ var fifo = new Fifo<string>();
|
|
|
+
|
|
|
+ // Try operations on empty fifo
|
|
|
+ string item;
|
|
|
+ assert_false(fifo.try_peek(out item));
|
|
|
+ assert_null(item);
|
|
|
+
|
|
|
+ string popped;
|
|
|
+ assert_false(fifo.try_pop(out popped));
|
|
|
+ assert_null(popped);
|
|
|
+
|
|
|
+ // Add an item
|
|
|
+ fifo.push("test");
|
|
|
+
|
|
|
+ // Try operations should now succeed
|
|
|
+ assert_true(fifo.try_peek(out item));
|
|
|
+ assert_cmpstr("test", CompareOperator.EQ, item);
|
|
|
+
|
|
|
+ assert_true(fifo.try_pop(out popped));
|
|
|
+ assert_cmpstr("test", CompareOperator.EQ, popped);
|
|
|
+
|
|
|
+ // Should be empty again
|
|
|
+ assert_false(fifo.try_pop(out popped));
|
|
|
+ assert_null(popped);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/fifo/push_start", () => {
|
|
|
+ var fifo = new Fifo<string>();
|
|
|
+
|
|
|
+ // Add items normally
|
|
|
+ fifo.push("first");
|
|
|
+ fifo.push("second");
|
|
|
+
|
|
|
+ // Add item to start
|
|
|
+ fifo.push_start("zero");
|
|
|
+
|
|
|
+ // Should pop: zero, first, second
|
|
|
+ assert_cmpstr("zero", CompareOperator.EQ, fifo.pop());
|
|
|
+ assert_cmpstr("first", CompareOperator.EQ, fifo.pop());
|
|
|
+ assert_cmpstr("second", CompareOperator.EQ, fifo.pop());
|
|
|
+ });
|
|
|
+
|
|
|
+}
|