File size: 5,969 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
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <stdio.h>

/* The following globals and function are defined in the included program code:
   - static char const *infile;
   - static int input_desc;
   - static bool simple_cat (char *buf, idx_t bufsize);
   We can reference them directly since this test file is included in the same translation unit.
*/

void setUp(void) {
  /* Setup code here, or leave empty */
}

void tearDown(void) {
  /* Cleanup code here, or leave empty */
}

/* Helper: run simple_cat with given input bytes, capturing stdout.
   Returns 0 on success, nonzero on helper failure.
   On success, *out points to malloc'd buffer (caller frees), *out_len set,
   and *func_ret holds the boolean return value of simple_cat. */
static int run_simple_cat_capture(const char *input, size_t input_len,
                                  size_t bufsize,
                                  char **out, size_t *out_len, bool *func_ret)
{
  int inpipe[2] = {-1, -1};
  int outpipe[2] = {-1, -1};
  int saved_stdout = -1;
  char *capture = NULL;
  size_t cap = 0, len = 0;
  int rc = 0;

  if (pipe(inpipe) != 0)
    return -1;

  /* Write all input, then close write end to signal EOF. */
  size_t written = 0;
  while (written < input_len) {
    ssize_t n = write(inpipe[1], input + written, input_len - written);
    if (n < 0) { rc = -2; goto cleanup; }
    written += (size_t)n;
  }
  if (close(inpipe[1]) != 0) { inpipe[1] = -1; rc = -3; goto cleanup; }
  inpipe[1] = -1;

  input_desc = inpipe[0];
  infile = "test-input";

  if (pipe(outpipe) != 0) { rc = -4; goto cleanup; }

  saved_stdout = dup(STDOUT_FILENO);
  if (saved_stdout < 0) { rc = -5; goto cleanup; }

  if (dup2(outpipe[1], STDOUT_FILENO) < 0) { rc = -6; goto cleanup; }
  /* We can close the original write end now; fd 1 refers to it. */
  if (close(outpipe[1]) != 0) { outpipe[1] = -1; rc = -7; goto cleanup; }
  outpipe[1] = -1;

  /* Allocate buffer for simple_cat. No Unity asserts while stdout is redirected. */
  char *buf = (char *)malloc(bufsize > 0 ? bufsize : 1);
  if (!buf) { rc = -8; goto restore_stdout; }

  bool ret = simple_cat(buf, bufsize);
  free(buf);

restore_stdout:
  /* Restore stdout and proceed to read captured output. */
  {
    int tmp = dup2(saved_stdout, STDOUT_FILENO);
    (void)tmp; /* avoid unused warning; if tmp<0, tests will fail later anyway */
    close(saved_stdout);
    saved_stdout = -1;
  }

  /* No longer need the input read end. */
  if (inpipe[0] >= 0) { close(inpipe[0]); inpipe[0] = -1; }

  /* Read all captured output. */
  {
    char tmpbuf[4096];
    ssize_t n;
    for (;;) {
      n = read(outpipe[0], tmpbuf, sizeof tmpbuf);
      if (n < 0) { rc = -9; goto cleanup; }
      if (n == 0) break;
      if (len + (size_t)n > cap) {
        size_t newcap = cap ? cap * 2 : 1024;
        while (newcap < len + (size_t)n) newcap *= 2;
        char *newp = (char *)realloc(capture, newcap);
        if (!newp) { rc = -10; goto cleanup; }
        capture = newp;
        cap = newcap;
      }
      memcpy(capture + len, tmpbuf, (size_t)n);
      len += (size_t)n;
    }
  }
  if (outpipe[0] >= 0) { close(outpipe[0]); outpipe[0] = -1; }

  *out = capture;
  *out_len = len;
  *func_ret = ret;
  return 0;

cleanup:
  if (saved_stdout >= 0) {
    /* Best effort to restore stdout if it was redirected. */
    dup2(saved_stdout, STDOUT_FILENO);
    close(saved_stdout);
    saved_stdout = -1;
  }
  if (inpipe[0] >= 0) close(inpipe[0]);
  if (inpipe[1] >= 0) close(inpipe[1]);
  if (outpipe[0] >= 0) close(outpipe[0]);
  if (outpipe[1] >= 0) close(outpipe[1]);
  free(capture);
  return rc;
}

/* Test: basic small input copied exactly, returns true. */
void test_simple_cat_basic(void)
{
  const char *input = "Hello, world!\nSecond line.\nNo newline at end";
  char *out = NULL;
  size_t out_len = 0;
  bool func_ret = false;

  int rc = run_simple_cat_capture(input, strlen(input), 64, &out, &out_len, &func_ret);
  TEST_ASSERT_EQUAL_INT(0, rc);
  TEST_ASSERT_TRUE(func_ret);
  TEST_ASSERT_EQUAL_INT((int)strlen(input), (int)out_len);
  TEST_ASSERT_EQUAL_MEMORY(input, out, out_len);

  free(out);
}

/* Test: empty input (immediate EOF) produces no output, returns true. */
void test_simple_cat_empty_input(void)
{
  const char *input = "";
  char *out = NULL;
  size_t out_len = 0;
  bool func_ret = false;

  int rc = run_simple_cat_capture(input, 0, 32, &out, &out_len, &func_ret);
  TEST_ASSERT_EQUAL_INT(0, rc);
  TEST_ASSERT_TRUE(func_ret);
  TEST_ASSERT_EQUAL_INT(0, (int)out_len);

  free(out);
}

/* Test: large input with small buffer to force multiple read cycles. */
void test_simple_cat_large_input_small_buf(void)
{
  const size_t N = 131072 + 123; /* >128 KiB to ensure multiple iterations */
  char *input = (char *)malloc(N);
  TEST_ASSERT_NOT_NULL(input);
  for (size_t i = 0; i < N; i++) {
    input[i] = (char)('A' + (int)(i % 26));
  }

  char *out = NULL;
  size_t out_len = 0;
  bool func_ret = false;

  int rc = run_simple_cat_capture(input, N, 128, &out, &out_len, &func_ret);
  TEST_ASSERT_EQUAL_INT(0, rc);
  TEST_ASSERT_TRUE(func_ret);
  TEST_ASSERT_EQUAL_INT((int)N, (int)out_len);
  TEST_ASSERT_EQUAL_MEMORY(input, out, N);

  free(out);
  free(input);
}

/* Test: read error (invalid fd) returns false. No need to redirect stdout. */
void test_simple_cat_read_error_returns_false(void)
{
  /* Set globals to induce read error. */
  input_desc = -1; /* invalid fd */
  infile = "invalid-fd-for-test";

  char dummy_buf[16];
  bool ret = simple_cat(dummy_buf, sizeof dummy_buf);
  TEST_ASSERT_FALSE(ret);
}

int main(void)
{
  UNITY_BEGIN();
  RUN_TEST(test_simple_cat_basic);
  RUN_TEST(test_simple_cat_empty_input);
  RUN_TEST(test_simple_cat_large_input_small_buf);
  RUN_TEST(test_simple_cat_read_error_returns_false);
  return UNITY_END();
}