LexLeo 0.0.0-dev+f8e5087-dirty
Technical documentation
Loading...
Searching...
No Matches
unit_tests_logger_default.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
28
32
35
36#include "osal/mem/osal_mem.h"
38
40
43
44#include "lexleo_cmocka.h"
45
62static void test_logger_default_default_cfg(void **state)
63{
64 (void)state;
65
67
68 assert_true(ret.append_newline);
69}
70
99static void test_logger_default_default_env(void **state) {
100 (void)state;
101
102 stream_t *dummy_stream_p = (stream_t *)(uintptr_t)0x1234u;
103
104 const osal_time_ops_t *dummy_time_ops =
105 (const osal_time_ops_t *)(uintptr_t)0x4567u;
106
107 const osal_mem_ops_t *dummy_adapter_mem_p =
108 (const osal_mem_ops_t *)(uintptr_t)0x5678u;
109
110 const logger_env_t dummy_port_env = {0};
111 const logger_env_t *dummy_port_env_p = &dummy_port_env;
112
115 dummy_stream_p,
116 dummy_time_ops,
117 dummy_adapter_mem_p,
118 dummy_port_env_p);
119
120 assert_ptr_equal(ret.stream, dummy_stream_p);
121 assert_ptr_equal(ret.time_ops, dummy_time_ops);
122 assert_ptr_equal(ret.adapter_mem, dummy_adapter_mem_p);
123 assert_memory_equal(&ret.port_env, dummy_port_env_p, sizeof(ret.port_env));
124}
125
193
196typedef enum {
197 OUT_CHECK_NONE,
198 OUT_EXPECT_NULL,
199 OUT_EXPECT_NON_NULL,
200 OUT_EXPECT_UNCHANGED
201} out_expect_t;
202
203typedef struct {
204 const char *name;
205
206 // arrange
208 size_t fail_call_idx; // 0 = no OOM; otherwise 1-based allocation index
209
210 // assert
211 logger_status_t expected_ret;
212 out_expect_t out_expect;
213} test_logger_default_create_logger_case_t;
214
215typedef struct {
216 // runtime resources
217 logger_t *out;
218 stream_fake_t *fake_stream_adapter;
219 stream_t *fake_stream;
220
221 // injection
223
225
226 const test_logger_default_create_logger_case_t *tc;
227} test_logger_default_create_logger_fixture_t;
228
229//-----------------------------------------------------------------------------
230// FIXTURES
231//-----------------------------------------------------------------------------
232
233static int setup_logger_default_create_logger(void **state)
234{
235 const test_logger_default_create_logger_case_t *tc =
236 (const test_logger_default_create_logger_case_t *)(*state);
237
238 test_logger_default_create_logger_fixture_t *fx = malloc(sizeof(*fx));
239 if (!fx) return -1;
240 osal_memset(fx, 0, sizeof(*fx));
241
242 fake_memory_reset();
243 fake_time_reset();
244
245 fx->cfg.append_newline = true;
246
247 assert_int_equal(
248 stream_fake_create(&fx->fake_stream_adapter, &fx->fake_stream, osal_mem_test_fake_ops()),
250 );
251
252 stream_fake_reset(fx->fake_stream_adapter);
253
254 // DI
255 fx->env.stream = fx->fake_stream;
256 fx->env.time_ops = osal_time_test_fake_ops();
257 fx->env.adapter_mem = osal_mem_test_fake_ops();
258 fx->env.port_env.mem = osal_mem_test_fake_ops();
259
260 fx->tc = tc;
261
262 if (tc->scenario == LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_OOM) {
263 fake_memory_fail_only_on_call(tc->fail_call_idx);
264 }
265
266 *state = fx;
267
268 return 0;
269}
270
271static int teardown_logger_default_create_logger(void **state)
272{
273 test_logger_default_create_logger_fixture_t *fx =
274 (test_logger_default_create_logger_fixture_t *)(*state);
275
276 if (fx->out) {
277 logger_destroy(&fx->out);
278 fx->out = NULL;
279 }
280
281 stream_fake_destroy(&fx->fake_stream_adapter, &fx->fake_stream);
282
283 assert_true(fake_memory_no_leak());
284 assert_true(fake_memory_no_invalid_free());
285 assert_true(fake_memory_no_double_free());
286
287 free(fx);
288
289 return 0;
290}
291
292//-----------------------------------------------------------------------------
293// TEST
294//-----------------------------------------------------------------------------
295
296static void test_logger_default_create_logger(void **state)
297{
298 test_logger_default_create_logger_fixture_t *fx =
299 (test_logger_default_create_logger_fixture_t *)(*state);
300 const test_logger_default_create_logger_case_t *tc = fx->tc;
301
302 // ARRANGE
303 logger_status_t ret = (logger_status_t)-1; // poison
304
305 logger_t **out_arg = &fx->out;
306 const logger_default_cfg_t *cfg_arg = &fx->cfg;
307 const logger_default_env_t *env_arg = &fx->env;
308
309 // invalid args
310 if (tc->scenario == LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_OUT_NULL) out_arg = NULL;
311 if (tc->scenario == LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_CFG_NULL) cfg_arg = NULL;
312 if (tc->scenario == LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_ENV_NULL) env_arg = NULL;
313
314 // ensure OUT_EXPECT_UNCHANGED is meaningful
315 if (tc->out_expect == OUT_EXPECT_UNCHANGED && out_arg != NULL) {
316 fx->out = (logger_t *)(uintptr_t)0xDEADC0DEu; // sentinel
317 }
318
319 logger_t *out_arg_snapshot = fx->out;
320
321 // ACT
322 ret = logger_default_create_logger(out_arg, cfg_arg, env_arg);
323
324 // ASSERT
325 assert_int_equal(ret, tc->expected_ret);
326
327 switch (tc->out_expect) {
328 case OUT_CHECK_NONE: break;
329 case OUT_EXPECT_NULL: assert_null(fx->out); break;
330 case OUT_EXPECT_NON_NULL: assert_non_null(fx->out); break;
331 case OUT_EXPECT_UNCHANGED:
332 assert_ptr_equal(out_arg_snapshot, fx->out);
333 fx->out = NULL; // prevent teardown from destroying sentinel
334 break;
335 default: assert_true(false);
336 }
337
338 if (tc->scenario == LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_OK) {
339 assert_non_null(fx->out);
340 }
341}
342
343//-----------------------------------------------------------------------------
344// CASES
345//-----------------------------------------------------------------------------
346
347static const test_logger_default_create_logger_case_t CASE_LOGGER_DEFAULT_CREATE_LOGGER_OUT_NULL = {
348 .name = "logger_default_create_logger_out_null",
350 .fail_call_idx = 0,
351
352 .expected_ret = LOGGER_STATUS_INVALID,
353 .out_expect = OUT_CHECK_NONE
354};
355
356static const test_logger_default_create_logger_case_t CASE_LOGGER_DEFAULT_CREATE_LOGGER_CFG_NULL = {
357 .name = "logger_default_create_logger_cfg_null",
359 .fail_call_idx = 0,
360
361 .expected_ret = LOGGER_STATUS_INVALID,
362 .out_expect = OUT_EXPECT_UNCHANGED
363};
364
365static const test_logger_default_create_logger_case_t CASE_LOGGER_DEFAULT_CREATE_LOGGER_ENV_NULL = {
366 .name = "logger_default_create_logger_env_null",
368 .fail_call_idx = 0,
369
370 .expected_ret = LOGGER_STATUS_INVALID,
371 .out_expect = OUT_EXPECT_UNCHANGED
372};
373
374static const test_logger_default_create_logger_case_t CASE_LOGGER_DEFAULT_CREATE_LOGGER_OOM_1 = {
375 .name = "logger_default_create_logger_oom_1",
377 .fail_call_idx = 1,
378
379 .expected_ret = LOGGER_STATUS_OOM,
380 .out_expect = OUT_EXPECT_UNCHANGED
381};
382
383static const test_logger_default_create_logger_case_t CASE_LOGGER_DEFAULT_CREATE_LOGGER_OOM_2 = {
384 .name = "logger_default_create_logger_oom_2",
386 .fail_call_idx = 2,
387
388 .expected_ret = LOGGER_STATUS_OOM,
389 .out_expect = OUT_EXPECT_UNCHANGED
390};
391
392static const test_logger_default_create_logger_case_t CASE_LOGGER_DEFAULT_CREATE_LOGGER_OK = {
393 .name = "logger_default_create_logger_ok",
395 .fail_call_idx = 0,
396
397 .expected_ret = LOGGER_STATUS_OK,
398 .out_expect = OUT_EXPECT_NON_NULL
399};
400
401//-----------------------------------------------------------------------------
402// CASES REGISTRY
403//-----------------------------------------------------------------------------
404
405#define LOGGER_DEFAULT_CREATE_LOGGER_CASES(X) \
406X(CASE_LOGGER_DEFAULT_CREATE_LOGGER_OUT_NULL) \
407X(CASE_LOGGER_DEFAULT_CREATE_LOGGER_CFG_NULL) \
408X(CASE_LOGGER_DEFAULT_CREATE_LOGGER_ENV_NULL) \
409X(CASE_LOGGER_DEFAULT_CREATE_LOGGER_OOM_1) \
410X(CASE_LOGGER_DEFAULT_CREATE_LOGGER_OOM_2) \
411X(CASE_LOGGER_DEFAULT_CREATE_LOGGER_OK)
412
413#define LOGGER_DEFAULT_MAKE_CREATE_LOGGER_TEST(case_sym) \
414LEXLEO_MAKE_TEST(logger_default_create_logger, case_sym)
415
416static const struct CMUnitTest logger_default_create_logger_tests[] = {
417 LOGGER_DEFAULT_CREATE_LOGGER_CASES(LOGGER_DEFAULT_MAKE_CREATE_LOGGER_TEST)
418};
419
420#undef LOGGER_DEFAULT_CREATE_LOGGER_CASES
421#undef LOGGER_DEFAULT_MAKE_CREATE_LOGGER_TEST
422
456
459typedef struct {
460 const char *name;
461
462 // arrange
464 const char *message;
465 stream_status_t write_ret;
466 osal_time_status_t time_ops_status;
467
468 // assert
469 logger_status_t expected_ret;
470 bool write_call;
471 size_t expected_written_len;
472 const char *expected_written_message;
473} test_logger_default_log_case_t;
474
475typedef struct {
476 logger_t *logger;
477
480
481 stream_fake_t *fake_stream_adapter;
482 stream_t *fake_stream;
483
484 const test_logger_default_log_case_t *tc;
485} test_logger_default_log_fixture_t;
486
487//-----------------------------------------------------------------------------
488// FIXTURES
489//-----------------------------------------------------------------------------
490
491static int setup_logger_default_log(void **state)
492{
493 const test_logger_default_log_case_t *tc = *state;
494
495 test_logger_default_log_fixture_t *fx = malloc(sizeof(*fx));
496 if (!fx) return -1;
497 osal_memset(fx, 0, sizeof(*fx));
498
499 fake_memory_reset();
500 fake_time_reset();
501
502 fake_time_set_now_status(OSAL_TIME_STATUS_OK);
503 fake_time_set_now_out((osal_time_t){ .epoch_seconds = 0 });
504
505 assert_int_equal(
506 stream_fake_create(&fx->fake_stream_adapter, &fx->fake_stream, osal_mem_test_fake_ops()),
508 );
509
510 stream_fake_reset(fx->fake_stream_adapter);
511
512 fx->cfg.append_newline =
514
515 fx->env.stream = fx->fake_stream;
516 fx->env.time_ops = osal_time_test_fake_ops();
517 fx->env.adapter_mem = osal_mem_test_fake_ops();
518 fx->env.port_env.mem = osal_mem_test_fake_ops();
519
520 assert_int_equal(
521 logger_default_create_logger(&fx->logger, &fx->cfg, &fx->env),
523 );
524
525 fx->tc = tc;
526 *state = fx;
527
528 return 0;
529}
530
531static int teardown_logger_default_log(void **state)
532{
533 test_logger_default_log_fixture_t *fx = *state;
534
535 logger_destroy(&fx->logger);
536 stream_fake_destroy(&fx->fake_stream_adapter, &fx->fake_stream);
537
538 assert_true(fake_memory_no_leak());
539 assert_true(fake_memory_no_invalid_free());
540 assert_true(fake_memory_no_double_free());
541
542 free(fx);
543 return 0;
544}
545
546//-----------------------------------------------------------------------------
547// TEST
548//-----------------------------------------------------------------------------
549
550static void test_logger_default_log(void **state)
551{
552 test_logger_default_log_fixture_t *fx = *state;
553
555
556 if (fx->tc->scenario == LOGGER_DEFAULT_LOG_SCENARIO_STREAM_WRITE_FAIL) {
558 fx->fake_stream_adapter,
559 1,
560 fx->tc->write_ret
561 );
562 }
563
564 if (fx->tc->scenario == LOGGER_DEFAULT_LOG_SCENARIO_TIME_OPS_FAIL) {
565 fake_time_set_now_status(fx->tc->time_ops_status);
566 }
567
568 ret = logger_log(fx->logger, fx->tc->message);
569
570 assert_int_equal(ret, fx->tc->expected_ret);
571
572 assert_true(
573 fx->tc->write_call ==
574 (stream_fake_counters(fx->fake_stream_adapter)->write_calls > 0)
575 );
576
577 if (fx->tc->expected_written_message) {
578 assert_int_equal(
579 fx->tc->expected_written_len,
580 stream_fake_written_len(fx->fake_stream_adapter)
581 );
582
583 assert_memory_equal(
584 stream_fake_written_data(fx->fake_stream_adapter),
585 fx->tc->expected_written_message,
586 fx->tc->expected_written_len
587 );
588 }
589}
590
591//-----------------------------------------------------------------------------
592// CASES
593//-----------------------------------------------------------------------------
594
595static const test_logger_default_log_case_t CASE_LOGGER_DEFAULT_LOG_MESSAGE_NULL = {
596 .name = "logger_default_log_message_null",
598 .message = NULL,
599 .expected_ret = LOGGER_STATUS_INVALID,
600 .write_call = false,
601};
602
603static const test_logger_default_log_case_t CASE_LOGGER_DEFAULT_LOG_OK_NO_NEWLINE = {
604 .name = "logger_default_log_ok_no_newline",
606 .message = "abc",
607 .write_ret = STREAM_STATUS_OK,
608 .time_ops_status = OSAL_TIME_STATUS_OK,
609 .expected_ret = LOGGER_STATUS_OK,
610 .write_call = true,
611 .expected_written_len = sizeof("[1970-01-01 00:00:00 UTC+0] abc") - 1,
612 .expected_written_message = "[1970-01-01 00:00:00 UTC+0] abc"
613};
614
615static const test_logger_default_log_case_t CASE_LOGGER_DEFAULT_LOG_OK_APPEND_NEWLINE = {
616 .name = "logger_default_log_ok_append_newline",
618 .message = "abc",
619 .write_ret = STREAM_STATUS_OK,
620 .time_ops_status = OSAL_TIME_STATUS_OK,
621 .expected_ret = LOGGER_STATUS_OK,
622 .write_call = true,
623 .expected_written_len = sizeof("[1970-01-01 00:00:00 UTC+0] abc\n") - 1,
624 .expected_written_message = "[1970-01-01 00:00:00 UTC+0] abc\n"
625};
626
627static const test_logger_default_log_case_t CASE_LOGGER_DEFAULT_LOG_WRITE_FAIL = {
628 .name = "logger_default_log_write_fail",
630 .message = "abc",
631 .write_ret = STREAM_STATUS_IO_ERROR,
632 .expected_ret = LOGGER_STATUS_IO_ERROR,
633 .write_call = true,
634};
635
636static const test_logger_default_log_case_t CASE_LOGGER_DEFAULT_LOG_TIME_FAIL = {
637 .name = "logger_default_log_time_fail",
639 .message = "abc",
640 .time_ops_status = OSAL_TIME_STATUS_ERROR,
641 .expected_ret = LOGGER_STATUS_OK,
642 .write_call = true,
643 .expected_written_len = sizeof("[timestamp error] abc") - 1,
644 .expected_written_message = "[timestamp error] abc"
645};
646
647//-----------------------------------------------------------------------------
648// REGISTRY
649//-----------------------------------------------------------------------------
650
651#define LOGGER_DEFAULT_LOG_CASES(X) \
652X(CASE_LOGGER_DEFAULT_LOG_MESSAGE_NULL) \
653X(CASE_LOGGER_DEFAULT_LOG_OK_NO_NEWLINE) \
654X(CASE_LOGGER_DEFAULT_LOG_OK_APPEND_NEWLINE) \
655X(CASE_LOGGER_DEFAULT_LOG_WRITE_FAIL) \
656X(CASE_LOGGER_DEFAULT_LOG_TIME_FAIL)
657
658#define LOGGER_DEFAULT_MAKE_LOG_TEST(case_sym) \
659LEXLEO_MAKE_TEST(logger_default_log, case_sym)
660
661static const struct CMUnitTest logger_default_log_tests[] = {
662 LOGGER_DEFAULT_LOG_CASES(LOGGER_DEFAULT_MAKE_LOG_TEST)
663};
664
665#undef LOGGER_DEFAULT_LOG_CASES
666#undef LOGGER_DEFAULT_MAKE_LOG_TEST
667
694static void test_logger_default_destroy(void **state)
695{
696 (void)state;
697
698 fake_memory_reset();
699 fake_time_reset();
700
701 logger_t *logger = NULL;
702 stream_fake_t *fake_stream_adapter = NULL;
703 stream_t *fake_stream = NULL;
704
705 const logger_default_cfg_t cfg = {
706 .append_newline = true
707 };
708
709 const logger_default_env_t env = {
710 .stream = NULL, /* set below after stream creation */
711 .time_ops = NULL, /* set below */
712 .adapter_mem = NULL, /* set below */
713 .port_env = {
714 .mem = NULL /* set below */
715 }
716 };
717
718 logger_default_env_t env_local = env;
719
720 assert_int_equal(
722 &fake_stream_adapter,
723 &fake_stream,
726 );
727
728 stream_fake_reset(fake_stream_adapter);
729
730 env_local.stream = fake_stream;
731 env_local.time_ops = osal_time_test_fake_ops();
733 env_local.port_env.mem = osal_mem_test_fake_ops();
734
735 assert_int_equal(
736 logger_default_create_logger(&logger, &cfg, &env_local),
738 );
739 assert_non_null(logger);
740
741 /*
742 * Exercise the private destroy callback through the public lifecycle API.
743 */
744 logger_destroy(&logger);
745
746 assert_null(logger);
747
748 stream_fake_destroy(&fake_stream_adapter, &fake_stream);
749
750 assert_true(fake_memory_no_leak());
751 assert_true(fake_memory_no_invalid_free());
752 assert_true(fake_memory_no_double_free());
753}
754
755//-----------------------------------------------------------------------------
756// MAIN
757//-----------------------------------------------------------------------------
758
761int main(void) {
762 static const struct CMUnitTest logger_default_non_parametric_tests[] = {
763 cmocka_unit_test(test_logger_default_default_cfg),
764 cmocka_unit_test(test_logger_default_default_env),
765 cmocka_unit_test(test_logger_default_destroy),
766 };
767
768 int failed = 0;
769 failed += cmocka_run_group_tests(logger_default_non_parametric_tests, NULL, NULL);
770 failed += cmocka_run_group_tests(logger_default_create_logger_tests, NULL, NULL);
771 failed += cmocka_run_group_tests(logger_default_log_tests, NULL, NULL);
772
773 return failed;
774}
775
Borrower-facing runtime operations for the logger port.
logger_status_t logger_log(logger_t *l, const char *message)
Emit a log message through a logger.
Definition logger.c:77
Composition Root helpers for the logger port.
Composition Root API for wiring the logger_default adapter.
logger_default_env_t logger_default_default_env(stream_t *stream, const osal_time_ops_t *time_ops, const osal_mem_ops_t *adapter_mem, const logger_env_t *port_env)
Build a default environment for the logger_default adapter.
logger_status_t logger_default_create_logger(logger_t **out, const logger_default_cfg_t *cfg, const logger_default_env_t *env)
Create a logger instance backed by the logger_default adapter.
logger_default_cfg_t logger_default_default_cfg(void)
Return a default configuration for the logger_default adapter.
Lifecycle services for logger_t handles.
void logger_destroy(logger_t **l)
Destroy a logger handle.
Definition logger.c:57
logger_status_t
@ LOGGER_STATUS_OOM
@ LOGGER_STATUS_IO_ERROR
@ LOGGER_STATUS_OK
@ LOGGER_STATUS_INVALID
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)
const osal_time_ops_t * osal_time_test_fake_ops(void)
osal_time_status_t
@ OSAL_TIME_STATUS_OK
@ OSAL_TIME_STATUS_ERROR
Fake stream provider used by stream tests.
const stream_fake_counters_t * stream_fake_counters(const stream_fake_t *fake)
Return the current call counters recorded by the fake stream.
void stream_fake_destroy(stream_fake_t **fake, stream_t **stream)
Destroy a fake stream backend and its associated public stream.
void stream_fake_fail_write_since(stream_fake_t *fake, size_t call_idx, stream_status_t status)
Make fake writes fail starting from a given call index.
void stream_fake_reset(stream_fake_t *fake)
Reset the fake stream runtime state and counters.
size_t stream_fake_written_len(const stream_fake_t *fake)
Return the number of bytes captured by the fake stream.
const uint8_t * stream_fake_written_data(const stream_fake_t *fake)
Return the captured bytes written through the fake stream.
stream_status_t stream_fake_create(stream_fake_t **out_fake, stream_t **out_stream, const osal_mem_ops_t *mem)
Create a fake stream backend and its associated public stream_t.
Borrower-visible public types for the stream port.
stream_status_t
Public status codes used by the stream port.
@ STREAM_STATUS_IO_ERROR
@ STREAM_STATUS_OK
Configuration type for the logger_default adapter.
bool append_newline
Whether the adapter appends a trailing newline to emitted messages.
Injected dependencies for the logger_default adapter.
logger_env_t port_env
Borrowed logger port environment.
const osal_mem_ops_t * adapter_mem
Borrowed memory operations used for adapter-backend allocation.
stream_t * stream
Borrowed target stream used by the adapter.
const osal_time_ops_t * time_ops
Borrowed time operations used for timestamp generation.
Runtime environment for the logger port.
Definition logger_env.h:34
const osal_mem_ops_t * mem
Memory operations used by the logger port.
Definition logger_env.h:42
Private handle structure for a logger_t.
Private handle structure for a stream_t.
const osal_mem_ops_t * mem
logger_default_create_logger_scenario_t
Scenarios for logger_default_create_logger().
@ LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_CFG_NULL
@ LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_OUT_NULL
@ LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_OK
@ LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_ENV_NULL
@ LOGGER_DEFAULT_CREATE_LOGGER_SCENARIO_OOM
logger_default_log_scenario_t
Scenarios for logger_default_log().
@ LOGGER_DEFAULT_LOG_SCENARIO_OK_APPEND_NEWLINE
@ LOGGER_DEFAULT_LOG_SCENARIO_TIME_OPS_FAIL
@ LOGGER_DEFAULT_LOG_SCENARIO_MESSAGE_NULL
@ LOGGER_DEFAULT_LOG_SCENARIO_OK_NO_NEWLINE
@ LOGGER_DEFAULT_LOG_SCENARIO_STREAM_WRITE_FAIL
static void test_logger_default_default_cfg(void **state)
Test logger_default_default_cfg().
static void test_logger_default_destroy(void **state)
Test logger_default_destroy().
static void test_logger_default_default_env(void **state)
Test logger_default_default_env().