File size: 5,876 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
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>

/* Note:
   - This test file is included at the end of env.c, so it shares the same
     translation unit and can access the static symbols:
       - usvars, usvars_used, usvars_alloc
       - unset_envvars()
   - We only manipulate usvars/usvars_used/usvars_alloc; we do not redefine any
     symbol from the program.
*/

static char const **allocated_usvars = NULL; /* Track array we allocate for cleanup */

/* Helper: set the list of variables to unset. Copies the pointer list, not the strings. */
static void prepare_usvars_list(char const *const *list, size_t count)
{
  /* Free any prior allocation from a previous test run */
  if (allocated_usvars)
    {
      free((void *)allocated_usvars);
      allocated_usvars = NULL;
    }

  if (count == 0 || list == NULL)
    {
      usvars = NULL;
      usvars_used = 0;
      usvars_alloc = 0;
      return;
    }

  char const **arr = (char const **)malloc(sizeof(char const *) * count);
  TEST_ASSERT_NOT_NULL(arr);
  for (size_t i = 0; i < count; i++)
    arr[i] = list[i];

  usvars = arr;
  usvars_used = (typeof(usvars_used))count;
  usvars_alloc = (typeof(usvars_alloc))count;
  allocated_usvars = arr;
}

/* Helper: make a unique environment variable name. Caller frees the returned string. */
static char *make_unique_name(const char *prefix)
{
  char buf[128];
  static unsigned counter = 0;
  snprintf(buf, sizeof buf, "%s_%ld_%u", prefix, (long)getpid(), ++counter);
  return strdup(buf);
}

void setUp(void)
{
  /* Reset our managed allocation; program globals will be set per-test */
  if (allocated_usvars)
    {
      free((void *)allocated_usvars);
      allocated_usvars = NULL;
    }
  usvars = NULL;
  usvars_used = 0;
  usvars_alloc = 0;
}

void tearDown(void)
{
  if (allocated_usvars)
    {
      free((void *)allocated_usvars);
      allocated_usvars = NULL;
    }
  /* Leave environment as modified by the test; each test sets/clears variables it needs */
}

/* Test: unsetting a single existing variable removes it from the environment. */
static void test_unset_envvars_single_existing(void)
{
  char *name = make_unique_name("ENV_TEST_ONE");
  TEST_ASSERT_NOT_NULL(name);

  /* Ensure the variable exists */
  TEST_ASSERT_EQUAL_INT(0, setenv(name, "value", 1));
  TEST_ASSERT_NOT_NULL(getenv(name));

  char const *list[] = { name };
  prepare_usvars_list(list, 1);

  unset_envvars();

  TEST_ASSERT_NULL(getenv(name));

  free(name);
}

/* Test: unsetting multiple variables including one that does not exist. */
static void test_unset_envvars_multiple_existing_and_nonexistent(void)
{
  char *n1 = make_unique_name("ENV_TEST_MULTI1");
  char *n2 = make_unique_name("ENV_TEST_MULTI2");
  char *n3 = make_unique_name("ENV_TEST_MULTI3_NOEXIST");
  TEST_ASSERT_NOT_NULL(n1);
  TEST_ASSERT_NOT_NULL(n2);
  TEST_ASSERT_NOT_NULL(n3);

  /* Create two variables, leave third non-existent */
  TEST_ASSERT_EQUAL_INT(0, setenv(n1, "v1", 1));
  TEST_ASSERT_EQUAL_INT(0, setenv(n2, "v2", 1));
  TEST_ASSERT_NOT_NULL(getenv(n1));
  TEST_ASSERT_NOT_NULL(getenv(n2));
  TEST_ASSERT_NULL(getenv(n3));

  char const *list[] = { n1, n2, n3 };
  prepare_usvars_list(list, 3);

  unset_envvars();

  TEST_ASSERT_NULL(getenv(n1));
  TEST_ASSERT_NULL(getenv(n2));
  TEST_ASSERT_NULL(getenv(n3));

  free(n1);
  free(n2);
  free(n3);
}

/* Test: unsetting a non-existent variable should not error and should remain non-existent. */
static void test_unset_envvars_nonexistent_only(void)
{
  char *name = make_unique_name("ENV_TEST_NOEXIST");
  TEST_ASSERT_NOT_NULL(name);

  /* Ensure it does not exist */
  unsetenv(name);
  TEST_ASSERT_NULL(getenv(name));

  char const *list[] = { name };
  prepare_usvars_list(list, 1);

  unset_envvars();

  TEST_ASSERT_NULL(getenv(name));

  free(name);
}

/* Test: empty unset list should be a no-op (does not unset other variables). */
static void test_unset_envvars_empty_list_noop(void)
{
  char *persist = make_unique_name("ENV_TEST_PERSIST");
  TEST_ASSERT_NOT_NULL(persist);

  TEST_ASSERT_EQUAL_INT(0, setenv(persist, "keep", 1));
  TEST_ASSERT_NOT_NULL(getenv(persist));

  prepare_usvars_list(NULL, 0); /* No variables to unset */

  unset_envvars();

  TEST_ASSERT_NOT_NULL(getenv(persist));

  /* Cleanup: remove the variable we created */
  TEST_ASSERT_EQUAL_INT(0, unsetenv(persist));
  TEST_ASSERT_NULL(getenv(persist));

  free(persist);
}

/* Test: invalid variable name (contains '=') should cause error() to exit with EXIT_CANCELED.
   Run in a child process to avoid terminating the whole test runner. */
static void test_unset_envvars_invalid_name_triggers_error(void)
{
  fflush(stdout);
    fflush(stderr);
    pid_t pid = fork();
  TEST_ASSERT_MESSAGE(pid >= 0, "fork() failed");

  if (pid == 0)
    {
      /* Child: set invalid name and call function; should exit via error(). */
      char const *list[] = { "BAD=NAME" };
      prepare_usvars_list(list, 1);

      /* Calling unset_envvars should invoke error(EXIT_CANCELED, ...) and exit. */
      unset_envvars();

      /* If we get here, it did not exit as expected. Use a distinct exit code. */
      _exit(0xEE);
    }

  int status = 0;
  pid_t w = waitpid(pid, &status, 0);
  TEST_ASSERT_EQUAL_INT(pid, w);
  TEST_ASSERT_TRUE(WIFEXITED(status));

  int es = WEXITSTATUS(status);
  /* EXIT_CANCELED is available from the program's headers included earlier. */
  TEST_ASSERT_EQUAL_INT(EXIT_CANCELED, es);
}

int main(void)
{
  UNITY_BEGIN();

  RUN_TEST(test_unset_envvars_single_existing);
  RUN_TEST(test_unset_envvars_multiple_existing_and_nonexistent);
  RUN_TEST(test_unset_envvars_nonexistent_only);
  RUN_TEST(test_unset_envvars_empty_list_noop);
  RUN_TEST(test_unset_envvars_invalid_name_triggers_error);

  return UNITY_END();
}