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

/* setUp and tearDown are required by Unity */
void setUp(void) {
  /* Disable order checking to avoid dereferencing any stale prevline pointers
     left from previous tests, since we free lines after each test. */
  check_input_order = CHECK_ORDER_DISABLED;
  /* Reset the unpairable flag just in case any logic consults it. */
  seen_unpairable = false;
}

void tearDown(void) {
  /* Ensure any spare lines are freed to avoid leaks across tests. */
  free_spareline();
}

/* Helper: create a temporary file preloaded with 'content' and rewound. */
static FILE* make_tempfile_with(const char* content) {
  FILE *fp = tmpfile();
  TEST_ASSERT_NOT_NULL_MESSAGE(fp, "tmpfile() failed");
  if (content && *content) {
    size_t len = strlen(content);
    TEST_ASSERT_EQUAL_MESSAGE(len, fwrite(content, 1, len, fp),
                              "Failed to write content to tmp file");
  }
  fflush(fp);
  rewind(fp);
  return fp;
}

/* Validate a field equals the given string (length and bytes). */
static void assert_field_equals(const struct field *f, const char *s) {
  size_t slen = strlen(s);
  TEST_ASSERT_EQUAL_size_t(slen, (size_t)f->len);
  if (slen > 0) {
    TEST_ASSERT_NOT_NULL(f->beg);
    TEST_ASSERT_EQUAL_INT(0, memcmp(f->beg, s, slen));
  }
}

/* Test: getseq reads a single line and parses three fields separated by blanks. */
void test_getseq_reads_single_line_and_fields(void) {
  FILE *fp = make_tempfile_with("a b c\n");

  struct seq s;
  initseq(&s);

  bool ok = getseq(fp, &s, 1);
  TEST_ASSERT_TRUE(ok);
  TEST_ASSERT_TRUE(s.count == 1);
  TEST_ASSERT_NOT_NULL(s.lines);
  TEST_ASSERT_TRUE(s.alloc >= 1);
  TEST_ASSERT_NOT_NULL(s.lines[0]);

  struct line *l = s.lines[0];
  TEST_ASSERT_NOT_NULL(l);
  TEST_ASSERT_TRUE(l->nfields == 3);

  TEST_ASSERT_NOT_NULL(l->fields);
  assert_field_equals(&l->fields[0], "a");
  assert_field_equals(&l->fields[1], "b");
  assert_field_equals(&l->fields[2], "c");

  delseq(&s);
  fclose(fp);
}

/* Test: getseq returns false on EOF and does not increment count. */
void test_getseq_returns_false_on_empty_file(void) {
  FILE *fp = make_tempfile_with("");

  struct seq s;
  initseq(&s);

  bool ok = getseq(fp, &s, 1);
  TEST_ASSERT_FALSE(ok);
  TEST_ASSERT_TRUE(s.count == 0);
  /* Storage should have been allocated, and the first slot initialized to NULL. */
  TEST_ASSERT_NOT_NULL(s.lines);
  TEST_ASSERT_TRUE(s.alloc >= 1);
  TEST_ASSERT_NULL(s.lines[0]);

  delseq(&s);
  fclose(fp);
}

/* Test: getseq reads multiple lines and increments count appropriately. */
void test_getseq_reads_multiple_lines(void) {
  FILE *fp = make_tempfile_with("x\ny\nz\n");

  struct seq s;
  initseq(&s);

  bool ok1 = getseq(fp, &s, 1);
  TEST_ASSERT_TRUE(ok1);
  TEST_ASSERT_TRUE(s.count == 1);
  TEST_ASSERT_NOT_NULL(s.lines[0]);
  TEST_ASSERT_TRUE(s.lines[0]->nfields == 1);
  assert_field_equals(&s.lines[0]->fields[0], "x");

  bool ok2 = getseq(fp, &s, 1);
  TEST_ASSERT_TRUE(ok2);
  TEST_ASSERT_TRUE(s.count == 2);
  TEST_ASSERT_NOT_NULL(s.lines[1]);
  TEST_ASSERT_TRUE(s.lines[1]->nfields == 1);
  assert_field_equals(&s.lines[1]->fields[0], "y");

  bool ok3 = getseq(fp, &s, 1);
  TEST_ASSERT_TRUE(ok3);
  TEST_ASSERT_TRUE(s.count == 3);
  TEST_ASSERT_NOT_NULL(s.lines[2]);
  TEST_ASSERT_TRUE(s.lines[2]->nfields == 1);
  assert_field_equals(&s.lines[2]->fields[0], "z");

  /* Next call should hit EOF and return false; count should remain unchanged. */
  bool ok4 = getseq(fp, &s, 1);
  TEST_ASSERT_FALSE(ok4);
  TEST_ASSERT_TRUE(s.count == 3);

  delseq(&s);
  fclose(fp);
}

/* Test: getseq works for both whichfile values independently. */
void test_getseq_works_for_both_files(void) {
  FILE *fp1 = make_tempfile_with("a\nb\n");
  FILE *fp2 = make_tempfile_with("c\nd\n");

  struct seq s1, s2;
  initseq(&s1);
  initseq(&s2);

  /* Read first line from file 1 */
  TEST_ASSERT_TRUE(getseq(fp1, &s1, 1));
  TEST_ASSERT_TRUE(s1.count == 1);
  TEST_ASSERT_TRUE(s1.lines[0]->nfields == 1);
  assert_field_equals(&s1.lines[0]->fields[0], "a");

  /* Read first line from file 2 */
  TEST_ASSERT_TRUE(getseq(fp2, &s2, 2));
  TEST_ASSERT_TRUE(s2.count == 1);
  TEST_ASSERT_TRUE(s2.lines[0]->nfields == 1);
  assert_field_equals(&s2.lines[0]->fields[0], "c");

  /* Read second line from each */
  TEST_ASSERT_TRUE(getseq(fp1, &s1, 1));
  TEST_ASSERT_TRUE(s1.count == 2);
  assert_field_equals(&s1.lines[1]->fields[0], "b");

  TEST_ASSERT_TRUE(getseq(fp2, &s2, 2));
  TEST_ASSERT_TRUE(s2.count == 2);
  assert_field_equals(&s2.lines[1]->fields[0], "d");

  /* EOF on both */
  TEST_ASSERT_FALSE(getseq(fp1, &s1, 1));
  TEST_ASSERT_FALSE(getseq(fp2, &s2, 2));

  delseq(&s1);
  delseq(&s2);
  fclose(fp1);
  fclose(fp2);
}

/* Test: handle a line without a trailing newline */
void test_getseq_handles_line_without_trailing_newline(void) {
  FILE *fp = make_tempfile_with("no-trailing-newline");

  struct seq s;
  initseq(&s);

  TEST_ASSERT_TRUE(getseq(fp, &s, 1));
  TEST_ASSERT_TRUE(s.count == 1);
  TEST_ASSERT_NOT_NULL(s.lines[0]);
  TEST_ASSERT_TRUE(s.lines[0]->nfields == 1);
  assert_field_equals(&s.lines[0]->fields[0], "no-trailing-newline");

  /* Next should be EOF */
  TEST_ASSERT_FALSE(getseq(fp, &s, 1));

  delseq(&s);
  fclose(fp);
}

int main(void) {
  UNITY_BEGIN();
  RUN_TEST(test_getseq_reads_single_line_and_fields);
  RUN_TEST(test_getseq_returns_false_on_empty_file);
  RUN_TEST(test_getseq_reads_multiple_lines);
  RUN_TEST(test_getseq_works_for_both_files);
  RUN_TEST(test_getseq_handles_line_without_trailing_newline);
  return UNITY_END();
}