| #include "../../unity/unity.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
|
|
| |
| void setUp(void) { |
| |
| } |
| void tearDown(void) { |
| |
| } |
|
|
| |
| |
| |
| 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"); |
| } |
|
|
| |
| printv(v); |
|
|
| if (fflush(stdout) != 0) { |
| |
| 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"); |
| } |
|
|
| |
| 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'; |
|
|
| |
| 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; |
| } |
|
|
| |
| static VALUE *int_value_from_str(const char *s) |
| { |
| VALUE *v = int_value(0); |
| |
| (void) mpz_set_str(v->u.i, s, 10); |
| return v; |
| } |
|
|
| |
|
|
| 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) |
| { |
| |
| 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); |
| free(out); |
| freev(v); |
| } |
|
|
| static void test_printv_string_multibyte_utf8(void) |
| { |
| |
| const char *s = "\xCE\xB1bc"; |
| 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); |
| } |
|
|
| |
| 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(); |
| } |