]> Kevux Git Server - fll/commitdiff
Progress: Work on basic list combining tests, changing test strategy.
authorKevin Day <Kevin@kevux.org>
Sat, 19 Sep 2026 22:07:29 +0000 (17:07 -0500)
committerKevin Day <Kevin@kevux.org>
Sat, 19 Sep 2026 22:07:29 +0000 (17:07 -0500)
The previous strategies that I used were only doing a file of only **Objects** and files of only **Contents**.
This worked well for simple things.
This ideally better allowed for more isolated unit tests.

However, it is easier to write and follow tests of a complete **FSS** file.
This means doing both the **Object** and **Content** tests in the same unit test (despite being separate functions).
This provides more accurate checks, better testing, and as mentioned easier to write and follow test files.

17 files changed:
level_1/fl_fss/c/fss/basic_list.c
level_1/fl_fss/c/private-fss-list.c
level_1/fl_fss/c/private-fss.c
level_1/fl_fss/data/tests/contents/basic_list-combining_read-1.txt
level_1/fl_fss/data/tests/contents/basic_list-combining_read-2.txt
level_1/fl_fss/data/tests/contents/basic_list-combining_read-3.txt
level_1/fl_fss/data/tests/contents/basic_list-combining_read-4.txt
level_1/fl_fss/data/tests/contents/basic_list-combining_read-5.txt [deleted file]
level_1/fl_fss/data/tests/counts/basic_list-combining_read.txt [new file with mode: 0644]
level_1/fl_fss/data/tests/matches/basic_list-combining_read.txt [new file with mode: 0644]
level_1/fl_fss/data/tests/objects/basic_list-combining_read.txt
level_1/fl_fss/data/tests/strings/basic_list-combining_read.txt
level_1/fl_fss/tests/unit/c/data-fss.c
level_1/fl_fss/tests/unit/c/data-fss.h
level_1/fl_fss/tests/unit/c/test-fss-basic_list_content_read.c
level_1/fl_fss/tests/unit/c/test-fss-extended_list_content_read.c
level_1/fl_fss/tests/unit/c/test-fss.c

index b6044cb88954ed4f37336ded61d31552f042e004..ba5b46d24b2dee3d9c9b492259513f6b7b98d726 100644 (file)
@@ -39,6 +39,7 @@ extern "C" {
 
     #define ___fl_flag_do_escape___                       0x1
     #define ___fl_flag_has_graph_first___                 0x2
+    #define ___fl_flag_has_graph_first_do_escape___       0x3
     #define ___fl_flag_has_delimit_comment___             0x4
     #define ___fl_flag_has_delimit_comment_graph_first___ 0x6
 
@@ -80,10 +81,11 @@ extern "C" {
       }
 
       if (buffer.string[range->start] == f_fss_slash_s.string[0]) {
-        slash_first = range->start;
+        slash_first = range->start++;
         slash_count = 1;
+        flag_fl |= ___fl_flag_do_escape___;
 
-        for (++range->start; range->start <= range->stop && range->start < buffer.used; ++range->start) {
+        for (; F_status_is_error_not(state->status) && range->start <= range->stop && range->start < buffer.used; ++range->start) {
 
           if (state->interrupt) {
             state->interrupt((void *) state, 0);
@@ -91,34 +93,47 @@ extern "C" {
           }
 
           if (buffer.string[range->start] == f_fss_placeholder_s.string[0]) continue;
+
+          // If any slash has a combining/joiner following it, then there is no escaping at all for that slash and therefore it is not considered an escaping character.
+          if (f_fss_is_combining_joiner(buffer, *range, state) == F_true) {
+            flag_fl &= ~___fl_flag_has_graph_first_do_escape___;
+            slash_count = 0;
+
+            f_fss_skip_past_combining_joiner(buffer, range, state);
+
+            break;
+          }
+
+          if (F_status_is_error(state->status)) return;
           if (buffer.string[range->start] != f_fss_slash_s.string[0]) break;
 
           ++slash_count;
-        } // for
+        } // while
 
+        if (F_status_is_error(state->status)) return;
         if (range->start > range->stop || range->start >= buffer.used) break;
+        if (!(flag_fl & ___fl_flag_do_escape___)) continue;
 
         if (buffer.string[range->start] == f_fss_basic_list_open_s.string[0]) {
           flag_fl &= ~___fl_flag_has_delimit_comment_graph_first___;
           ++range->start;
 
-          while (range->start <= range->stop && range->start < buffer.used) {
+          f_fss_skip_past_space(buffer, range, state);
 
-            if (state->interrupt) {
-              state->interrupt((void *) state, 0);
-              if (F_status_set_fine(state->status) == F_interrupt) break;
-            }
+          // The skip space returns F_complete_not_utf_start with error bit set on combining/joiner.
+          if (F_status_set_fine(state->status) == F_complete_not_utf_start) {
+            flag_fl &= ~___fl_flag_has_graph_first_do_escape___;
+            slash_count = 0;
 
-            if (buffer.string[range->start] == f_fss_eol_s.string[0]) break;
-            if (f_fss_is_space(buffer, *range, state) == F_false) break;
+            f_fss_skip_past_combining_joiner(buffer, range, state);
 
-            state->status = f_utf_buffer_increment(buffer, range, 1);
-            if (F_status_is_error(state->status)) break;
-          } // while
+            continue;
+          }
 
+          if (F_status_is_error(state->status) || state->status == F_data_not) return;
           if (range->start > range->stop || range->start >= buffer.used) break;
 
-          if (buffer.string[range->start] == f_fss_eol_s.string[0]) {
+          if (state->status == F_okay_eol) {
             start = range->start;
             range->start = slash_first;
 
@@ -159,8 +174,15 @@ extern "C" {
             flag_fl |= ___fl_flag_has_graph_first___;
             range->start = start + 1;
           }
+          else {
+            flag_fl &= ~___fl_flag_do_escape___;
+            slash_count = 0;
+          }
         }
+
+        // TODO: Resume here.
         else if ((flag_fl & ___fl_flag_has_graph_first___) && buffer.string[range->start] == f_fss_comment_s.string[0]) {
+          // TODO: check combining/joiner.
           flag_fl &= ___fl_flag_has_delimit_comment___;
           comment_delimit = slash_first;
           ++range->start;
@@ -173,7 +195,9 @@ extern "C" {
         ++range->start;
         flag_fl &= ~___fl_flag_has_delimit_comment_graph_first___;
 
-        while (range->start <= range->stop && range->start < buffer.used) {
+        // TODO: check combining/joiner.
+
+        while (range->start <= range->stop && range->start < buffer.used) { // TODO: replace with seek to eol?
 
           if (state->interrupt) {
             state->interrupt((void *) state, 0);
@@ -194,7 +218,6 @@ extern "C" {
         if (buffer.string[range->start] == f_fss_eol_s.string[0]) {
           if (newline_last == found->array[found->used].start && buffer.string[found->array[found->used].start] != f_fss_eol_s.string[0]) {
             range->start = newline_last;
-
             state->status = F_fss_found_content_not;
 
             return;
@@ -223,6 +246,7 @@ extern "C" {
       }
 
       if ((flag_fl & ___fl_flag_has_graph_first___) && buffer.string[range->start] == f_fss_comment_s.string[0]) {
+        // TODO: check combining/joiner.
 
         // The newline_last is initialized to the range->start, which may not actually be a new line.
         if (buffer.string[newline_last] == f_string_eol_s.string[0]) {
@@ -351,7 +375,7 @@ extern "C" {
 
     const f_number_unsigned_t destination_used = destination->used;
 
-    uint8_t flag = prepend ? ___fl_flag_do_prepend___ : 0x0;
+    uint8_t flag_fl = prepend ? ___fl_flag_do_prepend___ : 0x0;
     uint8_t width = 0;
 
     f_number_unsigned_t i = 0;
@@ -366,50 +390,50 @@ extern "C" {
         if (F_status_set_fine(state->status) == F_interrupt) break;
       }
 
-      if (flag & ___fl_flag_is_combining___) {
+      if (flag_fl & ___fl_flag_is_combining___) {
         f_fss_skip_past_delimit(content, range, state);
         if (F_status_is_error(state->status)) break;
 
         if (f_fss_is_combining(content, *range, state) != F_true) {
           if (F_status_is_error(state->status)) break;
 
-          flag &= ~___fl_flag_is_combining___;
+          flag_fl &= ~___fl_flag_is_combining___;
         }
       }
 
-      if (flag & ___fl_flag_is_joinerd___) {
+      if (flag_fl & ___fl_flag_is_joinerd___) {
         f_fss_skip_past_delimit(content, range, state);
         if (F_status_is_error(state->status)) break;
 
         if (f_fss_is_joiner(content, *range, state) == F_true) {
-          flag |= ___fl_flag_is_joiner___;
-          flag &= ~___fl_flag_is_joined___;
+          flag_fl |= ___fl_flag_is_joiner___;
+          flag_fl &= ~___fl_flag_is_joined___;
         }
         else {
           if (F_status_is_error(state->status)) break;
 
-          if (flag & ___fl_flag_is_joiner___) {
-            flag |= ___fl_flag_is_joined___;
-            flag &= ~___fl_flag_is_joiner___;
+          if (flag_fl & ___fl_flag_is_joiner___) {
+            flag_fl |= ___fl_flag_is_joined___;
+            flag_fl &= ~___fl_flag_is_joiner___;
           }
           else {
-            flag &= ~___fl_flag_is_joined___;
+            flag_fl &= ~___fl_flag_is_joined___;
           }
         }
       }
 
-      if (content.string[range->start] == f_fss_slash_s.string[0] && !(flag & ___fl_flag_is_comment___)) {
+      if (content.string[range->start] == f_fss_slash_s.string[0] && !(flag_fl & ___fl_flag_is_comment___)) {
         slash_count = 1;
 
-        if (flag & ___fl_flag_do_prepend___) {
+        if (flag_fl & ___fl_flag_do_prepend___) {
           state->status = f_string_dynamic_append(*prepend, destination);
           if (F_status_is_error(state->status)) break;
 
-          flag &= ~___fl_flag_do_prepend___;
+          flag_fl &= ~___fl_flag_do_prepend___;
         }
 
         destination->string[destination->used++] = content.string[range->start];
-        flag |= ___fl_flag_has_graph___;
+        flag_fl |= ___fl_flag_has_graph___;
 
         for (++range->start; range->start <= range->stop && range->start < content.used; ++range->start) {
 
@@ -437,6 +461,17 @@ extern "C" {
           start = range->start++;
 
           f_fss_skip_past_space(content, range, state);
+
+/*
+          if (F_status_set_fine(state->status) == F_complete_not_utf_start) {
+            flag_fl &= ~___fl_flag_has_graph_first_do_escape___;
+            slash_count = 0;
+
+            f_fss_skip_past_combining_joiner(buffer, range, state);
+
+            continue;
+          }*/
+
           if (F_status_is_error(state->status)) break;
 
           if (state->status == F_okay_eol || range->start >= content.used || range->start > range->stop) {
@@ -469,19 +504,29 @@ extern "C" {
           continue;
         }
       }
-      else if (content.string[range->start] == f_fss_basic_list_open_s.string[0] && !(flag & ___fl_flag_is_comment___)) {
+      else if (content.string[range->start] == f_fss_basic_list_open_s.string[0] && !(flag_fl & ___fl_flag_is_comment___)) {
         start = range->start++;
 
-        if (flag & ___fl_flag_do_prepend___) {
+        if (flag_fl & ___fl_flag_do_prepend___) {
           state->status = f_string_dynamic_append(*prepend, destination);
           if (F_status_is_error(state->status)) break;
 
-          flag &= ~___fl_flag_do_prepend___;
+          flag_fl &= ~___fl_flag_do_prepend___;
         }
 
-        flag |= ___fl_flag_has_graph___;
+        flag_fl |= ___fl_flag_has_graph___;
+
+        f_fss_skip_past_space(content, range, state); // TODO: destination->string[destination->used++] = f_fss_slash_s.string[0]; don't skip entirely, also write! Do this for all before continuing based on the "start" position above.
+/*
+        if (F_status_set_fine(state->status) == F_complete_not_utf_start) {
+          flag_fl &= ~___fl_flag_has_graph_first_do_escape___;
+          slash_count = 0;
+
+          f_fss_skip_past_combining_joiner(buffer, range, state);
+
+          continue; // TODO: see todo's above and also repeat for the other cases in this file.
+        }*/
 
-        f_fss_skip_past_space(content, range, state);
         if (F_status_is_error(state->status)) break;
 
         if (state->status == F_okay_eol || range->start >= content.used || range->start > range->stop) {
@@ -509,11 +554,11 @@ extern "C" {
 
         continue;
       }
-      else if (content.string[range->start] == f_fss_comment_s.string[0] && !(flag & ___fl_flag_has_graph___)) {
-        flag |= ___fl_flag_is_comment___;
+      else if (content.string[range->start] == f_fss_comment_s.string[0] && !(flag_fl & ___fl_flag_has_graph___)) {
+        flag_fl |= ___fl_flag_is_comment___;
       }
       else if (content.string[range->start] == f_fss_eol_s.string[0]) {
-        flag = 0x0;
+        flag_fl = 0x0;
       }
       else if (f_fss_is_graph(content, *range, state) == F_false) {
         if (F_status_is_error(state->status)) break;
@@ -525,10 +570,10 @@ extern "C" {
         if (state->status == F_okay) {
           if (f_fss_is_combining_joiner(content, *range, state)) {
             if (state->status == F_combarudoo) {
-              flag |= ___fl_flag_is_combining_graph___;
+              flag_fl |= ___fl_flag_is_combining_graph___;
             }
             else if (state->status == F_bound) {
-              flag |= ___fl_flag_is_joiner_graph___;
+              flag_fl |= ___fl_flag_is_joiner_graph___;
             }
           }
         }
@@ -538,19 +583,19 @@ extern "C" {
         range->start = previous;
       }
       else {
-        flag |= ___fl_flag_has_graph___;
+        flag_fl |= ___fl_flag_has_graph___;
       }
 
       if (content.string[range->start] != f_fss_placeholder_s.string[0]) {
-        if (flag & ___fl_flag_do_prepend___) {
+        if (flag_fl & ___fl_flag_do_prepend___) {
           state->status = f_string_dynamic_append(*prepend, destination);
           if (F_status_is_error(state->status)) break;
 
-          flag &= ~___fl_flag_do_prepend___;
+          flag_fl &= ~___fl_flag_do_prepend___;
         }
 
         if (content.string[range->start] == f_fss_eol_s.string[0] && prepend) {
-          flag |= ___fl_flag_do_prepend___;
+          flag_fl |= ___fl_flag_do_prepend___;
         }
 
         width = macro_f_utf_byte_width(content.string[range->start]);
@@ -686,7 +731,7 @@ extern "C" {
     f_number_unsigned_t previous = 0;
     f_number_unsigned_t slash_count = 0;
 
-    uint8_t flag = 0;
+    uint8_t flag_fl = 0;
     uint8_t width = 0;
 
     // Find the first graph character.
@@ -708,34 +753,34 @@ extern "C" {
         break;
       }
 
-      if (flag & ___fl_flag_is_combining___) {
+      if (flag_fl & ___fl_flag_is_combining___) {
         f_fss_skip_past_delimit(object, range, state);
         if (F_status_is_error(state->status)) break;
 
         if (f_fss_is_combining(object, *range, state) != F_true) {
           if (F_status_is_error(state->status)) break;
 
-          flag &= ~___fl_flag_is_combining___;
+          flag_fl &= ~___fl_flag_is_combining___;
         }
       }
 
-      if (flag & ___fl_flag_is_joinerd___) {
+      if (flag_fl & ___fl_flag_is_joinerd___) {
         f_fss_skip_past_delimit(object, range, state);
         if (F_status_is_error(state->status)) break;
 
         if (f_fss_is_joiner(object, *range, state) == F_true) {
-          flag |= ___fl_flag_is_joiner___;
-          flag &= ~___fl_flag_is_joined___;
+          flag_fl |= ___fl_flag_is_joiner___;
+          flag_fl &= ~___fl_flag_is_joined___;
         }
         else {
           if (F_status_is_error(state->status)) break;
 
-          if (flag & ___fl_flag_is_joiner___) {
-            flag |= ___fl_flag_is_joined___;
-            flag &= ~___fl_flag_is_joiner___;
+          if (flag_fl & ___fl_flag_is_joiner___) {
+            flag_fl |= ___fl_flag_is_joined___;
+            flag_fl &= ~___fl_flag_is_joiner___;
           }
           else {
-            flag &= ~___fl_flag_is_joined___;
+            flag_fl &= ~___fl_flag_is_joined___;
           }
         }
       }
@@ -750,10 +795,10 @@ extern "C" {
         if (state->status == F_okay) {
           if (f_fss_is_combining_joiner(object, *range, state)) {
             if (state->status == F_combarudoo) {
-              flag |= ___fl_flag_is_combining_graph___;
+              flag_fl |= ___fl_flag_is_combining_graph___;
             }
             else if (state->status == F_bound) {
-              flag |= ___fl_flag_is_joiner_graph___;
+              flag_fl |= ___fl_flag_is_joiner_graph___;
             }
           }
         }
index 9a0918a3de1f74ef7bbe154b654c7dbd069e041e..9940293e594cb7fbcd0ae8d95d44909e8e9dc843 100644 (file)
@@ -11,6 +11,7 @@ extern "C" {
     f_fss_skip_past_space(buffer, range, state);
     if (F_status_is_error(state->status) || state->status == F_data_not) return;
 
+    // Return found nothing if this line only contains white space and delimit placeholders.
     if (state->status == F_okay_eol) {
 
       // Move the start position to after the EOL.
@@ -26,16 +27,6 @@ extern "C" {
       return;
     }
 
-    // Return found nothing if this line only contains white space and delimit placeholders.
-    if (state->status == F_okay_eol) {
-
-      // Move the start position to after the EOL.
-      ++range->start;
-      state->status = F_fss_found_object_not;
-
-      return;
-    }
-
     const f_number_unsigned_t begin = range->start;
 
     // Ignore all comment lines.
@@ -176,6 +167,7 @@ extern "C" {
 
           if (F_status_is_error_not(state->status)) {
             if (f_fss_is_combining_joiner(buffer, *range, state) == F_true) {
+              flag_fl &= ~___fl_flag_do_escape___;
               slash_count = 0;
 
               f_fss_skip_past_combining_joiner(buffer, range, state);
@@ -219,7 +211,7 @@ extern "C" {
             }
 
             if (slash_count % 2 == 0) {
-              while (slash_count > 0) {
+              while (slash_count) {
 
                 if (buffer.string[range->start] == f_fss_slash_s.string[0]) {
                   if (slash_count % 2 == 1) {
@@ -275,6 +267,7 @@ extern "C" {
 
           if (F_status_is_error_not(state->status)) {
             if (f_fss_is_combining_joiner(buffer, *range, state) == F_true) {
+              flag_fl &= ~___fl_flag_do_escape___;
               slash_count = 0;
 
               f_fss_skip_past_combining_joiner(buffer, range, state);
index 1017af2312432e1a5fcfb8c9393c97c2464c31fc..2b49539c413c8ff3065637f17de79839050154ee 100644 (file)
@@ -115,7 +115,7 @@ extern "C" {
           }
 
           if (buffer.string[range->start] == f_fss_placeholder_s.string[0]) {
-            state->status = f_utf_buffer_increment(buffer, range, 1);
+            ++range->start;
 
             continue;
           }
@@ -254,8 +254,7 @@ extern "C" {
             }
 
             if (buffer.string[range->start] == f_fss_placeholder_s.string[0]) {
-              state->status = f_utf_buffer_increment(buffer, range, 1);
-              if (F_status_is_error(state->status)) return;
+              ++range->start;
 
               continue;
             }
index 311e0a56196063f0b20337190b3a1897e3031b32..177a9a6f72dda7ec4591baf515c34e7161583e22 100644 (file)
@@ -1 +1,3 @@
   is_second and content 2.
+ ́:
+  does not have ́ content 3.
index 652a208a4da41cccc5e295552841a07b11af04f4..96487d57a8e415215d50943411f9daa3ab207b61 100644 (file)
@@ -1 +1 @@
-  has ́ content 3.
+  has ͜no_space content 4.
index 96487d57a8e415215d50943411f9daa3ab207b61..78c611949a9d7df34f6f810bbd37eeb6c09f8875 100644 (file)
@@ -1 +1 @@
-  has ͜no_space content 4.
+  content 5.
index 78c611949a9d7df34f6f810bbd37eeb6c09f8875..5e7418a0614a45b0e8fff30c9b99748300e40d14 100644 (file)
@@ -1 +1,2 @@
-  content 5.
+  sixtẖ̚ is content 6.
+  not a valid list:́
diff --git a/level_1/fl_fss/data/tests/contents/basic_list-combining_read-5.txt b/level_1/fl_fss/data/tests/contents/basic_list-combining_read-5.txt
deleted file mode 100644 (file)
index 5e7418a..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-  sixtẖ̚ is content 6.
-  not a valid list:́
diff --git a/level_1/fl_fss/data/tests/counts/basic_list-combining_read.txt b/level_1/fl_fss/data/tests/counts/basic_list-combining_read.txt
new file mode 100644 (file)
index 0000000..9f145d8
--- /dev/null
@@ -0,0 +1,5 @@
+1
+3
+1
+1
+1
diff --git a/level_1/fl_fss/data/tests/matches/basic_list-combining_read.txt b/level_1/fl_fss/data/tests/matches/basic_list-combining_read.txt
new file mode 100644 (file)
index 0000000..3f22f14
--- /dev/null
@@ -0,0 +1,5 @@
+2
+2
+2
+2
+2
index d2132bf517b75ecd3a1e26de6bd8092901efb2a8..b540243f905fd0bbd29c55b5dc7821354b71035e 100644 (file)
@@ -1,6 +1,5 @@
 first valid.
 is_second and does not have embedded new line.
- ́
 fourth ͜no_space
 fifth is valid.
 sixtẖ̚ is valid with two combiners.
index 6f47d5cfbdc89f388687e67ca52574a699d6caea..5b912582803a65ae86d0f95c14ec137244581374 100644 (file)
@@ -3,7 +3,7 @@ first valid.:
 is_second and does not have embedded new line.:
   is_second and content 2.
  ́:
-  has ́ content 3.
+  does not have ́ content 3.
 fourth ͜no_space:
   has ͜no_space content 4.
 fifth is valid.:
index ea4d35db4d2caa31ade9b5d2a2a915d35f56788d..cd91e26d1e3d14f0a0a72531d8814edce152a960 100644 (file)
@@ -26,6 +26,65 @@ FILE *data__file_open__named_at(const f_string_t set, const f_string_t name, con
   return fopen(path, "r");
 }
 
+f_status_t data__file_load_buffer(FILE * const file, f_string_dynamic_t * const buffer) {
+
+  if (!buffer) {
+    return F_status_set_error(F_parameter);
+  }
+
+  f_status_t status = F_okay;
+  ssize_t result = 0;
+  size_t max = 0;
+  char *lines = 0;
+  f_string_static_t temporary = f_string_static_t_initialize;
+
+  buffer->used = 0;
+
+  for (int i = 0; ; ++i, max = 255) {
+
+    result = getline(&lines, &max, file);
+    if (result <= 0) break;
+
+    temporary.string = lines;
+    temporary.used = (f_number_unsigned_t) result;
+    temporary.size = temporary.used;
+
+    status = f_string_dynamic_append_nulless(temporary, buffer);
+    if (F_status_is_error(status)) break;
+
+    if (lines) free(lines);
+
+    lines = 0;
+  } // for
+
+  if (F_status_is_error_not(status)) {
+    status = f_string_dynamic_terminate_after(buffer);
+  }
+
+  if (lines) free(lines);
+
+  return status;
+}
+
+f_status_t data__extract_from_source(const f_string_static_t source, const f_range_t range, f_string_dynamic_t * const destination) {
+
+  if (!destination) {
+    return F_status_set_error(F_parameter);
+  }
+
+  f_status_t status = F_okay;
+
+  destination->used = 0;
+
+  status = f_string_dynamic_partial_append_nulless(source, range, destination);
+
+  if (F_status_is_error_not(status)) {
+    status = f_string_dynamic_terminate_after(destination);
+  }
+
+  return status;
+}
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 15a5ee84fbeed4d85104b4a14ef95c204380efa5..f3ea807cea322595af4effbe3fb7ac01fd5817a4 100644 (file)
@@ -74,6 +74,51 @@ extern FILE *data__file_open__named(const f_string_t set, const f_string_t name,
  */
 extern FILE *data__file_open__named_at(const f_string_t set, const f_string_t name, const f_string_t context, const uint16_t at);
 
+/**
+ * Append file into buffer.
+ *
+ * @param file
+ *   The file, which must already be open.
+ * @param buffer
+ *   The buffer to append to.
+ *   The buffer.used is set to 0 on start, unless NULL.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ *   Errors (with error bit) from: f_string_dynamic_append_nulless().
+ *   Errors (with error bit) from: f_string_dynamic_terminate_after().
+ *
+ * @see f_string_dynamic_append()
+ * @see f_string_dynamic_terminate_after()
+ */
+extern f_status_t data__file_load_buffer(FILE * const file, f_string_dynamic_t * const buffer);
+
+/**
+ * Extract the given string and range from the source buffer.
+ *
+ * The removes NULLs when extracting and ensures the string is NULL terminated.
+ *
+ * @param source
+ *   The buffer to read from.
+ * @param range
+ *   The range within the buffer to extract.
+ * @param destination
+ *   The extracted string.
+ *   The destination.used is set to 0 on start, unless NULL.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ *   Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ *   Errors (with error bit) from: f_string_dynamic_terminate_after().
+ */
+extern f_status_t data__extract_from_source(const f_string_static_t source, const f_range_t range, f_string_dynamic_t * const destination);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 0911349796207f5c023ac0e17cfe1729f0f587a3..ea84bad355cfc31c56180b209585ac5498bab6e7 100644 (file)
@@ -334,7 +334,9 @@ void test__fl_fss_basic_list_content_read__works_using_combining(void **void_sta
 
   {
     // Note: These files are required to have the same number of lines and each line should probably be at max 255 characters.
+    FILE *file_counts = data__file_open__named("counts", "basic_list", "combining_read");
     FILE *file_matches = data__file_open__named("matches", "basic_list", "combining_read");
+    FILE *file_objects = data__file_open__named("objects", "basic_list", "combining_read");
     FILE *file_strings = data__file_open__named("strings", "basic_list", "combining_read");
 
     FILE *file_contents[] = {
@@ -343,92 +345,129 @@ void test__fl_fss_basic_list_content_read__works_using_combining(void **void_sta
       data__file_open__named_at("contents", "basic_list", "combining_read", 2),
       data__file_open__named_at("contents", "basic_list", "combining_read", 3),
       data__file_open__named_at("contents", "basic_list", "combining_read", 4),
-      data__file_open__named_at("contents", "basic_list", "combining_read", 5),
     };
 
+    assert_non_null(file_counts);
     assert_non_null(file_matches);
+    assert_non_null(file_objects);
     assert_non_null(file_strings);
     assert_non_null(file_contents[0]);
     assert_non_null(file_contents[1]);
     assert_non_null(file_contents[2]);
     assert_non_null(file_contents[3]);
     assert_non_null(file_contents[4]);
-    assert_non_null(file_contents[5]);
 
+    const uint8_t sets_total = 5;
+
+    int counts = 0;
     int matches = 0;
     size_t max = 0;
-    char *line_matches = 0;
-    char *line_string = 0;
-    char *line_content[] = {
-      0,
-      0,
-      0,
-      0,
-      0,
-      0,
-    };
     ssize_t result = 0;
-    const uint8_t total_content = 6;
 
-    f_string_static_t buffer_string = f_string_static_t_initialize;
+    char *line_counts = 0;
+    char *line_matches = 0;
+    char *line_object = 0;
 
-    f_state_t state = f_state_t_initialize;
     f_range_t range = f_range_t_initialize;
     f_range_t found_object = f_range_t_initialize;
-    f_ranges_t found = f_ranges_t_initialize;
+    f_ranges_t found_content = f_ranges_t_initialize;
     f_ranges_t comments = f_ranges_t_initialize;
+
+    f_state_t state = f_state_t_initialize;
+    f_status_t status_object = F_okay;
     f_uint8s_t quotes = f_uint8s_t_initialize;
     f_number_unsigneds_t delimits = f_number_unsigneds_t_initialize;
-    f_string_dynamic_t result_string = f_string_dynamic_t_initialize;
-    f_string_dynamic_t delimit_string = f_string_dynamic_t_initialize;
-    f_status_t status_object = F_okay;
 
-    for (f_number_unsigned_t line = 0; ; ++line) {
+    f_string_dynamic_t set_delimit = f_string_dynamic_t_initialize;
+    f_string_dynamic_t set_result = f_string_dynamic_t_initialize;
+    f_string_dynamic_t set_strings = f_string_dynamic_t_initialize;
 
-      max = 255;
+    f_string_dynamic_t set_contents[] = {
+      macro_f_string_dynamic_t_initialize_2(0, 0),
+      macro_f_string_dynamic_t_initialize_2(0, 0),
+      macro_f_string_dynamic_t_initialize_2(0, 0),
+      macro_f_string_dynamic_t_initialize_2(0, 0),
+      macro_f_string_dynamic_t_initialize_2(0, 0),
+    };
 
-      result = getline(&line_matches, &max, file_matches);
-      if (result == -1) break;
+    {
+      const f_status_t status = data__file_load_buffer(file_strings, &set_strings);
 
-      matches = atoi(line_matches);
+      if (F_status_is_error(status)) {
+        printf("[ -------> ] --- [00] Failure with data__file_load_buffer() for loading file strings, status=%u has error bit set.\n", status);
+      }
+
+      assert_true(F_status_is_error_not(status));
+    }
+
+    for (uint8_t i = 0; i < sets_total; ++i) {
+
+      const f_status_t status = data__file_load_buffer(file_contents[i], &set_contents[i]);
+
+      if (F_status_is_error(status)) {
+        printf("[ -------> ] --- [01] Failure with data__file_load_buffer() for loading file contents at %u, status=%u has error bit set.\n", i, status);
+      }
+
+      assert_true(F_status_is_error_not(state.status));
+    } // for
+
+    {
+      const f_status_t status = f_string_dynamic_append(set_strings, &set_delimit);
+
+      if (status != F_okay) {
+        printf("[ -------> ] --- [02] Failure with f_string_dynamic_append() for preparing delimit string, status=%u, expected=%u.\n", status, F_okay);
+      }
+
+      assert_int_equal(status, F_okay);
+    }
+
+    range.start = 0;
+    range.stop = set_strings.used - 1;
+
+    for (f_number_unsigned_t set = 0; set < sets_total; ++set) {
 
       max = 255;
 
-      result = getline(&line_string, &max, file_strings);
-      if (result == -1) break;
+      result = getline(&line_object, &max, file_objects);
 
-      buffer_string.string = line_string;
-      buffer_string.used = (f_number_unsigned_t) result;
-      buffer_string.size = buffer_string.used;
+      if (result < 0) {
+        printf("[ -------> ] --- [03] Failure with getline() for objects, with set %lu, result is negative.\n", set);
+      }
 
-      for (uint8_t i = 0; i < total_content; ++i) {
+      assert_true(result >= 0);
 
-        max = 255;
+      // The newline is copied by getline(), and so remove that newline before comparing.
+      if (result) {
+        line_object[result - 1] = 0;
+      }
+      else {
+        line_object[0] = 0;
+      }
 
-        result = getline(&line_content[i], &max, file_contents[i]);
+      max = 255;
 
-        // The newline is copied by getline(), and so remove that newline before comparing.
-        if (result > 0) {
-          line_content[i][result - 1] = 0;
-        }
-        else {
-          line_content[i][0] = 0;
-        }
-      } // for
+      result = getline(&line_matches, &max, file_matches);
+
+      if (result < 0) {
+        printf("[ -------> ] --- [04] Failure with getline() for matches, with set %lu, result is negative.\n", set);
+      }
+
+      assert_true(result >= 0);
+
+      matches = atoi(line_matches);
 
-      state.status = F_none;
-      range.start = 0;
-      range.stop = buffer_string.used - 1;
       found_object.start = 1;
       found_object.stop = 0;
+      state.status = F_none;
 
-      fl_fss_basic_list_object_read(buffer_string, &range, &found_object, &delimits, &state);
+      fl_fss_basic_list_object_read(set_strings, &range, &found_object, &delimits, &state);
 
       if (matches) {
+
         // When matches is 2, then this matches both object and content.
         if (matches == 2) {
           if (!(state.status == F_fss_found_object)) {
-            printf("[ -------> ] --- [00] Failure with fl_fss_basic_list_object_read(), with line %lu, matches=%d, status=%u, expected %u.\n", line, matches, state.status, F_fss_found_object);
+            printf("[ -------> ] --- [05] Failure with fl_fss_basic_list_object_read(), with set %lu, matches=%d, status=%u, expected %u.\n", set, matches, state.status, F_fss_found_object);
           }
 
           assert_true(state.status == F_fss_found_object);
@@ -437,7 +476,7 @@ void test__fl_fss_basic_list_content_read__works_using_combining(void **void_sta
         // When matches is 1, then this matches only object.
         else {
           if (!(state.status == F_fss_found_object_content_not)) {
-            printf("[ -------> ] --- [01] Failure with fl_fss_basic_list_object_read(), with line %lu, matches=%d, status=%u, expected %u.\n", line, matches, state.status, F_fss_found_object_content_not);
+            printf("[ -------> ] --- [06] Failure with fl_fss_basic_list_object_read(), with set %lu, matches=%d, status=%u, expected %u.\n", set, matches, state.status, F_fss_found_object_content_not);
           }
 
           assert_true(state.status == F_fss_found_object_content_not);
@@ -445,7 +484,7 @@ void test__fl_fss_basic_list_content_read__works_using_combining(void **void_sta
       }
       else {
         if (!(state.status == F_fss_found_object_not)) {
-          printf("[ -------> ] --- [02] Failure with with fl_fss_basic_list_object_read(), line %lu, matches=%d, status=%u, expected %u.\n", line, matches, state.status, F_fss_found_object_not);
+          printf("[ -------> ] --- [07] Failure with with fl_fss_basic_list_object_read(), set %lu, matches=%d, status=%u, expected %u.\n", set, matches, state.status, F_fss_found_object_not);
         }
 
         assert_true(state.status == F_fss_found_object_not);
@@ -455,148 +494,144 @@ void test__fl_fss_basic_list_content_read__works_using_combining(void **void_sta
       state.status = F_none;
 
       if (status_object == F_fss_found_object) {
-        fl_fss_basic_list_content_read(buffer_string, &range, &found, &delimits, &comments, &state);
-
-        if (!(state.status == F_fss_found_content || state.status == F_data_not)) {
-          printf("[ -------> ] --- [03] Failure with fl_fss_basic_list_content_read(), with line %lu, status=%u, expected %u or %u.\n", line, state.status, F_fss_found_content, F_data_not);
-        }
 
-        assert_true(state.status == F_fss_found_content || state.status == F_data_not);
+        // The Object had to be read before getting to the Content, so test it as well.
+        {
+          const f_status_t status = data__extract_from_source(set_strings, found_object, &set_result);
 
-        if (state.status == F_fss_found_content) {
-          if (!found.used) {
-            printf("[ -------> ] --- [04] Failure with fl_fss_basic_list_content_read(), with line %lu, expected found.used > 0.\n", line);
+          if (F_status_is_error(status)) {
+            printf("[ -------> ] --- [14] Failure with data__extract_from_source() for extract object, with set %lu, status=%u has error bit set.\n", set, status);
           }
 
-          assert_true(found.used);
-
-          if (found.used <= (f_number_unsigned_t) total_content) {
-            printf("[ -------> ] --- [05] Failure with fl_fss_basic_list_content_read(), with line %lu, expected found.used (%lu) <= total_conent (%lu).\n", line, found.used, (f_number_unsigned_t) total_content);
-          }
+          assert_true(F_status_is_error_not(status));
+        }
 
-          assert_true(found.used <= (f_number_unsigned_t) total_content);
+        if (strcmp(set_result.string, line_object)) {
+          printf("[ -------> ] --- [09] Failure with set %lu, object strings do not match.\n", set);
+        }
 
-          {
-            const f_status_t status = f_string_dynamic_append(buffer_string, &delimit_string);
+        assert_string_equal(set_result.string, line_object);
 
-            if (status != F_okay) {
-              printf("[ -------> ] --- [06] Failure with f_string_dynamic_append(), with line %lu, status=%u, expected=%u.\n", line, status, F_okay);
-            }
+        max = 255;
 
-            assert_int_equal(status, F_okay);
-          }
+        result = getline(&line_counts, &max, file_counts);
 
-          state.status = F_none;
+        if (result < 0) {
+          printf("[ -------> ] --- [10] Failure with getline() for counts, with set %lu, result is negative.\n", set);
+        }
 
-          f_fss_apply_delimit(delimits, &delimit_string, &state);
+        assert_true(result >= 0);
 
-          if (state.status != F_okay) {
-            printf("[ -------> ] --- [07] Failure with f_fss_apply_delimit(), with line %lu, status=%u, expected=%u.\n", line, state.status, F_okay);
-          }
+        counts = atoi(line_counts);
 
-          assert_int_equal(state.status, F_okay);
+        fl_fss_basic_list_content_read(set_strings, &range, &found_content, &delimits, &comments, &state);
 
-          {
-            uint8_t i = 0;
+        if (!(state.status == F_fss_found_content || state.status == F_data_not)) {
+          printf("[ -------> ] --- [11] Failure with fl_fss_basic_list_content_read(), with set %lu, status=%u, expected %u or %u.\n", set, state.status, F_fss_found_content, F_data_not);
+        }
 
-            for (; i < found.used; ++i) {
+        assert_true(state.status == F_fss_found_content || state.status == F_data_not);
 
-              result_string.used = 0;
+        if (state.status == F_fss_found_content) {
+          if (!found_content.used) {
+            printf("[ -------> ] --- [12] Failure with fl_fss_basic_list_content_read(), with set %lu, expected found_content.used (%lu) <> 0.\n", set, found_content.used);
+          }
 
-              {
-                const f_status_t status = f_string_dynamic_partial_append_nulless(delimit_string, found.array[i], &result_string);
+          assert_true(found_content.used);
 
-                if (!(status == F_okay || status == F_data_not_eos)) {
-                  printf("[ -------> ] --- [08] Failure with f_string_dynamic_partial_append_nulless(), with line %lu, status=%u, expected %u or %u.\n", line, status, F_okay, F_data_not_eos);
-                }
+          if (found_content.used != (f_number_unsigned_t) counts) {
+            printf("[ -------> ] --- [13] Failure with fl_fss_basic_list_content_read(), with set %lu, expected found_content.used (%lu) == %lu.\n", set, found_content.used, (f_number_unsigned_t) counts);
+          }
 
-                assert_true(status == F_okay || status == F_data_not_eos);
-              }
+          assert_true(found_content.used == (f_number_unsigned_t) counts);
 
-              {
-                const f_status_t status = f_string_dynamic_terminate_after(&result_string);
+          state.status = F_none;
 
-                if (!(status == F_okay)) {
-                  printf("[ -------> ] --- [09] Failure with f_string_dynamic_terminate_after(), with line %lu, status=%u, expected %u.\n", line, status, F_okay);
-                }
+          f_fss_apply_delimit(delimits, &set_delimit, &state);
 
-                assert_int_equal(status, F_okay);
-              }
+          if (state.status != F_okay) {
+            printf("[ -------> ] --- [14] Failure with f_fss_apply_delimit(), with set %lu, status=%u, expected=%u.\n", set, state.status, F_okay);
+          }
 
-              if (strcmp(result_string.string, line_content[i])) {
-                printf("[ -------> ] --- [10] Failure with line %lu, strings do not match.\n", line);
-              }
+          assert_int_equal(state.status, F_okay);
 
-              assert_string_equal(result_string.string, line_content[i]);
-            } // for
+          {
+            const f_status_t status = data__extract_from_source(set_delimit, found_content.array[0], &set_result);
 
-            // All remaining positions in the file need to be a zero length string.
-            for (; i < total_content; ++i) {
-              assert_int_equal(line_content[i][0], 0);
+            if (F_status_is_error(status)) {
+              printf("[ -------> ] --- [15] Failure with data__extract_from_source() for extract set contents, with set %lu, status=%u has error bit set.\n", set, status);
             }
+
+            assert_true(F_status_is_error_not(status));
           }
+
+          if (strcmp(set_result.string, set_contents[set].string)) {
+            printf("[ -------> ] --- [16] Failure with set %lu, content strings do not match.\n", set);
+          }
+
+          assert_string_equal(set_result.string, set_contents[set].string);
         }
         else {
-          assert_true(!found.used);
+          if (found_content.used) {
+            printf("[ -------> ] --- [17] Failure with set %lu, should not have any content, but match %lu.\n", set, found_content.used);
+          }
+
+          assert_true(!found_content.used);
         }
       }
 
-      if (line_matches) free(line_matches);
-      if (line_string) free(line_string);
-      if (result_string.string) free(result_string.string);
-      if (delimit_string.string) free(delimit_string.string);
-      if (delimits.array) free(delimits.array);
-      if (found.array) free(found.array);
       if (comments.array) free(comments.array);
+      if (delimits.array) free(delimits.array);
+      if (found_content.array) free(found_content.array);
       if (quotes.array) free(quotes.array);
 
-      for (uint8_t i = 0; i < total_content; ++i) {
-
-        if (line_content[i]) free(line_content[i]);
-
-        line_content[i] = 0;
-      } // for
+      if (line_counts) free(line_counts);
+      if (line_matches) free(line_matches);
+      if (line_object) free(line_object);
+      if (set_result.string) free(set_result.string);
 
-      line_matches = 0;
-      line_string = 0;
-      result_string.string = 0;
-      result_string.used = 0;
-      result_string.size = 0;
-      delimit_string.string = 0;
-      delimit_string.used = 0;
-      delimit_string.size = 0;
-      delimits.array = 0;
-      delimits.used = 0;
-      delimits.size = 0;
-      found.array = 0;
-      found.used = 0;
-      found.size = 0;
       comments.array = 0;
       comments.used = 0;
       comments.size = 0;
+      delimits.array = 0;
+      delimits.used = 0;
+      delimits.size = 0;
+      found_content.array = 0;
+      found_content.used = 0;
+      found_content.size = 0;
+      line_counts = 0;
+      line_matches = 0;
+      line_object = 0;
       quotes.array = 0;
       quotes.used = 0;
       quotes.size = 0;
+      set_result.string = 0;
+      set_result.used = 0;
+      set_result.size = 0;
     } // for
 
-    for (uint8_t i = 0; i < total_content; ++i) {
+    for (uint8_t i = 0; i < sets_total; ++i) {
 
       if (file_contents[i]) fclose(file_contents[i]);
-      if (line_content[i]) free(line_content[i]);
-
-      line_content[i] = 0;
+      if (set_contents[i].string) free(set_contents[i].string);
     } // for
 
+    if (line_counts) free(line_counts);
+    if (line_matches) free(line_matches);
+    if (line_object) free(line_object);
+    if (set_delimit.string) free(set_delimit.string);
+    if (set_result.string) free(set_result.string);
+    if (set_strings.string) free(set_strings.string);
+
+    if (file_counts) fclose(file_counts);
+    if (file_matches) fclose(file_matches);
+    if (file_objects) fclose(file_objects);
     if (file_strings) fclose(file_strings);
 
-    if (delimits.array) free(delimits.array);
-    if (found.array) free(found.array);
     if (comments.array) free(comments.array);
+    if (delimits.array) free(delimits.array);
+    if (found_content.array) free(found_content.array);
     if (quotes.array) free(quotes.array);
-    if (line_matches) free(line_matches);
-    if (line_string) free(line_string);
-    if (result_string.string) free(result_string.string);
-    if (delimit_string.string) free(delimit_string.string);
   }
 }
 
index c2095647ef5e5ee8f6040a2ac20b390a1a3b7fe2..85118934b063c488d0e96127eb5544dca7a23889 100644 (file)
@@ -532,9 +532,6 @@ void test__fl_fss_extended_list_content_read__works_using_combining(void **void_
         if (result > 0) {
           line_content[i][result - 1] = 0;
         }
-        else {
-          line_content[i][0] = 0;
-        }
       } // or
 
       state.status = F_none;
index 2c9b604730b35d3656586db78cbb770ecbef2528..105b98330360132c7fa1f8274de310288728ed40 100644 (file)
@@ -35,9 +35,9 @@ int main(void) {
     cmocka_unit_test(test__fl_fss_basic_list_object_write__returns_data_not),
 
     cmocka_unit_test(test__fl_fss_basic_list_content_read__works),
-    //cmocka_unit_test(test__fl_fss_basic_list_content_read__works_using_combining),
+    cmocka_unit_test(test__fl_fss_basic_list_content_read__works_using_combining),
     cmocka_unit_test(test__fl_fss_basic_list_object_read__works),
-    //cmocka_unit_test(test__fl_fss_basic_list_object_read__works_using_combining),
+    cmocka_unit_test(test__fl_fss_basic_list_object_read__works_using_combining),
 
     cmocka_unit_test(test__fl_fss_embedded_list_content_read__returns_data_not),
     cmocka_unit_test(test__fl_fss_embedded_list_content_write__returns_data_not),