Repository: eclipse-bluechi/bluechi (language: python)

Issue:
Port the linked list macros from the POC
## Please describe what you would like to see

Port the various LIST_ macros from orch.h

## Acceptance Criteria

Maybe throw these macros in a header in the new project, and add a unit test for them.


Discussion / hints:
I've learned that the macros were originally taken from the systemd project, where they are also tested: 

https://github.com/systemd/systemd/blob/main/src/basic/list.h
https://github.com/systemd/systemd/blob/bf1886226724b3db0779d643195d428575cff0be/src/test/test-list.c
Reopening since we need to add unit tests for this.  I will take this issue.

Fix patch:
diff --git a/binchihua/src/common/list.h b/binchihua/src/common/list.h
index a54362b172..9c49df2eea 100644
--- a/binchihua/src/common/list.h
+++ b/binchihua/src/common/list.h
@@ -168,16 +168,17 @@
 #define LIST_FOREACH_AFTER(name, i, p) for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
 
 /* Iterate through all the members of the list p is included in, but skip over p */
-#define LIST_FOREACH_OTHERS(name, i, p)              \
-        for (({                                      \
-                     (i) = (p);                      \
-                     while ((i) && (i)->name##_prev) \
-                             (i) = (i)->name##_prev; \
-                     if ((i) == (p))                 \
-                             (i) = (p)->name##_next; \
-             });                                     \
-             (i);                                    \
-             (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next)
+#define LIST_FOREACH_OTHERS(name, i, p)            \
+        for (typeof(*(p)) *_p = (p), *(i) = ({     \
+                     typeof(*_p) *_j = _p;         \
+                     while (_j && _j->name##_prev) \
+                             _j = _j->name##_prev; \
+                     if (_j == _p)                 \
+                             _j = _p->name##_next; \
+                     _j;                           \
+             });                                   \
+             i;                                    \
+             (i) = (i)->name##_next == _p ? _p->name##_next : (i)->name##_next)
 
 /* Loop starting from p->next until p->prev.
    p can be adjusted meanwhile. */
@@ -202,4 +203,13 @@
                 (b) = NULL;                                \
         } while (false)
 
+#define LIST_POP(name, a)                           \
+        ({                                          \
+                typeof(a) *_a = &(a);               \
+                typeof(a) _p = *_a;                 \
+                if (_p)                             \
+                        LIST_REMOVE(name, *_a, _p); \
+                _p;                                 \
+        })
+
 #endif
\ No newline at end of file


Test patch:
diff --git a/binchihua/test/common/list_test.c b/binchihua/test/common/list_test.c
new file mode 100644
index 0000000000..97589c2d1e
--- /dev/null
+++ b/binchihua/test/common/list_test.c
@@ -0,0 +1,273 @@
+#include "../../src/common/list.h"
+#include <stdio.h>
+
+#define VOID_0 ((void) 0)
+
+#define ELEMENTSOF(x)                                                            \
+        (__builtin_choose_expr(                                                  \
+                        !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
+                        sizeof(x) / sizeof((x)[0]),                              \
+                        VOID_0))
+
+#define assert_se(expr)          \
+        do {                     \
+                if (!(expr)) {   \
+                        exit(1); \
+                }                \
+        } while (false)
+
+int main() {
+        size_t i;
+        typedef struct list_item {
+                LIST_FIELDS(struct list_item, item_list);
+        } list_item;
+        LIST_HEAD(list_item, head);
+        LIST_HEAD(list_item, head2);
+        list_item items[4];
+
+        LIST_HEAD_INIT(head);
+        LIST_HEAD_INIT(head2);
+        assert_se(head == NULL);
+        assert_se(head2 == NULL);
+
+        for (i = 0; i < ELEMENTSOF(items); i++) {
+                LIST_INIT(item_list, &items[i]);
+                assert_se(LIST_JUST_US(item_list, &items[i]));
+                LIST_PREPEND(item_list, head, &items[i]);
+        }
+
+        i = 0;
+        LIST_FOREACH_OTHERS(item_list, cursor, &items[2]) {
+                i++;
+                assert_se(cursor != &items[2]);
+        }
+        assert_se(i == ELEMENTSOF(items) - 1);
+
+        i = 0;
+        LIST_FOREACH_OTHERS(item_list, cursor, &items[0]) {
+                i++;
+                assert_se(cursor != &items[0]);
+        }
+        assert_se(i == ELEMENTSOF(items) - 1);
+
+        i = 0;
+        LIST_FOREACH_OTHERS(item_list, cursor, &items[3]) {
+                i++;
+                assert_se(cursor != &items[3]);
+        }
+        assert_se(i == ELEMENTSOF(items) - 1);
+
+        assert_se(!LIST_JUST_US(item_list, head));
+
+        assert_se(items[0].item_list_next == NULL);
+        assert_se(items[1].item_list_next == &items[0]);
+        assert_se(items[2].item_list_next == &items[1]);
+        assert_se(items[3].item_list_next == &items[2]);
+
+        assert_se(items[0].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == &items[2]);
+        assert_se(items[2].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        list_item *cursor;
+        LIST_FIND_HEAD(item_list, &items[0], cursor);
+        assert_se(cursor == &items[3]);
+
+        LIST_FIND_TAIL(item_list, &items[3], cursor);
+        assert_se(cursor == &items[0]);
+
+        LIST_REMOVE(item_list, head, &items[1]);
+        assert_se(LIST_JUST_US(item_list, &items[1]));
+
+        assert_se(items[0].item_list_next == NULL);
+        assert_se(items[2].item_list_next == &items[0]);
+        assert_se(items[3].item_list_next == &items[2]);
+
+        assert_se(items[0].item_list_prev == &items[2]);
+        assert_se(items[2].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_INSERT_AFTER(item_list, head, &items[3], &items[1]);
+        assert_se(items[0].item_list_next == NULL);
+        assert_se(items[2].item_list_next == &items[0]);
+        assert_se(items[1].item_list_next == &items[2]);
+        assert_se(items[3].item_list_next == &items[1]);
+
+        assert_se(items[0].item_list_prev == &items[2]);
+        assert_se(items[2].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_REMOVE(item_list, head, &items[1]);
+        assert_se(LIST_JUST_US(item_list, &items[1]));
+
+        assert_se(items[0].item_list_next == NULL);
+        assert_se(items[2].item_list_next == &items[0]);
+        assert_se(items[3].item_list_next == &items[2]);
+
+        assert_se(items[0].item_list_prev == &items[2]);
+        assert_se(items[2].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_INSERT_BEFORE(item_list, head, &items[2], &items[1]);
+        assert_se(items[0].item_list_next == NULL);
+        assert_se(items[2].item_list_next == &items[0]);
+        assert_se(items[1].item_list_next == &items[2]);
+        assert_se(items[3].item_list_next == &items[1]);
+
+        assert_se(items[0].item_list_prev == &items[2]);
+        assert_se(items[2].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_REMOVE(item_list, head, &items[0]);
+        assert_se(LIST_JUST_US(item_list, &items[0]));
+
+        assert_se(items[2].item_list_next == NULL);
+        assert_se(items[1].item_list_next == &items[2]);
+        assert_se(items[3].item_list_next == &items[1]);
+
+        assert_se(items[2].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_INSERT_BEFORE(item_list, head, &items[3], &items[0]);
+        assert_se(items[2].item_list_next == NULL);
+        assert_se(items[1].item_list_next == &items[2]);
+        assert_se(items[3].item_list_next == &items[1]);
+        assert_se(items[0].item_list_next == &items[3]);
+
+        assert_se(items[2].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == &items[0]);
+        assert_se(items[0].item_list_prev == NULL);
+        assert_se(head == &items[0]);
+
+        LIST_REMOVE(item_list, head, &items[0]);
+        assert_se(LIST_JUST_US(item_list, &items[0]));
+
+        assert_se(items[2].item_list_next == NULL);
+        assert_se(items[1].item_list_next == &items[2]);
+        assert_se(items[3].item_list_next == &items[1]);
+
+        assert_se(items[2].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_INSERT_BEFORE(item_list, head, NULL, &items[0]);
+        assert_se(items[0].item_list_next == NULL);
+        assert_se(items[2].item_list_next == &items[0]);
+        assert_se(items[1].item_list_next == &items[2]);
+        assert_se(items[3].item_list_next == &items[1]);
+
+        assert_se(items[0].item_list_prev == &items[2]);
+        assert_se(items[2].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_REMOVE(item_list, head, &items[0]);
+        assert_se(LIST_JUST_US(item_list, &items[0]));
+
+        assert_se(items[2].item_list_next == NULL);
+        assert_se(items[1].item_list_next == &items[2]);
+        assert_se(items[3].item_list_next == &items[1]);
+
+        assert_se(items[2].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_REMOVE(item_list, head, &items[1]);
+        assert_se(LIST_JUST_US(item_list, &items[1]));
+
+        assert_se(items[2].item_list_next == NULL);
+        assert_se(items[3].item_list_next == &items[2]);
+
+        assert_se(items[2].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_REMOVE(item_list, head, &items[2]);
+        assert_se(LIST_JUST_US(item_list, &items[2]));
+        assert_se(LIST_JUST_US(item_list, head));
+
+        LIST_REMOVE(item_list, head, &items[3]);
+        assert_se(LIST_JUST_US(item_list, &items[3]));
+
+        assert_se(head == NULL);
+
+        for (i = 0; i < ELEMENTSOF(items); i++) {
+                assert_se(LIST_JUST_US(item_list, &items[i]));
+                LIST_APPEND(item_list, head, &items[i]);
+        }
+
+        assert_se(!LIST_JUST_US(item_list, head));
+
+        assert_se(items[0].item_list_next == &items[1]);
+        assert_se(items[1].item_list_next == &items[2]);
+        assert_se(items[2].item_list_next == &items[3]);
+        assert_se(items[3].item_list_next == NULL);
+
+        assert_se(items[0].item_list_prev == NULL);
+        assert_se(items[1].item_list_prev == &items[0]);
+        assert_se(items[2].item_list_prev == &items[1]);
+        assert_se(items[3].item_list_prev == &items[2]);
+
+        for (i = 0; i < ELEMENTSOF(items); i++)
+                LIST_REMOVE(item_list, head, &items[i]);
+
+        assert_se(head == NULL);
+
+        for (i = 0; i < ELEMENTSOF(items) / 2; i++) {
+                LIST_INIT(item_list, &items[i]);
+                assert_se(LIST_JUST_US(item_list, &items[i]));
+                LIST_PREPEND(item_list, head, &items[i]);
+        }
+
+        for (i = ELEMENTSOF(items) / 2; i < ELEMENTSOF(items); i++) {
+                LIST_INIT(item_list, &items[i]);
+                assert_se(LIST_JUST_US(item_list, &items[i]));
+                LIST_PREPEND(item_list, head2, &items[i]);
+        }
+
+        assert_se(items[0].item_list_next == NULL);
+        assert_se(items[1].item_list_next == &items[0]);
+        assert_se(items[2].item_list_next == NULL);
+        assert_se(items[3].item_list_next == &items[2]);
+
+        assert_se(items[0].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == NULL);
+        assert_se(items[2].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_JOIN(item_list, head2, head);
+        assert_se(head == NULL);
+
+        assert_se(items[0].item_list_next == NULL);
+        assert_se(items[1].item_list_next == &items[0]);
+        assert_se(items[2].item_list_next == &items[1]);
+        assert_se(items[3].item_list_next == &items[2]);
+
+        assert_se(items[0].item_list_prev == &items[1]);
+        assert_se(items[1].item_list_prev == &items[2]);
+        assert_se(items[2].item_list_prev == &items[3]);
+        assert_se(items[3].item_list_prev == NULL);
+
+        LIST_JOIN(item_list, head, head2);
+        assert_se(head2 == NULL);
+        assert_se(head);
+
+        for (i = 0; i < ELEMENTSOF(items); i++)
+                LIST_REMOVE(item_list, head, &items[i]);
+
+        assert_se(head == NULL);
+
+        LIST_PREPEND(item_list, head, items + 0);
+        LIST_PREPEND(item_list, head, items + 1);
+        LIST_PREPEND(item_list, head, items + 2);
+
+        assert_se(LIST_POP(item_list, head) == items + 2);
+        assert_se(LIST_POP(item_list, head) == items + 1);
+        assert_se(LIST_POP(item_list, head) == items + 0);
+        assert_se(LIST_POP(item_list, head) == NULL);
+
+        return 0;
+}
\ No newline at end of file
diff --git a/binchihua/test/common/meson.build b/binchihua/test/common/meson.build
index 7b17a04621..8e077b3f0b 100644
--- a/binchihua/test/common/meson.build
+++ b/binchihua/test/common/meson.build
@@ -3,7 +3,15 @@ parse_util_src = [
   'parse-util_test.c'
 ]
 
+list_src = [
+  'list_test.c'
+]
+
 parse_util_src += common_src
+list_src += common_src
 
 e = executable('parse-util_test', parse_util_src)
-test('parse-util_test', e)
\ No newline at end of file
+test('parse-util_test', e)
+
+e = executable('list_test', list_src)
+test('list_test', e)
\ No newline at end of file
diff --git a/binchihua/test/common/parse-util_test.c b/binchihua/test/common/parse-util_test.c
index 70e5ca7884..10fa456064 100644
--- a/binchihua/test/common/parse-util_test.c
+++ b/binchihua/test/common/parse-util_test.c
@@ -3,12 +3,13 @@
 #include <assert.h>
 #include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #ifndef __FUNCTION_NAME__
 #        define __FUNCTION_NAME__ __func__
 #endif
 
-void test_parse_long(const char *input, bool expectedReturn, long expectedResult) {
+bool test_parse_long(const char *input, bool expectedReturn, long expectedResult) {
         char *msg = NULL;
 
         long res = 0;
@@ -26,10 +27,12 @@ void test_parse_long(const char *input, bool expectedReturn, long expectedResult
 
         if (msg != NULL) {
                 fprintf(stderr, "%s", msg);
+                return false;
         }
+        return true;
 }
 
-void test_parse_port(const char *input, bool expected_return, uint16_t expected_result) {
+bool test_parse_port(const char *input, bool expected_return, uint16_t expected_result) {
         char *msg = NULL;
 
         uint16_t res = 0;
@@ -47,23 +50,31 @@ void test_parse_port(const char *input, bool expected_return, uint16_t expected_
 
         if (msg != NULL) {
                 fprintf(stderr, "%s", msg);
+                return false;
         }
+        return true;
 }
 
 int main() {
-        test_parse_long(NULL, false, 0L);
-        test_parse_long("", false, 0L);
-        test_parse_long("foo", false, 0L);
-        test_parse_long("1234", true, 1234L);
-        test_parse_long("1234abc", false, 1234L);
-        test_parse_long("abc1234", false, 0L);
+        bool result = true;
+        result = result && test_parse_long(NULL, false, 0L);
+        result = result && test_parse_long("", false, 0L);
+        result = result && test_parse_long("foo", false, 0L);
+        result = result && test_parse_long("1234", true, 1234L);
+        result = result && test_parse_long("1234abc", false, 1234L);
+        result = result && test_parse_long("abc1234", false, 0L);
 
-        test_parse_port(NULL, false, 0);
-        test_parse_port("", false, 0);
-        test_parse_port("foo", false, 0);
-        test_parse_port("0", true, 0);
-        test_parse_port("8888", true, 8888);
-        test_parse_port("65535", true, 65535);
-        test_parse_port("65536", false, 0);
-        test_parse_port("-2", false, 0);
+        result = result && test_parse_port(NULL, false, 0);
+        result = result && test_parse_port("", false, 0);
+        result = result && test_parse_port("foo", false, 0);
+        result = result && test_parse_port("0", true, 0);
+        result = result && test_parse_port("8888", true, 8888);
+        result = result && test_parse_port("65535", true, 65535);
+        result = result && test_parse_port("65536", false, 0);
+        result = result && test_parse_port("-2", false, 0);
+
+        if (!result) {
+                return EXIT_FAILURE;
+        }
+        return EXIT_SUCCESS;
 }