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

/* Unity fixtures */
void setUp(void) {
  /* No global setup required */
}
void tearDown(void) {
  /* No global teardown required */
}

/* Helper: create a struct line with optional fields array and optional buffer.
   Note: freeline() frees only line->fields and line->buf.buffer, then delseq
   frees the struct line itself. We therefore avoid allocating per-field beg,
   to prevent leaks not handled by freeline. */
static struct line* make_line_with(size_t nfields, size_t bufsize_bytes)
{
  struct line *ln = (struct line*) malloc(sizeof *ln);
  if (!ln) return NULL;
  memset(ln, 0, sizeof *ln);

  if (nfields > 0) {
    ln->fields = (struct field*) malloc(nfields * sizeof *ln->fields);
    if (!ln->fields) {
      free(ln);
      return NULL;
    }
    /* We do not allocate per-field beg pointers; leave them NULL with len 0. */
    for (size_t i = 0; i < nfields; i++) {
      ln->fields[i].beg = NULL;
      ln->fields[i].len = 0;
    }
    ln->nfields = (idx_t)nfields;
    ln->nfields_allocated = (idx_t)nfields;
  } else {
    ln->fields = NULL;
    ln->nfields = 0;
    ln->nfields_allocated = 0;
  }

  if (bufsize_bytes > 0) {
    ln->buf.buffer = (char*) malloc(bufsize_bytes);
    if (ln->buf.buffer) {
      memset(ln->buf.buffer, 'X', bufsize_bytes);
      ln->buf.length = (idx_t)bufsize_bytes;
    } else {
      ln->buf.buffer = NULL;
      ln->buf.length = 0;
    }
  } else {
    ln->buf.buffer = NULL;
    ln->buf.length = 0;
  }

  return ln;
}

/* Test: delseq on an empty sequence (alloc=0, lines=NULL) should be a no-op */
static void test_delseq_empty_seq(void)
{
  struct seq s;
  s.count = 0;
  s.alloc = 0;
  s.lines = NULL;

  delseq(&s);

  TEST_ASSERT_EQUAL_UINT(0, (unsigned)s.alloc);
  TEST_ASSERT_NULL(s.lines);
}

/* Test: delseq on a lines array populated entirely with NULL entries */
static void test_delseq_null_entries_only(void)
{
  struct seq s;
  s.count = 0;
  s.alloc = 5;
  s.lines = (struct line**) calloc(s.alloc, sizeof *s.lines);
  TEST_ASSERT_NOT_NULL(s.lines);

  void *oldptr = s.lines;
  delseq(&s);

  /* delseq does not modify s.alloc nor s.lines pointer value */
  TEST_ASSERT_EQUAL_UINT(5, (unsigned)s.alloc);
  TEST_ASSERT_EQUAL_PTR(oldptr, s.lines);
}

/* Test: delseq on a sequence with mixed entries (NULL and allocated lines).
   Verifies it safely frees buffers and fields of non-NULL lines. */
static void test_delseq_mixed_entries(void)
{
  struct seq s;
  s.count = 0;
  s.alloc = 4;
  s.lines = (struct line**) malloc(s.alloc * sizeof *s.lines);
  TEST_ASSERT_NOT_NULL(s.lines);

  s.lines[0] = make_line_with(0, 0);     /* No fields, no buffer */
  s.lines[1] = NULL;                      /* NULL entry */
  s.lines[2] = make_line_with(3, 64);     /* Fields array and a buffer */
  s.lines[3] = make_line_with(0, 128);    /* Only buffer */

  TEST_ASSERT_NOT_NULL(s.lines[0]);
  TEST_ASSERT_NULL(s.lines[1]);
  TEST_ASSERT_NOT_NULL(s.lines[2]);
  TEST_ASSERT_NOT_NULL(s.lines[3]);

  void *oldptr = s.lines;
  delseq(&s);

  TEST_ASSERT_EQUAL_UINT(4, (unsigned)s.alloc);
  TEST_ASSERT_EQUAL_PTR(oldptr, s.lines);
}

/* Test: delseq on a larger array to exercise loop bounds and performance */
static void test_delseq_large_array(void)
{
  const size_t N = 1000;
  struct seq s;
  s.count = 0;
  s.alloc = (idx_t)N;
  s.lines = (struct line**) malloc(N * sizeof *s.lines);
  TEST_ASSERT_NOT_NULL(s.lines);

  /* Populate every 10th element; leave others NULL */
  for (size_t i = 0; i < N; i++) {
    if (i % 10 == 0) {
      s.lines[i] = make_line_with(2, 32);
      TEST_ASSERT_NOT_NULL_MESSAGE(s.lines[i], "Allocation failed in setup");
    } else {
      s.lines[i] = NULL;
    }
  }

  void *oldptr = s.lines;
  delseq(&s);

  TEST_ASSERT_EQUAL_UINT(N, (unsigned)s.alloc);
  TEST_ASSERT_EQUAL_PTR(oldptr, s.lines);
}

int main(void)
{
  UNITY_BEGIN();

  RUN_TEST(test_delseq_empty_seq);
  RUN_TEST(test_delseq_null_entries_only);
  RUN_TEST(test_delseq_mixed_entries);
  RUN_TEST(test_delseq_large_array);

  return UNITY_END();
}