File size: 7,275 Bytes
78d2150 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | #include "../../unity/unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
/* Unity hooks */
void setUp(void) {
/* No global setup needed */
}
void tearDown(void) {
/* No global teardown needed */
}
/* Helper to capture output of put_space while respecting the redirection rule.
It sets tabs and out_column as requested, calls put_space(space),
captures the emitted bytes into 'out' (NUL-terminated), returns the final
out_column via final_col, and the captured byte count via out_len.
Returns NULL on success, or a static error message string on failure. */
static char* capture_put_space_call(int start_col, bool tabs_flag, int space,
char* out, size_t out_cap,
int* final_col, size_t* out_len)
{
static char errbuf[128];
if (!out || out_cap == 0 || !final_col || !out_len) {
return "invalid arguments";
}
/* Save original globals */
bool tabs_saved = tabs;
int out_saved = out_column;
/* Prepare redirection */
fflush(stdout);
int saved_fd = dup(fileno(stdout));
if (saved_fd < 0) {
snprintf(errbuf, sizeof(errbuf), "dup failed: %s", strerror(errno));
return errbuf;
}
FILE* tmp = tmpfile();
if (!tmp) {
close(saved_fd);
snprintf(errbuf, sizeof(errbuf), "tmpfile failed: %s", strerror(errno));
return errbuf;
}
int tmpfd = fileno(tmp);
if (dup2(tmpfd, fileno(stdout)) < 0) {
fclose(tmp);
close(saved_fd);
snprintf(errbuf, sizeof(errbuf), "dup2 failed: %s", strerror(errno));
return errbuf;
}
/* Set globals for this call */
tabs = tabs_flag;
out_column = start_col;
/* Call the function under test (do not assert while redirected) */
put_space(space);
int result_col = out_column;
/* Flush and read back from tmp */
fflush(stdout);
long pos = ftell(tmp);
if (pos < 0) pos = 0;
if (fseek(tmp, 0, SEEK_SET) != 0) {
/* Attempt to restore stdout before returning error */
dup2(saved_fd, fileno(stdout));
close(saved_fd);
fclose(tmp);
snprintf(errbuf, sizeof(errbuf), "fseek failed: %s", strerror(errno));
/* Restore globals */
tabs = tabs_saved;
out_column = out_saved;
return errbuf;
}
size_t toread = (size_t)pos;
if (toread >= out_cap) toread = out_cap - 1;
size_t nread = fread(out, 1, toread, tmp);
out[nread] = '\0';
*out_len = nread;
/* Restore stdout */
if (dup2(saved_fd, fileno(stdout)) < 0) {
close(saved_fd);
fclose(tmp);
snprintf(errbuf, sizeof(errbuf), "restore dup2 failed: %s", strerror(errno));
/* Restore globals */
tabs = tabs_saved;
out_column = out_saved;
return errbuf;
}
close(saved_fd);
fclose(tmp);
/* Restore globals except report the result of the call */
tabs = tabs_saved;
out_column = out_saved;
*final_col = result_col;
return NULL;
}
/* Tests */
void test_put_space_zero_no_tabs(void) {
char buf[16];
size_t n = 0;
int final_col = -1;
char* err = capture_put_space_call(0, false, 0, buf, sizeof(buf), &final_col, &n);
TEST_ASSERT_MESSAGE(err == NULL, err ? err : "");
TEST_ASSERT_EQUAL_INT(0, (int)n);
TEST_ASSERT_EQUAL_INT(0, final_col);
TEST_ASSERT_EQUAL_STRING("", buf);
}
void test_put_space_only_spaces_simple(void) {
char buf[32];
size_t n = 0;
int final_col = -1;
char* err = capture_put_space_call(0, false, 5, buf, sizeof(buf), &final_col, &n);
TEST_ASSERT_MESSAGE(err == NULL, err ? err : "");
TEST_ASSERT_EQUAL_INT(5, (int)n);
TEST_ASSERT_EQUAL_INT(5, final_col);
TEST_ASSERT_EQUAL_STRING(" ", buf);
}
void test_put_space_preserves_starting_column_no_tabs(void) {
char buf[32];
size_t n = 0;
int final_col = -1;
/* Start not at column 0; still must print exactly 'space' spaces */
char* err = capture_put_space_call(7, false, 3, buf, sizeof(buf), &final_col, &n);
TEST_ASSERT_MESSAGE(err == NULL, err ? err : "");
TEST_ASSERT_EQUAL_INT(3, (int)n);
TEST_ASSERT_EQUAL_INT(10, final_col); /* 7 + 3 */
TEST_ASSERT_EQUAL_STRING(" ", buf);
}
void test_put_space_tabs_exact_tabstop_from_zero(void) {
char buf[32];
size_t n = 0;
int final_col = -1;
/* From col 0 to exactly 8: expect one tab, final col 8 */
char* err = capture_put_space_call(0, true, 8, buf, sizeof(buf), &final_col, &n);
TEST_ASSERT_MESSAGE(err == NULL, err ? err : "");
TEST_ASSERT_EQUAL_INT(1, (int)n);
TEST_ASSERT_EQUAL_INT(8, final_col);
TEST_ASSERT_EQUAL_STRING("\t", buf);
}
void test_put_space_tabs_to_non_tabstop_from_zero(void) {
char buf[32];
size_t n = 0;
int final_col = -1;
/* From col 0 to 10: expect tab (to 8) + two spaces, final col 10 */
char* err = capture_put_space_call(0, true, 10, buf, sizeof(buf), &final_col, &n);
TEST_ASSERT_MESSAGE(err == NULL, err ? err : "");
TEST_ASSERT_EQUAL_INT(3, (int)n);
TEST_ASSERT_EQUAL_INT(10, final_col);
TEST_ASSERT_EQUAL_STRING("\t ", buf);
}
void test_put_space_tabs_one_before_tabstop_no_tab_used(void) {
char buf[32];
size_t n = 0;
int final_col = -1;
/* out_column=7, space=1 -> target=8; condition prevents tab; expect one space */
char* err = capture_put_space_call(7, true, 1, buf, sizeof(buf), &final_col, &n);
TEST_ASSERT_MESSAGE(err == NULL, err ? err : "");
TEST_ASSERT_EQUAL_INT(1, (int)n);
TEST_ASSERT_EQUAL_INT(8, final_col);
TEST_ASSERT_EQUAL_STRING(" ", buf);
}
void test_put_space_tabs_multiple_tabs_then_spaces(void) {
char buf[32];
size_t n = 0;
int final_col = -1;
/* From 0 to 20: two tabs to 16, then 4 spaces */
char* err = capture_put_space_call(0, true, 20, buf, sizeof(buf), &final_col, &n);
TEST_ASSERT_MESSAGE(err == NULL, err ? err : "");
TEST_ASSERT_EQUAL_INT(6, (int)n);
TEST_ASSERT_EQUAL_INT(20, final_col);
TEST_ASSERT_EQUAL_STRING("\t\t ", buf);
}
void test_put_space_negative_space_no_output(void) {
char buf[16];
size_t n = 0;
int final_col = -1;
/* Negative space should result in no output and no backward move */
char* err = capture_put_space_call(5, false, -3, buf, sizeof(buf), &final_col, &n);
TEST_ASSERT_MESSAGE(err == NULL, err ? err : "");
TEST_ASSERT_EQUAL_INT(0, (int)n);
TEST_ASSERT_EQUAL_INT(5, final_col);
TEST_ASSERT_EQUAL_STRING("", buf);
}
void test_put_space_tabs_from_nonzero_cross_one_tab(void) {
char buf[32];
size_t n = 0;
int final_col = -1;
/* Start at col 3, target 13: tab to 8, then 5 spaces */
char* err = capture_put_space_call(3, true, 10, buf, sizeof(buf), &final_col, &n);
TEST_ASSERT_MESSAGE(err == NULL, err ? err : "");
TEST_ASSERT_EQUAL_INT(6, (int)n);
TEST_ASSERT_EQUAL_INT(13, final_col);
TEST_ASSERT_EQUAL_STRING("\t ", buf);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_put_space_zero_no_tabs);
RUN_TEST(test_put_space_only_spaces_simple);
RUN_TEST(test_put_space_preserves_starting_column_no_tabs);
RUN_TEST(test_put_space_tabs_exact_tabstop_from_zero);
RUN_TEST(test_put_space_tabs_to_non_tabstop_from_zero);
RUN_TEST(test_put_space_tabs_one_before_tabstop_no_tab_used);
RUN_TEST(test_put_space_tabs_multiple_tabs_then_spaces);
RUN_TEST(test_put_space_negative_space_no_output);
RUN_TEST(test_put_space_tabs_from_nonzero_cross_one_tab);
return UNITY_END();
} |