LexLeo 0.0.0-dev+f8e5087-dirty
Technical documentation
Loading...
Searching...
No Matches
unit_test_stream.c
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-3.0-or-later
2 * Copyright (C) 2026 Sylvain Labopin
3 */
4
30
34
35#include "osal/mem/osal_mem.h"
37
41
42#include "lexleo_cmocka.h"
43
46//-----------------------------------------------------------------------------
47// LOCAL TEST DOUBLES
48//-----------------------------------------------------------------------------
49
65typedef struct fake_stream_backend_t {
67 int read_called;
68 int write_called;
69 int flush_called;
70 int close_called;
71
73 void *last_read_buf;
74 const void *last_write_buf;
75 size_t last_read_n;
76 size_t last_write_n;
77 void *last_backend;
78 stream_status_t *last_read_st;
79 stream_status_t *last_write_st;
80
82 stream_status_t read_st_to_set;
83 stream_status_t write_st_to_set;
84 size_t read_ret;
85 size_t write_ret;
86 stream_status_t flush_ret;
87 stream_status_t close_ret;
88} fake_stream_backend_t;
89
95static void fake_stream_backend_reset(fake_stream_backend_t *b) {
96 assert_non_null(b);
97 b->read_called = 0;
98 b->write_called = 0;
99 b->flush_called = 0;
100 b->close_called = 0;
101
102 // last arguments
103 b->last_read_buf = NULL;
104 b->last_write_buf = NULL;
105 b->last_read_n = (size_t)-1;
106 b->last_write_n = (size_t)-1;
107 b->last_backend = NULL;
108 b->last_read_st = NULL;
109 b->last_write_st = NULL;
110
111 // configurable behavior
112 b->read_st_to_set = STREAM_STATUS_INVALID;
113 b->write_st_to_set = STREAM_STATUS_INVALID;
114 b->read_ret = (size_t)-1;
115 b->write_ret = (size_t)-1;
116 b->flush_ret = STREAM_STATUS_INVALID;
117 b->close_ret = STREAM_STATUS_OK;
118}
119
129static size_t fake_stream_read(
130 void *backend,
131 void *buf,
132 size_t n,
133 stream_status_t *st)
134{
135 fake_stream_backend_t *b = (fake_stream_backend_t *)backend;
136 assert_non_null(b);
137
138 b->read_called++;
139
140 b->last_backend = backend;
141 b->last_read_buf = buf;
142 b->last_read_n = n;
143 b->last_read_st = st;
144
145 if (st) *st = b->read_st_to_set;
146 return b->read_ret;
147}
148
158static size_t fake_stream_write(
159 void *backend,
160 const void *buf,
161 size_t n,
162 stream_status_t *st)
163{
164 fake_stream_backend_t *b = (fake_stream_backend_t *)backend;
165 assert_non_null(b);
166
167 b->write_called++;
168
169 b->last_backend = backend;
170 b->last_write_buf = buf;
171 b->last_write_n = n;
172 b->last_write_st = st;
173
174 if (st) *st = b->write_st_to_set;
175 return b->write_ret;
176}
177
185static stream_status_t fake_stream_flush(void *backend)
186{
187 fake_stream_backend_t *b = (fake_stream_backend_t *)backend;
188 assert_non_null(b);
189
190 b->flush_called++;
191
192 b->last_backend = backend;
193
194 return b->flush_ret;
195}
196
207static stream_status_t fake_stream_close(void *backend)
208{
209 if (!backend) return STREAM_STATUS_NO_BACKEND;
210 fake_stream_backend_t *b = (fake_stream_backend_t *)backend;
211
212 b->close_called++;
213
214 b->last_backend = backend;
215
216 return b->close_ret;
217}
218
227static const stream_vtbl_t fake_stream_vtbl = {
228 .read = fake_stream_read,
229 .write = fake_stream_write,
230 .flush = fake_stream_flush,
231 .close = fake_stream_close
232};
233
251static void test_stream_default_ops(void **state) {
252 (void)state;
253 const stream_ops_t *ret = stream_default_ops();
254 assert_non_null(ret);
255 assert_non_null(ret->read);
256 assert_non_null(ret->write);
257 assert_non_null(ret->flush);
258}
259
278static void test_stream_default_env(void **state) {
279 (void)state;
280
281 const osal_mem_ops_t dummy = {0};
282 const osal_mem_ops_t *dummy_p = &dummy;
283
284 stream_env_t ret = stream_default_env(dummy_p);
285
286 assert_ptr_equal(ret.mem, dummy_p);
287}
288
406
425typedef enum {
426 OUT_CHECK_NONE,
427 OUT_EXPECT_NULL,
428 OUT_EXPECT_NON_NULL,
429 OUT_EXPECT_UNCHANGED
430} out_expect_t;
431
439typedef struct {
440 const char *name;
441
443 size_t fail_call_idx;
444
445 stream_status_t expected_ret;
446 out_expect_t out_expect;
447} test_stream_lifecycle_case_t;
448
458typedef struct {
459 // runtime resources
460 stream_t *out;
461
462 // injection
463 stream_env_t env;
464
465 fake_stream_backend_t backend;
466
467 const test_stream_lifecycle_case_t *tc;
468} test_stream_lifecycle_fixture_t;
469
470//-----------------------------------------------------------------------------
471// FIXTURES
472//-----------------------------------------------------------------------------
473
477static int setup_stream_lifecycle(void **state)
478{
479 const test_stream_lifecycle_case_t *tc =
480 (const test_stream_lifecycle_case_t *)(*state);
481
482 test_stream_lifecycle_fixture_t *fx =
483 (test_stream_lifecycle_fixture_t *)malloc(sizeof(*fx));
484 if (!fx) return -1;
485
486 osal_memset(fx, 0, sizeof(*fx));
487 fx->tc = tc;
488
489 fake_memory_reset();
490 if (tc->scenario == STREAM_LIFECYCLE_SCENARIO_OOM && tc->fail_call_idx > 0) {
491 fake_memory_fail_only_on_call(tc->fail_call_idx);
492 }
493
494 // DI
495 fx->env.mem = osal_mem_test_fake_ops();
496
497 osal_memset(&fx->backend, 0, sizeof(fx->backend));
498 fx->backend.close_ret = STREAM_STATUS_OK;
499
500 *state = fx;
501 return 0;
502}
503
507static int teardown_stream_lifecycle(void **state)
508{
509 test_stream_lifecycle_fixture_t *fx =
510 (test_stream_lifecycle_fixture_t *)(*state);
511
512 if (fx->out) {
513 stream_destroy(&fx->out);
514 }
515
516 assert_true(fake_memory_no_leak());
517 assert_true(fake_memory_no_invalid_free());
518 assert_true(fake_memory_no_double_free());
519
520 free(fx);
521 return 0;
522}
523
524//-----------------------------------------------------------------------------
525// TEST
526//-----------------------------------------------------------------------------
527
531static void test_stream_lifecycle(void **state)
532{
533 test_stream_lifecycle_fixture_t *fx =
534 (test_stream_lifecycle_fixture_t *)(*state);
535 const test_stream_lifecycle_case_t *tc = fx->tc;
536
537 // ARRANGE
539
540 stream_t **out_arg = &fx->out;
541 const stream_vtbl_t *vtbl_arg = &fake_stream_vtbl;
542 stream_vtbl_t vtbl_local;
543 void *backend_arg = &fx->backend;
544 const stream_env_t *env_arg = &fx->env;
545
546 // invalid args
547 if (tc->scenario == STREAM_LIFECYCLE_SCENARIO_OUT_NULL) out_arg = NULL;
548 if (tc->scenario == STREAM_LIFECYCLE_SCENARIO_VTBL_NULL) vtbl_arg = NULL;
549 if (tc->scenario == STREAM_LIFECYCLE_SCENARIO_ENV_NULL) env_arg = NULL;
550 if (tc->scenario == STREAM_LIFECYCLE_SCENARIO_ENV_MEM_NULL) fx->env.mem = NULL;
551 if (tc->scenario == STREAM_LIFECYCLE_SCENARIO_VTBL_READ_NULL) {
552 vtbl_local = fake_stream_vtbl;
553 vtbl_local.read = NULL;
554 vtbl_arg = &vtbl_local;
555 }
556
557 // ensure OUT_EXPECT_UNCHANGED is meaningful
558 if (tc->out_expect == OUT_EXPECT_UNCHANGED && out_arg != NULL) {
559 fx->out = (stream_t *)(uintptr_t)0xDEADC0DEu; // sentinel
560 }
561
562 stream_t *out_arg_snapshot = fx->out;
563
564 // ACT
565 ret = stream_create(out_arg, vtbl_arg, backend_arg, env_arg);
567 assert_int_equal(ret, STREAM_STATUS_OK);
568 assert_non_null(fx->out);
569
570 stream_destroy(&fx->out);
571 assert_null(fx->out);
572
573 stream_destroy(&fx->out);
574 assert_null(fx->out);
575 }
576
577 // ASSERT
578 assert_int_equal(ret, tc->expected_ret);
579
580 switch (tc->out_expect) {
581 case OUT_CHECK_NONE: break;
582 case OUT_EXPECT_NULL: assert_null(fx->out); break;
583 case OUT_EXPECT_NON_NULL: assert_non_null(fx->out); break;
584 case OUT_EXPECT_UNCHANGED:
585 assert_ptr_equal(out_arg_snapshot, fx->out);
586 fx->out = NULL; // prevent teardown from destroying sentinel
587 break;
588 default: fail();
589 }
590}
591
592//-----------------------------------------------------------------------------
593// CASES
594//-----------------------------------------------------------------------------
595
596static const test_stream_lifecycle_case_t CASE_STREAM_LIFECYCLE_OUT_NULL = {
597 .name = "stream_lifecycle_out_null",
599 .fail_call_idx = 0,
600
601 .expected_ret = STREAM_STATUS_INVALID,
602 .out_expect = OUT_CHECK_NONE
603};
604
605static const test_stream_lifecycle_case_t CASE_STREAM_LIFECYCLE_VTBL_NULL = {
606 .name = "stream_lifecycle_vtbl_null",
608 .fail_call_idx = 0,
609
610 .expected_ret = STREAM_STATUS_INVALID,
611 .out_expect = OUT_EXPECT_UNCHANGED
612};
613
614static const test_stream_lifecycle_case_t CASE_STREAM_LIFECYCLE_ENV_NULL = {
615 .name = "stream_lifecycle_env_null",
617 .fail_call_idx = 0,
618
619 .expected_ret = STREAM_STATUS_INVALID,
620 .out_expect = OUT_EXPECT_UNCHANGED
621};
622
623static const test_stream_lifecycle_case_t CASE_STREAM_LIFECYCLE_ENV_MEM_NULL = {
624 .name = "stream_lifecycle_env_mem_null",
626 .fail_call_idx = 0,
627
628 .expected_ret = STREAM_STATUS_INVALID,
629 .out_expect = OUT_EXPECT_UNCHANGED
630};
631
632static const test_stream_lifecycle_case_t CASE_STREAM_LIFECYCLE_OOM = {
633 .name = "stream_lifecycle_oom",
635 .fail_call_idx = 1,
636
637 .expected_ret = STREAM_STATUS_OOM,
638 .out_expect = OUT_EXPECT_UNCHANGED
639};
640
641static const test_stream_lifecycle_case_t CASE_STREAM_LIFECYCLE_DESTROY_IDEMPOTENT = {
642 .name = "stream_lifecycle_destroy_idempotent",
644 .fail_call_idx = 0,
645
646 .expected_ret = STREAM_STATUS_OK,
647 .out_expect = OUT_EXPECT_NULL
648};
649
650static const test_stream_lifecycle_case_t CASE_STREAM_LIFECYCLE_OK = {
651 .name = "stream_lifecycle_ok",
653 .fail_call_idx = 0,
654
655 .expected_ret = STREAM_STATUS_OK,
656 .out_expect = OUT_EXPECT_NON_NULL
657};
658
659static const test_stream_lifecycle_case_t CASE_STREAM_LIFECYCLE_VTBL_READ_NULL = {
660 .name = "stream_lifecycle_vtbl_read_null",
662 .fail_call_idx = 0,
663
664 .expected_ret = STREAM_STATUS_INVALID,
665 .out_expect = OUT_EXPECT_UNCHANGED
666};
667
668//-----------------------------------------------------------------------------
669// CASES REGISTRY
670//-----------------------------------------------------------------------------
671
672#define STREAM_LIFECYCLE_CASES(X) \
673X(CASE_STREAM_LIFECYCLE_OUT_NULL) \
674X(CASE_STREAM_LIFECYCLE_VTBL_NULL) \
675X(CASE_STREAM_LIFECYCLE_ENV_NULL) \
676X(CASE_STREAM_LIFECYCLE_ENV_MEM_NULL) \
677X(CASE_STREAM_LIFECYCLE_OOM) \
678X(CASE_STREAM_LIFECYCLE_DESTROY_IDEMPOTENT) \
679X(CASE_STREAM_LIFECYCLE_OK) \
680X(CASE_STREAM_LIFECYCLE_VTBL_READ_NULL)
681
682#define STREAM_MAKE_LIFECYCLE_TEST(case_sym) \
683LEXLEO_MAKE_TEST(stream_lifecycle, case_sym)
684
685static const struct CMUnitTest stream_lifecycle_tests[] = {
686 STREAM_LIFECYCLE_CASES(STREAM_MAKE_LIFECYCLE_TEST)
687};
688
689#undef STREAM_LIFECYCLE_CASES
690#undef STREAM_MAKE_LIFECYCLE_TEST
691
820
829typedef struct {
830 const char *name;
831 stream_read_scenario_t scenario;
832} test_stream_read_case_t;
833
844typedef struct {
845 stream_t *stream;
846 stream_t *stream_no_backend;
847 stream_env_t stream_env;
848 fake_stream_backend_t fake_backend;
849 uint8_t buf[32];
851 const test_stream_read_case_t *tc;
852} test_stream_read_fixture_t;
853
854//-----------------------------------------------------------------------------
855// FIXTURES
856//-----------------------------------------------------------------------------
857
861static int setup_stream_read(void **state) {
862 const test_stream_read_case_t *tc = (const test_stream_read_case_t *) *state;
863 test_stream_read_fixture_t *fx = (test_stream_read_fixture_t *)malloc(sizeof(*fx));
864 if (!fx) return -1;
865 osal_memset(fx, 0, sizeof(*fx));
866 fx->tc = tc;
867
868 fake_memory_reset();
869
870 fx->stream_env.mem = osal_mem_test_fake_ops();
871
872 fx->fake_backend.close_ret = STREAM_STATUS_OK;
873
874 assert_int_equal(
875 stream_create(&fx->stream, &fake_stream_vtbl, &fx->fake_backend, &fx->stream_env),
877 );
878
879 assert_int_equal(
880 stream_create(&fx->stream_no_backend, &fake_stream_vtbl, NULL, &fx->stream_env),
882 );
883
884 *state = fx;
885 return 0;
886}
887
891static int teardown_stream_read(void **state) {
892 test_stream_read_fixture_t *fx = (test_stream_read_fixture_t *)*state;
893 if (!fx) return 0;
894
895 stream_destroy(&fx->stream);
896 stream_destroy(&fx->stream_no_backend);
897
898 assert_true(fake_memory_no_leak());
899 assert_true(fake_memory_no_invalid_free());
900 assert_true(fake_memory_no_double_free());
901
902 free(fx);
903
904 return 0;
905}
906
907//-----------------------------------------------------------------------------
908// TEST
909//-----------------------------------------------------------------------------
910
914static void test_stream_read(void **state) {
915 test_stream_read_fixture_t *fx = (test_stream_read_fixture_t *)*state;
916
917 // ARRANGE
918 size_t ret = (size_t)-1;
919 stream_t *s_arg = fx->stream;
920 void *buf_arg = fx->buf;
921 size_t n_arg = 5;
922 stream_status_t *st_arg = &fx->st;
923
924 // poison status so tests are meaningful
925 *st_arg = (stream_status_t)-1;
926
927 fake_stream_backend_reset(&fx->fake_backend);
928
929 switch (fx->tc->scenario) {
930 case STREAM_READ_SCENARIO_N_ZERO: n_arg = 0; break;
931 case STREAM_READ_SCENARIO_N_ZERO_ST_NULL: n_arg = 0; st_arg = NULL; break;
932 case STREAM_READ_SCENARIO_S_NULL: s_arg = NULL; break;
933 case STREAM_READ_SCENARIO_S_NULL_ST_NULL: s_arg = NULL; st_arg = NULL; break;
934 case STREAM_READ_SCENARIO_BUF_NULL: buf_arg = NULL; break;
935 case STREAM_READ_SCENARIO_BUF_NULL_ST_NULL: buf_arg = NULL; st_arg = NULL; break;
936 case STREAM_READ_SCENARIO_BACKEND_NULL: s_arg = fx->stream_no_backend; break;
937 case STREAM_READ_SCENARIO_BACKEND_NULL_ST_NULL: s_arg = fx->stream_no_backend; st_arg = NULL; break;
939 fx->fake_backend.read_ret = n_arg;
940 fx->fake_backend.read_st_to_set = STREAM_STATUS_OK;
941 break;
943 fx->fake_backend.read_ret = 0;
944 fx->fake_backend.read_st_to_set = STREAM_STATUS_EOF;
945 break;
947 fx->fake_backend.read_ret = 5;
948 st_arg = NULL;
949 break;
950 default: fail();
951 }
952
953 // ACT
954 ret = stream_read(s_arg, buf_arg, n_arg, st_arg);
955
956 // ASSERT
957 switch (fx->tc->scenario) {
959 assert_int_equal((int)ret, 0);
960 assert_int_equal(*st_arg, STREAM_STATUS_OK);
961 assert_int_equal(fx->fake_backend.read_called, 0);
962 break;
964 assert_int_equal((int)ret, 0);
965 assert_int_equal(fx->fake_backend.read_called, 0);
966 break;
968 assert_int_equal((int)ret, 0);
969 assert_int_equal(*st_arg, STREAM_STATUS_INVALID);
970 assert_int_equal(fx->fake_backend.read_called, 0);
971 break;
973 assert_int_equal((int)ret, 0);
974 assert_int_equal(fx->fake_backend.read_called, 0);
975 break;
977 assert_int_equal((int)ret, 0);
978 assert_int_equal(*st_arg, STREAM_STATUS_INVALID);
979 assert_int_equal(fx->fake_backend.read_called, 0);
980 break;
982 assert_int_equal((int)ret, 0);
983 assert_int_equal(fx->fake_backend.read_called, 0);
984 break;
986 assert_int_equal((int)ret, 0);
987 assert_int_equal(*st_arg, STREAM_STATUS_NO_BACKEND);
988 assert_int_equal(fx->fake_backend.read_called, 0);
989 break;
991 assert_int_equal((int)ret, 0);
992 assert_int_equal(fx->fake_backend.read_called, 0);
993 break;
995 assert_int_equal((int)ret, (int)fx->fake_backend.read_ret);
996 assert_int_equal(*st_arg, STREAM_STATUS_OK);
997 assert_int_equal(fx->fake_backend.read_called, 1);
998 assert_ptr_equal(fx->fake_backend.last_backend, &fx->fake_backend);
999 assert_ptr_equal(fx->fake_backend.last_read_buf, buf_arg);
1000 assert_int_equal((int)fx->fake_backend.last_read_n, (int)n_arg);
1001 assert_ptr_equal(fx->fake_backend.last_read_st, st_arg);
1002 break;
1004 assert_int_equal((int)ret, (int)fx->fake_backend.read_ret);
1005 assert_int_equal(*st_arg, STREAM_STATUS_EOF);
1006 assert_int_equal(fx->fake_backend.read_called, 1);
1007 assert_ptr_equal(fx->fake_backend.last_backend, &fx->fake_backend);
1008 assert_ptr_equal(fx->fake_backend.last_read_buf, buf_arg);
1009 assert_int_equal((int)fx->fake_backend.last_read_n, (int)n_arg);
1010 assert_ptr_equal(fx->fake_backend.last_read_st, st_arg);
1011 break;
1013 assert_int_equal((int)ret, (int)fx->fake_backend.read_ret);
1014 assert_int_equal(fx->fake_backend.read_called, 1);
1015 assert_ptr_equal(fx->fake_backend.last_backend, &fx->fake_backend);
1016 assert_ptr_equal(fx->fake_backend.last_read_buf, buf_arg);
1017 assert_int_equal((int)fx->fake_backend.last_read_n, (int)n_arg);
1018 assert_ptr_equal(fx->fake_backend.last_read_st, NULL);
1019 break;
1020 default: fail();
1021 }
1022
1023 assert_int_equal(fx->fake_backend.write_called, 0);
1024 assert_int_equal(fx->fake_backend.flush_called, 0);
1025 assert_int_equal(fx->fake_backend.close_called, 0);
1026}
1027
1028//-----------------------------------------------------------------------------
1029// CASES
1030//-----------------------------------------------------------------------------
1031
1032static const test_stream_read_case_t CASE_STREAM_READ_N_ZERO = {
1033 .name = "stream_read_n_zero",
1034 .scenario = STREAM_READ_SCENARIO_N_ZERO,
1035};
1036
1037static const test_stream_read_case_t CASE_STREAM_READ_N_ZERO_ST_NULL = {
1038 .name = "stream_read_n_zero_st_null",
1040};
1041
1042static const test_stream_read_case_t CASE_STREAM_READ_S_NULL = {
1043 .name = "stream_read_s_null",
1044 .scenario = STREAM_READ_SCENARIO_S_NULL,
1045};
1046
1047static const test_stream_read_case_t CASE_STREAM_READ_S_NULL_ST_NULL = {
1048 .name = "stream_read_s_null_st_null",
1050};
1051
1052static const test_stream_read_case_t CASE_STREAM_READ_BUF_NULL = {
1053 .name = "stream_read_buf_null",
1055};
1056
1057static const test_stream_read_case_t CASE_STREAM_READ_BUF_NULL_ST_NULL = {
1058 .name = "stream_read_buf_null_st_null",
1060};
1061
1062static const test_stream_read_case_t CASE_STREAM_READ_BACKEND_NULL = {
1063 .name = "stream_read_backend_null",
1065};
1066
1067static const test_stream_read_case_t CASE_STREAM_READ_BACKEND_NULL_ST_NULL = {
1068 .name = "stream_read_backend_null_st_null",
1070};
1071
1072static const test_stream_read_case_t CASE_STREAM_READ_NOMINAL_OK = {
1073 .name = "stream_read_nominal_ok",
1075};
1076
1077static const test_stream_read_case_t CASE_STREAM_READ_NOMINAL_EOF = {
1078 .name = "stream_read_nominal_eof",
1080};
1081
1082static const test_stream_read_case_t CASE_STREAM_READ_NOMINAL_ST_NULL = {
1083 .name = "stream_read_nominal_st_null",
1085};
1086
1087//-----------------------------------------------------------------------------
1088// CASES REGISTRY
1089//-----------------------------------------------------------------------------
1090
1091#define STREAM_READ_CASES(X) \
1092X(CASE_STREAM_READ_N_ZERO) \
1093X(CASE_STREAM_READ_N_ZERO_ST_NULL) \
1094X(CASE_STREAM_READ_S_NULL) \
1095X(CASE_STREAM_READ_S_NULL_ST_NULL) \
1096X(CASE_STREAM_READ_BUF_NULL) \
1097X(CASE_STREAM_READ_BUF_NULL_ST_NULL) \
1098X(CASE_STREAM_READ_BACKEND_NULL) \
1099X(CASE_STREAM_READ_BACKEND_NULL_ST_NULL) \
1100X(CASE_STREAM_READ_NOMINAL_OK) \
1101X(CASE_STREAM_READ_NOMINAL_EOF) \
1102X(CASE_STREAM_READ_NOMINAL_ST_NULL)
1103
1104#define STREAM_MAKE_STREAM_READ_TEST(case_sym) \
1105LEXLEO_MAKE_TEST(stream_read, case_sym)
1106
1107static const struct CMUnitTest stream_read_tests[] = {
1108 STREAM_READ_CASES(STREAM_MAKE_STREAM_READ_TEST)
1109};
1110
1111#undef STREAM_READ_CASES
1112#undef STREAM_MAKE_STREAM_READ_TEST
1113
1240
1249typedef struct {
1250 const char *name;
1251 stream_write_scenario_t scenario;
1252} test_stream_write_case_t;
1253
1264typedef struct {
1265 stream_t *stream;
1266 stream_t *stream_no_backend;
1267 stream_env_t stream_env;
1268 fake_stream_backend_t fake_backend;
1269 uint8_t buf[32];
1270 stream_status_t st;
1271 const test_stream_write_case_t *tc;
1272} test_stream_write_fixture_t;
1273
1274//-----------------------------------------------------------------------------
1275// FIXTURES
1276//-----------------------------------------------------------------------------
1277
1281static int setup_stream_write(void **state) {
1282 const test_stream_write_case_t *tc = (const test_stream_write_case_t *) *state;
1283 test_stream_write_fixture_t *fx = (test_stream_write_fixture_t *)malloc(sizeof(*fx));
1284 if (!fx) return -1;
1285 osal_memset(fx, 0, sizeof(*fx));
1286 fx->tc = tc;
1287
1288 fake_memory_reset();
1289
1290 fx->stream_env.mem = osal_mem_test_fake_ops();
1291
1292 fx->fake_backend.close_ret = STREAM_STATUS_OK;
1293
1294 assert_int_equal(
1295 stream_create(&fx->stream, &fake_stream_vtbl, &fx->fake_backend, &fx->stream_env),
1297 );
1298
1299 assert_int_equal(
1300 stream_create(&fx->stream_no_backend, &fake_stream_vtbl, NULL, &fx->stream_env),
1302 );
1303
1304 *state = fx;
1305 return 0;
1306}
1307
1311static int teardown_stream_write(void **state) {
1312 test_stream_write_fixture_t *fx = (test_stream_write_fixture_t *)*state;
1313 if (!fx) return 0;
1314
1315 stream_destroy(&fx->stream);
1316 stream_destroy(&fx->stream_no_backend);
1317
1318 assert_true(fake_memory_no_leak());
1319 assert_true(fake_memory_no_invalid_free());
1320 assert_true(fake_memory_no_double_free());
1321
1322 free(fx);
1323
1324 return 0;
1325}
1326
1327//-----------------------------------------------------------------------------
1328// TEST
1329//-----------------------------------------------------------------------------
1330
1334static void test_stream_write(void **state) {
1335 test_stream_write_fixture_t *fx = (test_stream_write_fixture_t *)*state;
1336
1337 // ARRANGE
1338 size_t ret = (size_t)-1;
1339 stream_t *s_arg = fx->stream;
1340 const void *buf_arg = fx->buf;
1341 size_t n_arg = 5;
1342 stream_status_t *st_arg = &fx->st;
1343
1344 // poison status so tests are meaningful
1345 *st_arg = (stream_status_t)-1;
1346
1347 fake_stream_backend_reset(&fx->fake_backend);
1348
1349 switch (fx->tc->scenario) {
1350 case STREAM_WRITE_SCENARIO_N_ZERO: n_arg = 0; break;
1351 case STREAM_WRITE_SCENARIO_N_ZERO_ST_NULL: n_arg = 0; st_arg = NULL; break;
1352 case STREAM_WRITE_SCENARIO_S_NULL: s_arg = NULL; break;
1353 case STREAM_WRITE_SCENARIO_S_NULL_ST_NULL: s_arg = NULL; st_arg = NULL; break;
1354 case STREAM_WRITE_SCENARIO_BUF_NULL: buf_arg = NULL; break;
1355 case STREAM_WRITE_SCENARIO_BUF_NULL_ST_NULL: buf_arg = NULL; st_arg = NULL; break;
1356 case STREAM_WRITE_SCENARIO_BACKEND_NULL: s_arg = fx->stream_no_backend; break;
1357 case STREAM_WRITE_SCENARIO_BACKEND_NULL_ST_NULL: s_arg = fx->stream_no_backend; st_arg = NULL; break;
1359 fx->fake_backend.write_ret = n_arg;
1360 fx->fake_backend.write_st_to_set = STREAM_STATUS_OK;
1361 break;
1363 fx->fake_backend.write_ret = 0;
1364 fx->fake_backend.write_st_to_set = STREAM_STATUS_IO_ERROR;
1365 break;
1367 fx->fake_backend.write_ret = 5;
1368 st_arg = NULL;
1369 break;
1370 default: fail();
1371 }
1372
1373 // ACT
1374 ret = stream_write(s_arg, buf_arg, n_arg, st_arg);
1375
1376 // ASSERT
1377 switch (fx->tc->scenario) {
1379 assert_int_equal((int)ret, 0);
1380 assert_int_equal(*st_arg, STREAM_STATUS_OK);
1381 assert_int_equal(fx->fake_backend.write_called, 0);
1382 break;
1384 assert_int_equal((int)ret, 0);
1385 assert_int_equal(fx->fake_backend.write_called, 0);
1386 break;
1388 assert_int_equal((int)ret, 0);
1389 assert_int_equal(*st_arg, STREAM_STATUS_INVALID);
1390 assert_int_equal(fx->fake_backend.write_called, 0);
1391 break;
1393 assert_int_equal((int)ret, 0);
1394 assert_int_equal(fx->fake_backend.write_called, 0);
1395 break;
1397 assert_int_equal((int)ret, 0);
1398 assert_int_equal(*st_arg, STREAM_STATUS_INVALID);
1399 assert_int_equal(fx->fake_backend.write_called, 0);
1400 break;
1402 assert_int_equal((int)ret, 0);
1403 assert_int_equal(fx->fake_backend.write_called, 0);
1404 break;
1406 assert_int_equal((int)ret, 0);
1407 assert_int_equal(*st_arg, STREAM_STATUS_NO_BACKEND);
1408 assert_int_equal(fx->fake_backend.write_called, 0);
1409 break;
1411 assert_int_equal((int)ret, 0);
1412 assert_int_equal(fx->fake_backend.write_called, 0);
1413 break;
1415 assert_int_equal((int)ret, (int)fx->fake_backend.write_ret);
1416 assert_int_equal(*st_arg, STREAM_STATUS_OK);
1417 assert_int_equal(fx->fake_backend.write_called, 1);
1418 assert_ptr_equal(fx->fake_backend.last_backend, &fx->fake_backend);
1419 assert_ptr_equal(fx->fake_backend.last_write_buf, buf_arg);
1420 assert_int_equal((int)fx->fake_backend.last_write_n, (int)n_arg);
1421 assert_ptr_equal(fx->fake_backend.last_write_st, st_arg);
1422 break;
1424 assert_int_equal((int)ret, (int)fx->fake_backend.write_ret);
1425 assert_int_equal(*st_arg, STREAM_STATUS_IO_ERROR);
1426 assert_int_equal(fx->fake_backend.write_called, 1);
1427 assert_ptr_equal(fx->fake_backend.last_backend, &fx->fake_backend);
1428 assert_ptr_equal(fx->fake_backend.last_write_buf, buf_arg);
1429 assert_int_equal((int)fx->fake_backend.last_write_n, (int)n_arg);
1430 assert_ptr_equal(fx->fake_backend.last_write_st, st_arg);
1431 break;
1433 assert_int_equal((int)ret, (int)fx->fake_backend.write_ret);
1434 assert_int_equal(fx->fake_backend.write_called, 1);
1435 assert_ptr_equal(fx->fake_backend.last_backend, &fx->fake_backend);
1436 assert_ptr_equal(fx->fake_backend.last_write_buf, buf_arg);
1437 assert_int_equal((int)fx->fake_backend.last_write_n, (int)n_arg);
1438 assert_ptr_equal(fx->fake_backend.last_write_st, NULL);
1439 break;
1440 default: fail();
1441 }
1442
1443 assert_int_equal(fx->fake_backend.read_called, 0);
1444 assert_int_equal(fx->fake_backend.flush_called, 0);
1445 assert_int_equal(fx->fake_backend.close_called, 0);
1446}
1447
1448//-----------------------------------------------------------------------------
1449// CASES
1450//-----------------------------------------------------------------------------
1451
1452static const test_stream_write_case_t CASE_STREAM_WRITE_N_ZERO = {
1453 .name = "stream_write_n_zero",
1454 .scenario = STREAM_WRITE_SCENARIO_N_ZERO,
1455};
1456
1457static const test_stream_write_case_t CASE_STREAM_WRITE_N_ZERO_ST_NULL = {
1458 .name = "stream_write_n_zero_st_null",
1460};
1461
1462static const test_stream_write_case_t CASE_STREAM_WRITE_S_NULL = {
1463 .name = "stream_write_s_null",
1464 .scenario = STREAM_WRITE_SCENARIO_S_NULL,
1465};
1466
1467static const test_stream_write_case_t CASE_STREAM_WRITE_S_NULL_ST_NULL = {
1468 .name = "stream_write_s_null_st_null",
1470};
1471
1472static const test_stream_write_case_t CASE_STREAM_WRITE_BUF_NULL = {
1473 .name = "stream_write_buf_null",
1475};
1476
1477static const test_stream_write_case_t CASE_STREAM_WRITE_BUF_NULL_ST_NULL = {
1478 .name = "stream_write_buf_null_st_null",
1480};
1481
1482static const test_stream_write_case_t CASE_STREAM_WRITE_BACKEND_NULL = {
1483 .name = "stream_write_backend_null",
1485};
1486
1487static const test_stream_write_case_t CASE_STREAM_WRITE_BACKEND_NULL_ST_NULL = {
1488 .name = "stream_write_backend_null_st_null",
1490};
1491
1492static const test_stream_write_case_t CASE_STREAM_WRITE_NOMINAL_OK = {
1493 .name = "stream_write_nominal_ok",
1495};
1496
1497static const test_stream_write_case_t CASE_STREAM_WRITE_NOMINAL_IO_ERROR = {
1498 .name = "stream_write_nominal_io_error",
1500};
1501
1502static const test_stream_write_case_t CASE_STREAM_WRITE_NOMINAL_ST_NULL = {
1503 .name = "stream_write_nominal_st_null",
1505};
1506
1507//-----------------------------------------------------------------------------
1508// CASES REGISTRY
1509//-----------------------------------------------------------------------------
1510
1511#define STREAM_WRITE_CASES(X) \
1512X(CASE_STREAM_WRITE_N_ZERO) \
1513X(CASE_STREAM_WRITE_N_ZERO_ST_NULL) \
1514X(CASE_STREAM_WRITE_S_NULL) \
1515X(CASE_STREAM_WRITE_S_NULL_ST_NULL) \
1516X(CASE_STREAM_WRITE_BUF_NULL) \
1517X(CASE_STREAM_WRITE_BUF_NULL_ST_NULL) \
1518X(CASE_STREAM_WRITE_BACKEND_NULL) \
1519X(CASE_STREAM_WRITE_BACKEND_NULL_ST_NULL) \
1520X(CASE_STREAM_WRITE_NOMINAL_OK) \
1521X(CASE_STREAM_WRITE_NOMINAL_IO_ERROR) \
1522X(CASE_STREAM_WRITE_NOMINAL_ST_NULL)
1523
1524#define STREAM_MAKE_STREAM_WRITE_TEST(case_sym) \
1525LEXLEO_MAKE_TEST(stream_write, case_sym)
1526
1527static const struct CMUnitTest stream_write_tests[] = {
1528 STREAM_WRITE_CASES(STREAM_MAKE_STREAM_WRITE_TEST)
1529};
1530
1531#undef STREAM_WRITE_CASES
1532#undef STREAM_MAKE_STREAM_WRITE_TEST
1533
1593
1602typedef struct {
1603 const char *name;
1604 stream_flush_scenario_t scenario;
1605} test_stream_flush_case_t;
1606
1616typedef struct {
1617 stream_t *stream;
1618 stream_t *stream_no_backend;
1619 stream_env_t env;
1620 fake_stream_backend_t backend;
1621 const test_stream_flush_case_t *tc;
1622} test_stream_flush_fixture_t;
1623
1624//-----------------------------------------------------------------------------
1625// FIXTURES
1626//-----------------------------------------------------------------------------
1627
1631static int setup_stream_flush(void **state)
1632{
1633 const test_stream_flush_case_t *tc = (const test_stream_flush_case_t *)(*state);
1634
1635 test_stream_flush_fixture_t *fx = (test_stream_flush_fixture_t *)malloc(sizeof(*fx));
1636 if (!fx) return -1;
1637
1638 osal_memset(fx, 0, sizeof(*fx));
1639 fx->tc = tc;
1640
1641 fake_memory_reset();
1642
1643 fx->env.mem = osal_mem_test_fake_ops();
1644
1645 fake_stream_backend_reset(&fx->backend);
1646 fx->backend.close_ret = STREAM_STATUS_OK;
1647
1648 assert_int_equal(
1649 stream_create(&fx->stream, &fake_stream_vtbl, &fx->backend, &fx->env),
1651 );
1652
1653 assert_int_equal(
1654 stream_create(&fx->stream_no_backend, &fake_stream_vtbl, NULL, &fx->env),
1656 );
1657
1658 *state = fx;
1659 return 0;
1660}
1661
1665static int teardown_stream_flush(void **state)
1666{
1667 test_stream_flush_fixture_t *fx = (test_stream_flush_fixture_t *)(*state);
1668 if (!fx) return 0;
1669
1670 stream_destroy(&fx->stream);
1671 stream_destroy(&fx->stream_no_backend);
1672
1673 assert_true(fake_memory_no_leak());
1674 assert_true(fake_memory_no_invalid_free());
1675 assert_true(fake_memory_no_double_free());
1676
1677 free(fx);
1678 return 0;
1679}
1680
1681//-----------------------------------------------------------------------------
1682// TEST
1683//-----------------------------------------------------------------------------
1684
1688static void test_stream_flush(void **state)
1689{
1690 test_stream_flush_fixture_t *fx = (test_stream_flush_fixture_t *)*state;
1691
1692 stream_t *s_arg = fx->stream;
1693
1694 fake_stream_backend_reset(&fx->backend);
1695
1696 switch (fx->tc->scenario) {
1698 s_arg = NULL;
1699 break;
1701 s_arg = fx->stream_no_backend;
1702 break;
1704 fx->backend.flush_ret = STREAM_STATUS_OK;
1705 break;
1707 fx->backend.flush_ret = STREAM_STATUS_IO_ERROR;
1708 break;
1709 default:
1710 fail();
1711 }
1712
1713 stream_status_t ret = stream_flush(s_arg);
1714
1715 switch (fx->tc->scenario) {
1717 assert_int_equal(ret, STREAM_STATUS_INVALID);
1718 assert_int_equal(fx->backend.flush_called, 0);
1719 break;
1721 assert_int_equal(ret, STREAM_STATUS_NO_BACKEND);
1722 assert_int_equal(fx->backend.flush_called, 0);
1723 break;
1725 assert_int_equal(ret, STREAM_STATUS_OK);
1726 assert_int_equal(fx->backend.flush_called, 1);
1727 assert_ptr_equal(fx->backend.last_backend, &fx->backend);
1728 break;
1730 assert_int_equal(ret, STREAM_STATUS_IO_ERROR);
1731 assert_int_equal(fx->backend.flush_called, 1);
1732 assert_ptr_equal(fx->backend.last_backend, &fx->backend);
1733 break;
1734 default:
1735 fail();
1736 }
1737
1738 assert_int_equal(fx->backend.read_called, 0);
1739 assert_int_equal(fx->backend.write_called, 0);
1740 assert_int_equal(fx->backend.close_called, 0);
1741}
1742
1743//-----------------------------------------------------------------------------
1744// CASES
1745//-----------------------------------------------------------------------------
1746
1747static const test_stream_flush_case_t CASE_STREAM_FLUSH_S_NULL = {
1748 .name = "stream_flush_s_null",
1749 .scenario = STREAM_FLUSH_SCENARIO_S_NULL,
1750};
1751
1752static const test_stream_flush_case_t CASE_STREAM_FLUSH_BACKEND_NULL = {
1753 .name = "stream_flush_backend_null",
1755};
1756
1757static const test_stream_flush_case_t CASE_STREAM_FLUSH_NOMINAL_OK = {
1758 .name = "stream_flush_nominal_ok",
1760};
1761
1762static const test_stream_flush_case_t CASE_STREAM_FLUSH_NOMINAL_IO_ERROR = {
1763 .name = "stream_flush_nominal_io_error",
1765};
1766
1767//-----------------------------------------------------------------------------
1768// CASES REGISTRY
1769//-----------------------------------------------------------------------------
1770
1771#define STREAM_FLUSH_CASES(X) \
1772X(CASE_STREAM_FLUSH_S_NULL) \
1773X(CASE_STREAM_FLUSH_BACKEND_NULL) \
1774X(CASE_STREAM_FLUSH_NOMINAL_OK) \
1775X(CASE_STREAM_FLUSH_NOMINAL_IO_ERROR)
1776
1777#define STREAM_MAKE_STREAM_FLUSH_TEST(case_sym) \
1778LEXLEO_MAKE_TEST(stream_flush, case_sym)
1779
1780static const struct CMUnitTest stream_flush_tests[] = {
1781 STREAM_FLUSH_CASES(STREAM_MAKE_STREAM_FLUSH_TEST)
1782};
1783
1784#undef STREAM_FLUSH_CASES
1785#undef STREAM_MAKE_STREAM_FLUSH_TEST
1786
1787//-----------------------------------------------------------------------------
1788// MAIN
1789//-----------------------------------------------------------------------------
1790
1791int main(void) {
1792 static const struct CMUnitTest stream_tests[] = {
1793 cmocka_unit_test(test_stream_default_ops),
1794 cmocka_unit_test(test_stream_default_env)
1795 };
1796
1797 int failed = 0;
1798 failed += cmocka_run_group_tests(stream_tests, NULL, NULL);
1799 failed += cmocka_run_group_tests(stream_lifecycle_tests, NULL, NULL);
1800 failed += cmocka_run_group_tests(stream_read_tests, NULL, NULL);
1801 failed += cmocka_run_group_tests(stream_write_tests, NULL, NULL);
1802 failed += cmocka_run_group_tests(stream_flush_tests, NULL, NULL);
1803 return failed;
1804}
1805
int main()
Definition main.c:5
void * osal_memset(void *s, int c, size_t n)
Definition osal_mem.c:30
const osal_mem_ops_t * osal_mem_test_fake_ops(void)
Borrower-facing runtime operations for the stream port.
stream_status_t stream_flush(stream_t *s)
Flush a stream.
Definition stream.c:62
size_t stream_write(stream_t *s, const void *buf, size_t n, stream_status_t *st)
Write bytes to a stream.
Definition stream.c:38
size_t stream_read(stream_t *s, void *buf, size_t n, stream_status_t *st)
Read bytes from a stream.
Definition stream.c:19
Adapter-side API for constructing and binding stream_t objects.
stream_status_t stream_create(stream_t **out, const stream_vtbl_t *vtbl, void *backend, const stream_env_t *env)
Create a generic stream handle from adapter-provided backend bindings.
Definition stream.c:69
Composition Root helpers for the stream port.
const stream_ops_t * stream_default_ops(void)
Return the default borrower-facing ops table for the stream port.
Definition stream.c:123
stream_env_t stream_default_env(const osal_mem_ops_t *mem_ops)
Build a default stream_env_t from injected memory operations.
Definition stream.c:132
Lifecycle services for stream_t handles.
void stream_destroy(stream_t **s)
Destroy a stream handle.
Definition stream.c:98
stream_status_t
Public status codes used by the stream port.
@ STREAM_STATUS_INVALID
@ STREAM_STATUS_EOF
@ STREAM_STATUS_IO_ERROR
@ STREAM_STATUS_OK
@ STREAM_STATUS_OOM
@ STREAM_STATUS_NO_BACKEND
Runtime environment for the stream port.
Definition stream_env.h:35
const osal_mem_ops_t * mem
Memory operations used by the stream port.
Definition stream_env.h:43
Borrower-facing operation table for the stream port.
stream_status_t(* flush)(stream_t *s)
Flush a stream.
size_t(* read)(stream_t *s, void *buf, size_t n, stream_status_t *st)
Read bytes from a stream.
size_t(* write)(stream_t *s, const void *buf, size_t n, stream_status_t *st)
Write bytes to a stream.
Private handle structure for a stream_t.
void * backend
Adapter dispatch table bound to a stream_t instance.
stream_read_fn_t read
stream_lifecycle_scenario_t
Scenarios for stream_create() / stream_destroy().
@ STREAM_LIFECYCLE_SCENARIO_VTBL_NULL
@ STREAM_LIFECYCLE_SCENARIO_OOM
@ STREAM_LIFECYCLE_SCENARIO_ENV_MEM_NULL
@ STREAM_LIFECYCLE_SCENARIO_ENV_NULL
@ STREAM_LIFECYCLE_SCENARIO_OUT_NULL
@ STREAM_LIFECYCLE_SCENARIO_DESTROY_IDEMPOTENT
@ STREAM_LIFECYCLE_SCENARIO_VTBL_READ_NULL
@ STREAM_LIFECYCLE_SCENARIO_OK
stream_flush_scenario_t
Scenarios for stream_flush().
@ STREAM_FLUSH_SCENARIO_NOMINAL_IO_ERROR
@ STREAM_FLUSH_SCENARIO_BACKEND_NULL
@ STREAM_FLUSH_SCENARIO_S_NULL
@ STREAM_FLUSH_SCENARIO_NOMINAL_OK
static void test_stream_default_ops(void **state)
Test stream_default_ops().
static void test_stream_default_env(void **state)
Test stream_default_env().
stream_write_scenario_t
Scenarios for stream_write().
@ STREAM_WRITE_SCENARIO_NOMINAL_IO_ERROR
@ STREAM_WRITE_SCENARIO_N_ZERO
@ STREAM_WRITE_SCENARIO_BUF_NULL
@ STREAM_WRITE_SCENARIO_NOMINAL_ST_NULL
@ STREAM_WRITE_SCENARIO_S_NULL
@ STREAM_WRITE_SCENARIO_S_NULL_ST_NULL
@ STREAM_WRITE_SCENARIO_NOMINAL_OK
@ STREAM_WRITE_SCENARIO_BUF_NULL_ST_NULL
@ STREAM_WRITE_SCENARIO_N_ZERO_ST_NULL
@ STREAM_WRITE_SCENARIO_BACKEND_NULL
@ STREAM_WRITE_SCENARIO_BACKEND_NULL_ST_NULL
stream_read_scenario_t
Scenarios for stream_read().
@ STREAM_READ_SCENARIO_NOMINAL_ST_NULL
@ STREAM_READ_SCENARIO_BUF_NULL
@ STREAM_READ_SCENARIO_BACKEND_NULL
@ STREAM_READ_SCENARIO_S_NULL
@ STREAM_READ_SCENARIO_N_ZERO_ST_NULL
@ STREAM_READ_SCENARIO_BACKEND_NULL_ST_NULL
@ STREAM_READ_SCENARIO_N_ZERO
@ STREAM_READ_SCENARIO_NOMINAL_EOF
@ STREAM_READ_SCENARIO_S_NULL_ST_NULL
@ STREAM_READ_SCENARIO_NOMINAL_OK
@ STREAM_READ_SCENARIO_BUF_NULL_ST_NULL