#include "../../unity/unity.h" #include #include #include #include #include /* Unity fixtures */ void setUp(void) { /* No setup required */ } void tearDown(void) { /* No teardown required */ } /* Helper: capture stdout from printv(v) into *out. Returns NULL on success, or a malloc'd error message on failure. This function must not use Unity asserts while stdout is redirected. */ static char *capture_printv_output(VALUE *v, char **out) { int saved_stdout = dup(STDOUT_FILENO); if (saved_stdout < 0) return strdup("dup(STDOUT_FILENO) failed"); FILE *tmp = tmpfile(); if (!tmp) { int e = errno; close(saved_stdout); char *msg = (char*)malloc(128); if (msg) snprintf(msg, 128, "tmpfile() failed: errno=%d", e); return msg ? msg : strdup("tmpfile() failed"); } int tmpfd = fileno(tmp); if (tmpfd < 0) { int e = errno; fclose(tmp); close(saved_stdout); char *msg = (char*)malloc(128); if (msg) snprintf(msg, 128, "fileno(tmp) failed: errno=%d", e); return msg ? msg : strdup("fileno(tmp) failed"); } if (fflush(stdout) != 0) { int e = errno; fclose(tmp); close(saved_stdout); char *msg = (char*)malloc(128); if (msg) snprintf(msg, 128, "fflush(stdout) before dup2 failed: errno=%d", e); return msg ? msg : strdup("fflush(stdout) failed"); } if (dup2(tmpfd, STDOUT_FILENO) < 0) { int e = errno; fclose(tmp); close(saved_stdout); char *msg = (char*)malloc(128); if (msg) snprintf(msg, 128, "dup2(tmpfd, STDOUT_FILENO) failed: errno=%d", e); return msg ? msg : strdup("dup2 failed"); } /* Call the function under test while stdout is redirected. */ printv(v); if (fflush(stdout) != 0) { /* Attempt to restore stdout even on error */ int e = errno; dup2(saved_stdout, STDOUT_FILENO); close(saved_stdout); fclose(tmp); char *msg = (char*)malloc(128); if (msg) snprintf(msg, 128, "fflush(stdout) after printv failed: errno=%d", e); return msg ? msg : strdup("fflush(stdout) failed after printv"); } /* Read back the content */ if (fseek(tmp, 0, SEEK_END) != 0) { int e = errno; dup2(saved_stdout, STDOUT_FILENO); close(saved_stdout); fclose(tmp); char *msg = (char*)malloc(128); if (msg) snprintf(msg, 128, "fseek(tmp, SEEK_END) failed: errno=%d", e); return msg ? msg : strdup("fseek failed"); } long len = ftell(tmp); if (len < 0) { int e = errno; dup2(saved_stdout, STDOUT_FILENO); close(saved_stdout); fclose(tmp); char *msg = (char*)malloc(128); if (msg) snprintf(msg, 128, "ftell(tmp) failed: errno=%d", e); return msg ? msg : strdup("ftell failed"); } if (fseek(tmp, 0, SEEK_SET) != 0) { int e = errno; dup2(saved_stdout, STDOUT_FILENO); close(saved_stdout); fclose(tmp); char *msg = (char*)malloc(128); if (msg) snprintf(msg, 128, "fseek(tmp, SEEK_SET) failed: errno=%d", e); return msg ? msg : strdup("fseek reset failed"); } char *buf = (char*)malloc((size_t)len + 1); if (!buf) { dup2(saved_stdout, STDOUT_FILENO); close(saved_stdout); fclose(tmp); return strdup("malloc for output buffer failed"); } size_t rd = fread(buf, 1, (size_t)len, tmp); buf[rd] = '\0'; /* Restore stdout and clean up */ if (dup2(saved_stdout, STDOUT_FILENO) < 0) { int e = errno; close(saved_stdout); fclose(tmp); free(buf); char *msg = (char*)malloc(128); if (msg) snprintf(msg, 128, "dup2(saved_stdout, STDOUT_FILENO) restore failed: errno=%d", e); return msg ? msg : strdup("dup2 restore failed"); } close(saved_stdout); fclose(tmp); *out = buf; return NULL; } /* Convenience: build an integer VALUE from a base-10 C string using mpz_set_str. */ static VALUE *int_value_from_str(const char *s) { VALUE *v = int_value(0); /* mpz_set_str returns 0 on success. We use valid inputs in tests. */ (void) mpz_set_str(v->u.i, s, 10); return v; } /* Tests */ static void test_printv_integer_zero(void) { VALUE *v = int_value(0); char *out = NULL; char *err = capture_printv_output(v, &out); TEST_ASSERT_NULL_MESSAGE(err, err ? err : "unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING("0\n", out); free(out); freev(v); } static void test_printv_integer_positive_small(void) { VALUE *v = int_value(12345UL); char *out = NULL; char *err = capture_printv_output(v, &out); TEST_ASSERT_NULL_MESSAGE(err, err ? err : "unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING("12345\n", out); free(out); freev(v); } static void test_printv_integer_negative(void) { VALUE *v = int_value(0); mpz_set_si(v->u.i, -42); char *out = NULL; char *err = capture_printv_output(v, &out); TEST_ASSERT_NULL_MESSAGE(err, err ? err : "unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING("-42\n", out); free(out); freev(v); } static void test_printv_integer_large(void) { /* Larger than 64-bit integer */ const char *big = "1234567890123456789012345678901234567890"; VALUE *v = int_value_from_str(big); char *out = NULL; char *err = capture_printv_output(v, &out); TEST_ASSERT_NULL_MESSAGE(err, err ? err : "unexpected error"); TEST_ASSERT_NOT_NULL(out); size_t need = strlen(big) + 2; char *expected = (char*)malloc(need); TEST_ASSERT_NOT_NULL(expected); snprintf(expected, need, "%s\n", big); TEST_ASSERT_EQUAL_STRING(expected, out); free(expected); free(out); freev(v); } static void test_printv_string_simple(void) { VALUE *v = str_value("hello"); char *out = NULL; char *err = capture_printv_output(v, &out); TEST_ASSERT_NULL_MESSAGE(err, err ? err : "unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING("hello\n", out); free(out); freev(v); } static void test_printv_string_empty(void) { VALUE *v = str_value(""); char *out = NULL; char *err = capture_printv_output(v, &out); TEST_ASSERT_NULL_MESSAGE(err, err ? err : "unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING("\n", out); /* puts("") emits a newline */ free(out); freev(v); } static void test_printv_string_multibyte_utf8(void) { /* Greek alpha (UTF-8) followed by ASCII */ const char *s = "\xCE\xB1bc"; /* "αbc" */ VALUE *v = str_value(s); char *out = NULL; char *err = capture_printv_output(v, &out); TEST_ASSERT_NULL_MESSAGE(err, err ? err : "unexpected error"); TEST_ASSERT_NOT_NULL(out); size_t need = strlen(s) + 2; char *expected = (char*)malloc(need); TEST_ASSERT_NOT_NULL(expected); snprintf(expected, need, "%s\n", s); TEST_ASSERT_EQUAL_STRING(expected, out); free(expected); free(out); freev(v); } static void test_printv_string_with_embedded_newline(void) { const char *s = "hi\nthere"; VALUE *v = str_value(s); char *out = NULL; char *err = capture_printv_output(v, &out); TEST_ASSERT_NULL_MESSAGE(err, err ? err : "unexpected error"); TEST_ASSERT_NOT_NULL(out); size_t need = strlen(s) + 2; char *expected = (char*)malloc(need); TEST_ASSERT_NOT_NULL(expected); snprintf(expected, need, "%s\n", s); TEST_ASSERT_EQUAL_STRING(expected, out); free(expected); free(out); freev(v); } /* Unity runner */ int main(void) { UNITY_BEGIN(); RUN_TEST(test_printv_integer_zero); RUN_TEST(test_printv_integer_positive_small); RUN_TEST(test_printv_integer_negative); RUN_TEST(test_printv_integer_large); RUN_TEST(test_printv_string_simple); RUN_TEST(test_printv_string_empty); RUN_TEST(test_printv_string_multibyte_utf8); RUN_TEST(test_printv_string_with_embedded_newline); return UNITY_END(); }