root/tests/lib/utilunix__mc_pstream_get_string.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. setup
  2. teardown
  3. START_PARAMETRIZED_TEST
  4. test_mc_popen
  5. test_mc_pread
  6. START_TEST
  7. main

   1 /*
   2    lib - Read string from mc_pipe_stream
   3 
   4    Copyright (C) 2021-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Andrew Borodin <aborodin@vmail.ru>, 2021
   9 
  10    This file is part of the Midnight Commander.
  11 
  12    The Midnight Commander is free software: you can redistribute it
  13    and/or modify it under the terms of the GNU General Public License as
  14    published by the Free Software Foundation, either version 3 of the License,
  15    or (at your option) any later version.
  16 
  17    The Midnight Commander is distributed in the hope that it will be useful,
  18    but WITHOUT ANY WARRANTY; without even the implied warranty of
  19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20    GNU General Public License for more details.
  21 
  22    You should have received a copy of the GNU General Public License
  23    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  24  */
  25 
  26 #define TEST_SUITE_NAME "/lib/util"
  27 
  28 #include "tests/mctest.h"
  29 
  30 #include "lib/util.h"
  31 
  32 /* --------------------------------------------------------------------------------------------- */
  33 
  34 #define MAX_CHUNKS 8
  35 
  36 /* --------------------------------------------------------------------------------------------- */
  37 
  38 static mc_pipe_stream_t stream;
  39 
  40 static char etalon_long_file_list[BUF_1K];
  41 static size_t etalon_long_file_list_pos;
  42 
  43 /* --------------------------------------------------------------------------------------------- */
  44 
  45 /* @Before */
  46 static void
  47 setup (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  48 {
  49 }
  50 
  51 /* @After */
  52 static void
  53 teardown (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  54 {
  55 }
  56 
  57 /* --------------------------------------------------------------------------------------------- */
  58 
  59 /* @DataSource("data_source") */
  60 static const struct data_source
  61 {
  62     // input
  63     const char *buf;  // string to read
  64 
  65     // output
  66     int pos[MAX_CHUNKS];          // ps.pos values
  67     const char *str[MAX_CHUNKS];  // chunks
  68     size_t len[MAX_CHUNKS];       // chunk lengths
  69 } data_source[] = {
  70     {
  71         // 0
  72         .buf = "",
  73         .pos = { 0 },
  74         .str = { "" },
  75         .len = { 0 },
  76     },
  77     {
  78         // 1
  79         .buf = "\n",
  80         .pos = { 0, 1 },
  81         .str = { "\n" },
  82         .len = { 1, 0 },
  83     },
  84     {
  85         // 2
  86         .buf = "\\\n",
  87         .pos = { 0, 2 },
  88         .str = { "\\\n" },
  89         .len = { 2, 0 },
  90     },
  91     {
  92         // 3
  93         .buf = "\\\\\n",
  94         .pos = { 0, 3 },
  95         .str = { "\\\\\n" },
  96         .len = { 3, 0 },
  97     },
  98     {
  99         // 4
 100         .buf = "\\\\\\\n",
 101         .pos = { 0, 4 },
 102         .str = { "\\\\\\\n" },
 103         .len = { 4, 0 },
 104     },
 105     {
 106         // 5
 107         .buf = "\\\\\\\\\n",
 108         .pos = { 0, 5 },
 109         .str = { "\\\\\\\\\n" },
 110         .len = { 5, 0 },
 111     },
 112     {
 113         // 6
 114         .buf = "12345",
 115         .pos = { 0, 5 },
 116         .str = { "12345" },
 117         .len = { 5, 0 },
 118     },
 119     {
 120         // 7
 121         .buf = "12345\n",
 122         .pos = { 0, 6 },
 123         .str = { "12345\n" },
 124         .len = { 6, 0 },
 125     },
 126     {
 127         // 8
 128         .buf = "12345\\\n",
 129         .pos = { 0, 7 },
 130         .str = { "12345\\\n" },
 131         .len = { 7, 0 },
 132     },
 133     {
 134         // 9
 135         .buf = "12345\\\\\n",
 136         .pos = { 0, 8 },
 137         .str = { "12345\\\\\n" },
 138         .len = { 8, 0 },
 139     },
 140     {
 141         // 10
 142         .buf = "12345\nabcd",
 143         .pos = { 0, 6, 10 },
 144         .str = { "12345\n", "abcd" },
 145         .len = { 6, 4, 0 },
 146     },
 147     {
 148         // 11
 149         .buf = "12345\\\nabcd",
 150         .pos = { 0, 11 },
 151         .str = { "12345\\\nabcd" },
 152         .len = { 11, 0 },
 153     },
 154     {
 155         // 12
 156         .buf = "12345\\\\\nabcd",
 157         .pos = { 0, 8, 12 },
 158         .str = { "12345\\\\\n", "abcd" },
 159         .len = { 8, 4, 0 },
 160     },
 161     {
 162         // 13
 163         .buf = "12345\\\\\\\nabcd",
 164         .pos = { 0, 13 },
 165         .str = { "12345\\\\\\\nabcd" },
 166         .len = { 13, 0 },
 167     },
 168     {
 169         // 14
 170         .buf = "12345\\\\\\\\\nabcd",
 171         .pos = { 0, 10, 14 },
 172         .str = { "12345\\\\\\\\\n", "abcd" },
 173         .len = { 10, 4, 0 },
 174     },
 175     {
 176         // 15
 177         .buf = "12345\nabcd\n",
 178         .pos = { 0, 6, 11 },
 179         .str = { "12345\n", "abcd\n" },
 180         .len = { 6, 5, 0 },
 181     },
 182     {
 183         // 16
 184         .buf = "12345\nabcd\n~!@#$%^",
 185         .pos = { 0, 6, 11, 18 },
 186         .str = { "12345\n", "abcd\n", "~!@#$%^" },
 187         .len = { 6, 5, 7, 0 },
 188     },
 189     {
 190         // 17
 191         .buf = "12345\nabcd\n~!@#$%^\n",
 192         .pos = { 0, 6, 11, 19 },
 193         .str = { "12345\n", "abcd\n", "~!@#$%^\n" },
 194         .len = { 6, 5, 8, 0 },
 195     },
 196 };
 197 
 198 /* @Test(dataSource = "data_source") */
 199 START_PARAMETRIZED_TEST (mc_pstream_get_string_test, data_source)
     /* [previous][next][first][last][top][bottom][index][help]  */
 200 {
 201     // given
 202     int j = 0;
 203 
 204     // when
 205     memset (&stream, 0, sizeof (stream));
 206     stream.len = strlen (data->buf);
 207     memmove (&stream.buf, data->buf, stream.len);
 208 
 209     // then
 210     do
 211     {
 212         GString *ret;
 213 
 214         ck_assert_int_eq (stream.pos, data->pos[j]);
 215 
 216         ret = mc_pstream_get_string (&stream);
 217         if (ret == NULL)
 218             break;
 219 
 220         ck_assert_int_eq (ret->len, data->len[j]);
 221         mctest_assert_str_eq (ret->str, data->str[j]);
 222 
 223         g_string_free (ret, TRUE);
 224 
 225         j++;
 226     }
 227     while (TRUE);
 228 }
 229 END_PARAMETRIZED_TEST
 230 
 231 /* --------------------------------------------------------------------------------------------- */
 232 
 233 static mc_pipe_t *
 234 test_mc_popen (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 235 {
 236     mc_pipe_t *p;
 237 
 238     p = g_try_new0 (mc_pipe_t, 1);
 239     // make less than sizeof (etalon_long_file_list)
 240     p->out.len = 128;
 241 
 242     etalon_long_file_list_pos = 0;
 243 
 244     return p;
 245 }
 246 
 247 static void
 248 test_mc_pread (mc_pipe_t *p)
     /* [previous][next][first][last][top][bottom][index][help]  */
 249 {
 250     size_t len;
 251 
 252     p->out.pos = 0;
 253 
 254     if (etalon_long_file_list_pos >= sizeof (etalon_long_file_list))
 255     {
 256         etalon_long_file_list_pos = sizeof (etalon_long_file_list);
 257         p->out.len = MC_PIPE_STREAM_EOF;
 258         return;
 259     }
 260 
 261     len = sizeof (etalon_long_file_list) - etalon_long_file_list_pos;
 262     len = MIN (len, (size_t) p->out.len);
 263     memmove (p->out.buf, etalon_long_file_list + etalon_long_file_list_pos, len);
 264     p->out.len = (ssize_t) len;
 265 
 266     etalon_long_file_list_pos += len;
 267 }
 268 
 269 START_TEST (mc_pstream_get_long_file_list_test)
     /* [previous][next][first][last][top][bottom][index][help]  */
 270 
 271 {
 272     // given
 273     GString *result_long_file_list = NULL;
 274     mc_pipe_t *pip;
 275     GString *remain_file_name = NULL;
 276 
 277     // when
 278     // fill the list
 279     memset (etalon_long_file_list, 'a', sizeof (etalon_long_file_list) - 1);
 280     // create an \n-separated list
 281     etalon_long_file_list[5] = '\n';
 282     etalon_long_file_list[25] = '\n';
 283     etalon_long_file_list[50] = '\n';
 284     etalon_long_file_list[75] = '\n';
 285     etalon_long_file_list[127] = '\n';
 286     etalon_long_file_list[200] = '\n';
 287     etalon_long_file_list[310] = '\n';
 288     etalon_long_file_list[325] = '\n';
 289     etalon_long_file_list[360] = '\n';
 290     etalon_long_file_list[512] = '\n';
 291     etalon_long_file_list[701] = '\n';
 292     etalon_long_file_list[725] = '\n';
 293     etalon_long_file_list[800] = '\n';
 294     etalon_long_file_list[sizeof (etalon_long_file_list) - 2] = '\n';
 295     etalon_long_file_list[sizeof (etalon_long_file_list) - 1] = '\0';
 296 
 297     // then
 298     // read file list
 299     pip = test_mc_popen ();
 300 
 301     while (TRUE)
 302     {
 303         GString *line;
 304 
 305         test_mc_pread (pip);
 306 
 307         if (pip->out.len == MC_PIPE_STREAM_EOF)
 308             break;
 309 
 310         while ((line = mc_pstream_get_string (&pip->out)) != NULL)
 311         {
 312             // handle an \n-separated file list
 313 
 314             if (line->str[line->len - 1] == '\n')
 315             {
 316                 // entire file name or last chunk
 317 
 318                 g_string_truncate (line, line->len - 1);
 319 
 320                 // join filename chunks
 321                 if (remain_file_name != NULL)
 322                 {
 323                     g_string_append_len (remain_file_name, line->str, line->len);
 324                     g_string_free (line, TRUE);
 325                     line = remain_file_name;
 326                     remain_file_name = NULL;
 327                 }
 328             }
 329             else
 330             {
 331                 // first or middle chunk of file name
 332                 if (remain_file_name == NULL)
 333                     remain_file_name = line;
 334                 else
 335                 {
 336                     g_string_append_len (remain_file_name, line->str, line->len);
 337                     g_string_free (line, TRUE);
 338                 }
 339 
 340                 line = NULL;
 341             }
 342 
 343             // collect file names to assemble the result string
 344             if (line == NULL)
 345                 continue;
 346 
 347             if (result_long_file_list == NULL)
 348                 result_long_file_list = line;
 349             else
 350             {
 351                 g_string_append_len (result_long_file_list, line->str, line->len);
 352                 g_string_free (line, TRUE);
 353             }
 354 
 355             g_string_append_c (result_long_file_list, '\n');
 356         }
 357     }
 358 
 359     mctest_assert_str_eq (etalon_long_file_list, result_long_file_list->str);
 360     g_string_free (result_long_file_list, TRUE);
 361 }
 362 END_TEST
 363 
 364 /* --------------------------------------------------------------------------------------------- */
 365 
 366 int
 367 main (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 368 {
 369     TCase *tc_core;
 370 
 371     tc_core = tcase_create ("Core");
 372 
 373     tcase_add_checked_fixture (tc_core, setup, teardown);
 374 
 375     // Add new tests here: ***************
 376     mctest_add_parameterized_test (tc_core, mc_pstream_get_string_test, data_source);
 377     tcase_add_test (tc_core, mc_pstream_get_long_file_list_test);
 378     // ***********************************
 379 
 380     return mctest_run_all (tc_core);
 381 }
 382 
 383 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */