File size: 5,246 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
#include "../../unity/unity.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

/* Test-helper state */
static FILE *test_last_stream_seen = NULL;
static int test_callback_invoked = 0;
static size_t test_bytes_read = 0;

/* Prototypes of target under test are available since this file is included
   into the same translation unit as cut_file. We can call cut_file directly. */

/* Helper: create a temporary file with the given content, return malloc'ed path. */
static char *make_temp_file_with_content(const char *content)
{
  char tmp_template[] = "/tmp/cut_ut_XXXXXX";
  int fd = mkstemp(tmp_template);
  TEST_ASSERT_MESSAGE(fd >= 0, "mkstemp failed");
  size_t len = strlen(content);
  if (len > 0)
    {
      ssize_t w = write(fd, content, len);
      TEST_ASSERT_MESSAGE(w == (ssize_t)len, "write failed to write all content");
    }
  int rc = close(fd);
  TEST_ASSERT_MESSAGE(rc == 0, "close(temp file) failed");

  /* Return a heap-allocated copy of the path so caller can free it. */
  char *path = (char *)malloc(strlen(tmp_template) + 1);
  TEST_ASSERT_NOT_NULL(path);
  strcpy(path, tmp_template);
  return path;
}

/* Helper: reopen stdin from given path. */
static void reopen_stdin_from_path(const char *path)
{
  FILE *f = freopen(path, "r", stdin);
  TEST_ASSERT_MESSAGE(f != NULL, "freopen on stdin failed");
}

/* cut_stream callback that reads all bytes from the given stream */
static void test_cut_stream_read_all(FILE *s)
{
  test_callback_invoked++;
  test_last_stream_seen = s;
  test_bytes_read = 0;
  int ch;
  while ((ch = getc(s)) != EOF)
    {
      test_bytes_read++;
    }
}

/* cut_stream callback that does nothing */
static void test_cut_stream_noop(FILE *s)
{
  (void)s;
  test_callback_invoked++;
  test_last_stream_seen = s;
}

/* Unity fixtures */
void setUp(void)
{
  test_last_stream_seen = NULL;
  test_callback_invoked = 0;
  test_bytes_read = 0;
  errno = 0;

  /* Reset the program's internal flag before each test. */
  /* have_read_stdin is defined in the production code above, static at file scope.
     Since this test is included in the same translation unit, we can access it. */
  extern bool have_read_stdin; /* forward declaration to appease some compilers */
  have_read_stdin = false;

  /* Ensure stdin has no error/eof flags set */
  clearerr(stdin);
}

void tearDown(void)
{
  /* Nothing special */
}

/* Test: Using "-" uses stdin, sets have_read_stdin, invokes callback, and clears EOF */
static void test_cut_file_stdin_reads_and_clears_eof(void)
{
  /* Prepare stdin with some data */
  char *p = make_temp_file_with_content("ABC");
  reopen_stdin_from_path(p);

  extern bool have_read_stdin;

  bool ok = cut_file("-", test_cut_stream_read_all);
  TEST_ASSERT_TRUE(ok);
  TEST_ASSERT_TRUE(have_read_stdin);
  TEST_ASSERT_EQUAL_INT(1, test_callback_invoked);
  TEST_ASSERT_EQUAL_PTR(stdin, test_last_stream_seen);
  TEST_ASSERT_EQUAL_UINT32(3u, (unsigned) test_bytes_read);

  /* Because cut_file clears errors and EOF on stdin, feof(stdin) must be false */
  TEST_ASSERT_FALSE(feof(stdin));

  free(p);
}

/* Test: Regular file path is opened, callback sees that FILE*, and success is returned */
static void test_cut_file_regular_file_success(void)
{
  char *p = make_temp_file_with_content("xyz123");
  extern bool have_read_stdin;

  bool ok = cut_file(p, test_cut_stream_read_all);
  TEST_ASSERT_TRUE(ok);
  TEST_ASSERT_FALSE(have_read_stdin);
  TEST_ASSERT_EQUAL_INT(1, test_callback_invoked);
  TEST_ASSERT_NOT_NULL(test_last_stream_seen);
  /* For a regular file, cut_file opens its own stream, which must not be stdin */
  TEST_ASSERT_NOT_EQUAL_PTR(stdin, test_last_stream_seen);
  TEST_ASSERT_EQUAL_UINT32(6u, (unsigned) test_bytes_read);

  /* Clean up the temporary file */
  int rc = unlink(p);
  (void)rc; /* Ignore unlink failure on platforms that restrict deleting open files */
  free(p);
}

/* Test: Nonexistent file returns false and does not invoke the callback */
static void test_cut_file_nonexistent_returns_false(void)
{
  const char *nonexistent = "/tmp/cut_ut_no_such_file____XXXX";
  extern bool have_read_stdin;

  bool ok = cut_file(nonexistent, test_cut_stream_noop);
  TEST_ASSERT_FALSE(ok);
  TEST_ASSERT_EQUAL_INT(0, test_callback_invoked);
  TEST_ASSERT_FALSE(have_read_stdin);
}

/* Test: Using "-" without reading still sets have_read_stdin and returns true */
static void test_cut_file_stdin_no_read_sets_flag(void)
{
  /* Prepare stdin with empty file */
  char *p = make_temp_file_with_content("");
  reopen_stdin_from_path(p);

  extern bool have_read_stdin;

  bool ok = cut_file("-", test_cut_stream_noop);
  TEST_ASSERT_TRUE(ok);
  TEST_ASSERT_TRUE(have_read_stdin);
  TEST_ASSERT_EQUAL_INT(1, test_callback_invoked);
  TEST_ASSERT_EQUAL_PTR(stdin, test_last_stream_seen);
  /* No read was performed, but also no EOF should be set after clearerr */
  TEST_ASSERT_FALSE(feof(stdin));

  free(p);
}

int main(void)
{
  UNITY_BEGIN();

  RUN_TEST(test_cut_file_stdin_reads_and_clears_eof);
  RUN_TEST(test_cut_file_regular_file_success);
  RUN_TEST(test_cut_file_nonexistent_returns_false);
  RUN_TEST(test_cut_file_stdin_no_read_sets_flag);

  return UNITY_END();
}