|
|
#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(); |
|
|
} |