|
|
@@ -0,0 +1,212 @@
|
|
|
+using Invercargill;
|
|
|
+using Invercargill.DataStructures;
|
|
|
+
|
|
|
+void lifo_tests() {
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/lifo/basic_push_pop", () => {
|
|
|
+ var lifo = new Lifo<string>();
|
|
|
+
|
|
|
+ // Push items
|
|
|
+ lifo.push("first");
|
|
|
+ lifo.push("second");
|
|
|
+ lifo.push("third");
|
|
|
+
|
|
|
+ // Pop in reverse order (LIFO)
|
|
|
+ var first = lifo.pop();
|
|
|
+ var second = lifo.pop();
|
|
|
+ var third = lifo.pop();
|
|
|
+
|
|
|
+ assert_cmpstr("third", CompareOperator.EQ, first);
|
|
|
+ assert_cmpstr("second", CompareOperator.EQ, second);
|
|
|
+ assert_cmpstr("first", CompareOperator.EQ, third);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/lifo/peek functionality", () => {
|
|
|
+ var lifo = new Lifo<int>();
|
|
|
+
|
|
|
+ lifo.push(1);
|
|
|
+ lifo.push(2);
|
|
|
+ lifo.push(3);
|
|
|
+
|
|
|
+ // Peek should return top item without removing
|
|
|
+ var top = lifo.peek();
|
|
|
+ assert_cmpint(3, CompareOperator.EQ, top);
|
|
|
+
|
|
|
+ // Pop should still return 3
|
|
|
+ var popped = lifo.pop();
|
|
|
+ assert_cmpint(3, CompareOperator.EQ, popped);
|
|
|
+
|
|
|
+ // Peek now should return 2
|
|
|
+ var new_top = lifo.peek();
|
|
|
+ assert_cmpint(2, CompareOperator.EQ, new_top);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/lifo/empty_lifo_behavior", () => {
|
|
|
+ // Test peek on empty lifo
|
|
|
+ var lifo1 = new Lifo<string>();
|
|
|
+ try {
|
|
|
+ lifo1.peek();
|
|
|
+ assert_not_reached();
|
|
|
+ }
|
|
|
+ catch (SequenceError e) {
|
|
|
+ assert_cmpstr("No elements in stack", CompareOperator.EQ, e.message);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Test pop on empty lifo - need to unblock first
|
|
|
+ var lifo2 = new Lifo<string>();
|
|
|
+ lifo2.unblock();
|
|
|
+ try {
|
|
|
+ lifo2.pop();
|
|
|
+ assert_not_reached();
|
|
|
+ }
|
|
|
+ catch (SequenceError e) {
|
|
|
+ assert_cmpstr("No elements in unblocked stack to pop", CompareOperator.EQ, e.message);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/lifo/push_all", () => {
|
|
|
+ var lifo = new Lifo<int>();
|
|
|
+ var items = new Series<int>();
|
|
|
+ items.add(1);
|
|
|
+ items.add(2);
|
|
|
+ items.add(3);
|
|
|
+
|
|
|
+ // Push all items
|
|
|
+ lifo.push_all(items);
|
|
|
+
|
|
|
+ // Should pop in reverse order
|
|
|
+ assert_cmpint(3, CompareOperator.EQ, lifo.pop());
|
|
|
+ assert_cmpint(2, CompareOperator.EQ, lifo.pop());
|
|
|
+ assert_cmpint(1, CompareOperator.EQ, lifo.pop());
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/lifo/enumerable_behavior", () => {
|
|
|
+ var lifo = new Lifo<string>();
|
|
|
+
|
|
|
+ lifo.push("first");
|
|
|
+ lifo.push("second");
|
|
|
+ lifo.push("third");
|
|
|
+ lifo.unblock();
|
|
|
+
|
|
|
+ // Iteration should return items in LIFO order
|
|
|
+ var tracker = lifo.get_tracker();
|
|
|
+ var items = new string[3];
|
|
|
+ var index = 0;
|
|
|
+
|
|
|
+ while (tracker.has_next()) {
|
|
|
+ items[index++] = tracker.get_next();
|
|
|
+ }
|
|
|
+
|
|
|
+ assert_cmpstr("third", CompareOperator.EQ, items[0]);
|
|
|
+ assert_cmpstr("second", CompareOperator.EQ, items[1]);
|
|
|
+ assert_cmpstr("first", CompareOperator.EQ, items[2]);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/lifo/blocking_behavior", () => {
|
|
|
+ var lifo = new Lifo<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 = lifo.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
|
|
|
+ lifo.push("test_item");
|
|
|
+
|
|
|
+ // Wait for thread to finish
|
|
|
+ thread.join();
|
|
|
+ assert_true(thread_finished);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/lifo/unblock_behavior", () => {
|
|
|
+ var lifo = new Lifo<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 = lifo.pop(); // This should throw after unblock
|
|
|
+ assert_not_reached();
|
|
|
+ }
|
|
|
+ catch (SequenceError e) {
|
|
|
+ assert_cmpstr("No elements in unblocked stack 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 lifo
|
|
|
+ lifo.unblock();
|
|
|
+
|
|
|
+ // Wait for thread to finish
|
|
|
+ thread.join();
|
|
|
+ assert_true(exception_thrown);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/lifo/try_peek_and_try_pop", () => {
|
|
|
+ var lifo = new Lifo<string>();
|
|
|
+
|
|
|
+ // Try operations on empty lifo
|
|
|
+ string item;
|
|
|
+ assert_false(lifo.try_peek(out item));
|
|
|
+ assert_null(item);
|
|
|
+
|
|
|
+ string popped;
|
|
|
+ lifo.unblock();
|
|
|
+ assert_false(lifo.try_pop(out popped));
|
|
|
+ assert_null(popped);
|
|
|
+
|
|
|
+ // Add an item
|
|
|
+ lifo.push("test");
|
|
|
+
|
|
|
+ // Try operations should now succeed
|
|
|
+ assert_true(lifo.try_peek(out item));
|
|
|
+ assert_cmpstr("test", CompareOperator.EQ, item);
|
|
|
+
|
|
|
+ assert_true(lifo.try_pop(out popped));
|
|
|
+ assert_cmpstr("test", CompareOperator.EQ, popped);
|
|
|
+
|
|
|
+ // Should be empty again
|
|
|
+ assert_false(lifo.try_pop(out popped));
|
|
|
+ assert_null(popped);
|
|
|
+ });
|
|
|
+
|
|
|
+ Test.add_func("/invercargill/lifo/push_start", () => {
|
|
|
+ var lifo = new Lifo<string>();
|
|
|
+
|
|
|
+ // Add items normally
|
|
|
+ lifo.push("first");
|
|
|
+ lifo.push("second");
|
|
|
+
|
|
|
+ // Add item to start (same as push for LIFO)
|
|
|
+ lifo.push_start("zero");
|
|
|
+
|
|
|
+ // Should pop: zero, second, first
|
|
|
+ assert_cmpstr("zero", CompareOperator.EQ, lifo.pop());
|
|
|
+ assert_cmpstr("second", CompareOperator.EQ, lifo.pop());
|
|
|
+ assert_cmpstr("first", CompareOperator.EQ, lifo.pop());
|
|
|
+ });
|
|
|
+
|
|
|
+}
|