File size: 7,153 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 |
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
/* Globals from cut.c are visible since this file is included into cut.c */
extern struct field_range_pair *frp;
/* Helper: build selection ranges and set frp.
'pairs' is an array of {lo, hi} with 'count' entries.
Returns the allocated array to be freed by the caller. */
static struct field_range_pair* set_frp_from_pairs(const uintmax_t (*pairs)[2], size_t count) {
struct field_range_pair *arr = (struct field_range_pair*) malloc(sizeof(*arr) * (count + 1));
if (!arr) return NULL;
for (size_t i = 0; i < count; i++) {
arr[i].lo = pairs[i][0];
arr[i].hi = pairs[i][1];
}
/* Sentinel to ensure no stepping beyond valid ranges. */
arr[count].lo = UINTMAX_MAX;
arr[count].hi = UINTMAX_MAX;
frp = arr;
return arr;
}
/* Helper: run cut_fields on provided input string with given configuration,
capturing stdout and returning the captured output in a malloc'ed string.
The caller must free the returned string.
out_delim_str==NULL => use default (input delimiter). */
static char* run_cut_fields_capture(
const char *input,
const uintmax_t (*pairs)[2], size_t npairs,
unsigned char in_delim,
const char *out_delim_str,
bool only_delimited)
{
/* Configure globals used by cut_fields. */
suppress_non_delimited = only_delimited;
/* Set input delimiter. */
delim = in_delim;
/* Configure default and actual output delimiter. */
output_delimiter_default[0] = delim;
if (out_delim_str == NULL) {
output_delimiter_string = output_delimiter_default;
output_delimiter_length = 1;
} else {
output_delimiter_string = (char*)out_delim_str;
output_delimiter_length = strlen(out_delim_str);
}
/* Ensure line delimiter is standard newline for these tests. */
line_delim = '\n';
/* Set selection ranges. */
struct field_range_pair *local_frp = set_frp_from_pairs(pairs, npairs);
if (!local_frp) {
/* Allocation failure – return a copy of empty string to avoid NULL. */
char *empty = (char*)malloc(1);
if (empty) empty[0] = '\0';
return empty;
}
/* Prepare input stream. */
FILE *in = tmpfile();
if (!in) {
free(local_frp);
char *empty = (char*)malloc(1);
if (empty) empty[0] = '\0';
return empty;
}
if (input && *input) {
fwrite(input, 1, strlen(input), in);
}
fflush(in);
fseek(in, 0, SEEK_SET);
/* Capture stdout to a temporary file. */
fflush(stdout);
int saved_stdout_fd = dup(fileno(stdout));
FILE *cap = tmpfile();
if (cap == NULL) {
fclose(in);
free(local_frp);
char *empty = (char*)malloc(1);
if (empty) empty[0] = '\0';
return empty;
}
dup2(fileno(cap), fileno(stdout));
clearerr(stdout);
/* Call target function. */
cut_fields(in);
/* Stop capturing and restore stdout. */
fflush(stdout);
dup2(saved_stdout_fd, fileno(stdout));
close(saved_stdout_fd);
/* Read captured output into a buffer. */
fflush(cap);
fseek(cap, 0, SEEK_END);
long sz = ftell(cap);
if (sz < 0) sz = 0;
fseek(cap, 0, SEEK_SET);
char *out = (char*)malloc((size_t)sz + 1);
if (!out) {
out = (char*)malloc(1);
if (out) out[0] = '\0';
} else {
size_t nread = fread(out, 1, (size_t)sz, cap);
out[nread] = '\0';
}
/* Cleanup. */
fclose(cap);
fclose(in);
free(local_frp);
return out;
}
void setUp(void) {
/* Ensure a clean starting state for globals influenced by previous calls. */
suppress_non_delimited = false;
/* Default delimiters */
delim = '\t';
line_delim = '\n';
output_delimiter_default[0] = delim;
output_delimiter_string = output_delimiter_default;
output_delimiter_length = 1;
}
void tearDown(void) {
/* Release buffer possibly allocated by cut_fields. */
if (field_1_buffer) {
free(field_1_buffer);
field_1_buffer = NULL;
field_1_bufsize = 0;
}
}
/* Test: Basic selection with default delimiter (TAB): select fields 2-3. */
void test_cut_fields_basic_selection_default_delim(void) {
const char *input = "a\tb\tc\n1\t2\t3\n";
const uintmax_t pairs[][2] = { {2,3} };
char *out = run_cut_fields_capture(input, pairs, 1, '\t', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("b\tc\n2\t3\n", out);
free(out);
}
/* Test: Non-delimited line without -s should be printed unchanged. */
void test_cut_fields_nondelimited_without_s(void) {
const char *input = "abc\n";
const uintmax_t pairs[][2] = { {2,2} }; /* select field 2 */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("abc\n", out);
free(out);
}
/* Test: Non-delimited line with -s should be suppressed entirely. */
void test_cut_fields_nondelimited_with_s(void) {
const char *input = "abc\n";
const uintmax_t pairs[][2] = { {1,1} }; /* select field 1 */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, true);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("", out);
free(out);
}
/* Test: Custom output delimiter between selected fields (select 1 and 3). */
void test_cut_fields_custom_output_delimiter(void) {
const char *input = "a,b,c\nx,y,z\n";
const uintmax_t pairs[][2] = { {1,1}, {3,3} };
char *out = run_cut_fields_capture(input, pairs, 2, ',', ":", false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("a:c\nx:z\n", out);
free(out);
}
/* Test: Line has delimiters but not enough fields; output should be empty line(s). */
void test_cut_fields_missing_field_produces_empty_line(void) {
const char *input = "a,b\nx,y\n";
const uintmax_t pairs[][2] = { {3,3} }; /* third field does not exist */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("\n\n", out);
free(out);
}
/* Test: Input without trailing newline – ensure output is newline-terminated. */
void test_cut_fields_no_trailing_newline(void) {
const char *input = "a,b,c";
const uintmax_t pairs[][2] = { {2,2} }; /* select field 2 */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("b\n", out);
free(out);
}
/* Test: Empty selected field (adjacent delimiters) should yield empty output for that line. */
void test_cut_fields_empty_selected_field(void) {
const char *input = "a,,c\n";
const uintmax_t pairs[][2] = { {2,2} }; /* select empty field 2 */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("\n", out);
free(out);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_cut_fields_basic_selection_default_delim);
RUN_TEST(test_cut_fields_nondelimited_without_s);
RUN_TEST(test_cut_fields_nondelimited_with_s);
RUN_TEST(test_cut_fields_custom_output_delimiter);
RUN_TEST(test_cut_fields_missing_field_produces_empty_line);
RUN_TEST(test_cut_fields_no_trailing_newline);
RUN_TEST(test_cut_fields_empty_selected_field);
return UNITY_END();
} |