1/* Self tests for gdb_environ for GDB, the GNU debugger.
2
3   Copyright (C) 2017-2020 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20#include "defs.h"
21#include "gdbsupport/selftest.h"
22#include "gdbsupport/environ.h"
23#include "diagnostics.h"
24
25static const char gdb_selftest_env_var[] = "GDB_SELFTEST_ENVIRON";
26
27static bool
28set_contains (const std::set<std::string> &set, std::string key)
29{
30  return set.find (key) != set.end ();
31}
32
33namespace selftests {
34namespace gdb_environ_tests {
35
36/* Test if the vector is initialized in a correct way.  */
37
38static void
39test_vector_initialization ()
40{
41  gdb_environ env;
42
43  /* When the vector is initialized, there should always be one NULL
44     element in it.  */
45  SELF_CHECK (env.envp ()[0] == NULL);
46  SELF_CHECK (env.user_set_env ().size () == 0);
47  SELF_CHECK (env.user_unset_env ().size () == 0);
48
49  /* Make sure that there is no other element.  */
50  SELF_CHECK (env.get ("PWD") == NULL);
51}
52
53/* Test initialization using the host's environ.  */
54
55static void
56test_init_from_host_environ ()
57{
58  /* Initialize the environment vector using the host's environ.  */
59  gdb_environ env = gdb_environ::from_host_environ ();
60
61  /* The user-set and user-unset lists must be empty.  */
62  SELF_CHECK (env.user_set_env ().size () == 0);
63  SELF_CHECK (env.user_unset_env ().size () == 0);
64
65  /* Our test environment variable should be present at the
66     vector.  */
67  SELF_CHECK (strcmp (env.get (gdb_selftest_env_var), "1") == 0);
68}
69
70/* Test reinitialization using the host's environ.  */
71
72static void
73test_reinit_from_host_environ ()
74{
75  /* Reinitialize our environ vector using the host environ.  We
76     should be able to see one (and only one) instance of the test
77     variable.  */
78  gdb_environ env = gdb_environ::from_host_environ ();
79  env = gdb_environ::from_host_environ ();
80  char **envp = env.envp ();
81  int num_found = 0;
82
83  for (size_t i = 0; envp[i] != NULL; ++i)
84    if (strcmp (envp[i], "GDB_SELFTEST_ENVIRON=1") == 0)
85      ++num_found;
86  SELF_CHECK (num_found == 1);
87}
88
89/* Test the case when we set a variable A, then set a variable B,
90   then unset A, and make sure that we cannot find A in the environ
91   vector, but can still find B.  */
92
93static void
94test_set_A_unset_B_unset_A_cannot_find_A_can_find_B ()
95{
96  gdb_environ env;
97
98  env.set ("GDB_SELFTEST_ENVIRON_1", "aaa");
99  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_1"), "aaa") == 0);
100  /* User-set environ var list must contain one element.  */
101  SELF_CHECK (env.user_set_env ().size () == 1);
102  SELF_CHECK (set_contains (env.user_set_env (),
103			    std::string ("GDB_SELFTEST_ENVIRON_1=aaa")));
104
105  env.set ("GDB_SELFTEST_ENVIRON_2", "bbb");
106  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0);
107
108  env.unset ("GDB_SELFTEST_ENVIRON_1");
109  SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON_1") == NULL);
110  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0);
111
112  /* The user-set environ var list must contain only one element
113     now.  */
114  SELF_CHECK (set_contains (env.user_set_env (),
115			    std::string ("GDB_SELFTEST_ENVIRON_2=bbb")));
116  SELF_CHECK (env.user_set_env ().size () == 1);
117}
118
119/* Check if unset followed by a set in an empty vector works.  */
120
121static void
122test_unset_set_empty_vector ()
123{
124  gdb_environ env;
125
126  env.set ("PWD", "test");
127  SELF_CHECK (strcmp (env.get ("PWD"), "test") == 0);
128  SELF_CHECK (set_contains (env.user_set_env (), std::string ("PWD=test")));
129  SELF_CHECK (env.user_unset_env ().size () == 0);
130  /* The second element must be NULL.  */
131  SELF_CHECK (env.envp ()[1] == NULL);
132  SELF_CHECK (env.user_set_env ().size () == 1);
133  env.unset ("PWD");
134  SELF_CHECK (env.envp ()[0] == NULL);
135  SELF_CHECK (env.user_set_env ().size () == 0);
136  SELF_CHECK (env.user_unset_env ().size () == 1);
137  SELF_CHECK (set_contains (env.user_unset_env (), std::string ("PWD")));
138}
139
140/* When we clear our environ vector, there should be only one
141   element on it (NULL), and we shouldn't be able to get our test
142   variable.  */
143
144static void
145test_vector_clear ()
146{
147  gdb_environ env;
148
149  env.set (gdb_selftest_env_var, "1");
150
151  env.clear ();
152  SELF_CHECK (env.envp ()[0] == NULL);
153  SELF_CHECK (env.user_set_env ().size () == 0);
154  SELF_CHECK (env.user_unset_env ().size () == 0);
155  SELF_CHECK (env.get (gdb_selftest_env_var) == NULL);
156}
157
158/* Test that after a std::move the moved-from object is left at a
159   valid state (i.e., its only element is NULL).  */
160
161static void
162test_std_move ()
163{
164  gdb_environ env;
165  gdb_environ env2;
166
167  env.set ("A", "1");
168  SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
169  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
170  SELF_CHECK (env.user_set_env ().size () == 1);
171
172  env2 = std::move (env);
173  SELF_CHECK (env.envp ()[0] == NULL);
174  SELF_CHECK (strcmp (env2.get ("A"), "1") == 0);
175  SELF_CHECK (env2.envp ()[1] == NULL);
176  SELF_CHECK (env.user_set_env ().size () == 0);
177  SELF_CHECK (set_contains (env2.user_set_env (), std::string ("A=1")));
178  SELF_CHECK (env2.user_set_env ().size () == 1);
179  env.set ("B", "2");
180  SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
181  SELF_CHECK (env.envp ()[1] == NULL);
182}
183
184/* Test that the move constructor leaves everything at a valid
185   state.  */
186
187static void
188test_move_constructor ()
189{
190  gdb_environ env;
191
192  env.set ("A", "1");
193  SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
194  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
195
196  gdb_environ env2 = std::move (env);
197  SELF_CHECK (env.envp ()[0] == NULL);
198  SELF_CHECK (env.user_set_env ().size () == 0);
199  SELF_CHECK (strcmp (env2.get ("A"), "1") == 0);
200  SELF_CHECK (env2.envp ()[1] == NULL);
201  SELF_CHECK (set_contains (env2.user_set_env (), std::string ("A=1")));
202  SELF_CHECK (env2.user_set_env ().size () == 1);
203
204  env.set ("B", "2");
205  SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
206  SELF_CHECK (env.envp ()[1] == NULL);
207  SELF_CHECK (set_contains (env.user_set_env (), std::string ("B=2")));
208  SELF_CHECK (env.user_set_env ().size () == 1);
209}
210
211/* Test that self-moving works.  */
212
213static void
214test_self_move ()
215{
216  gdb_environ env;
217
218  /* Test self-move.  */
219  env.set ("A", "1");
220  SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
221  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
222  SELF_CHECK (env.user_set_env ().size () == 1);
223
224  /* Some compilers warn about moving to self, but that's precisely what we want
225     to test here, so turn this warning off.  */
226  DIAGNOSTIC_PUSH
227  DIAGNOSTIC_IGNORE_SELF_MOVE
228  env = std::move (env);
229  DIAGNOSTIC_POP
230
231  SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
232  SELF_CHECK (strcmp (env.envp ()[0], "A=1") == 0);
233  SELF_CHECK (env.envp ()[1] == NULL);
234  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
235  SELF_CHECK (env.user_set_env ().size () == 1);
236}
237
238/* Test if set/unset/reset works.  */
239
240static void
241test_set_unset_reset ()
242{
243  gdb_environ env = gdb_environ::from_host_environ ();
244
245  /* Our test variable should already be present in the host's environment.  */
246  SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") != NULL);
247
248  /* Set our test variable to another value.  */
249  env.set ("GDB_SELFTEST_ENVIRON", "test");
250  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "test") == 0);
251  SELF_CHECK (env.user_set_env ().size () == 1);
252  SELF_CHECK (env.user_unset_env ().size () == 0);
253
254  /* And unset our test variable.  The variable still exists in the
255     host's environment, but doesn't exist in our vector.  */
256  env.unset ("GDB_SELFTEST_ENVIRON");
257  SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
258  SELF_CHECK (env.user_set_env ().size () == 0);
259  SELF_CHECK (env.user_unset_env ().size () == 1);
260  SELF_CHECK (set_contains (env.user_unset_env (),
261			    std::string ("GDB_SELFTEST_ENVIRON")));
262
263  /* Re-set the test variable.  */
264  env.set ("GDB_SELFTEST_ENVIRON", "1");
265  SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "1") == 0);
266}
267
268static void
269run_tests ()
270{
271  /* Set a test environment variable.  */
272  if (setenv ("GDB_SELFTEST_ENVIRON", "1", 1) != 0)
273    error (_("Could not set environment variable for testing."));
274
275  test_vector_initialization ();
276
277  test_unset_set_empty_vector ();
278
279  test_init_from_host_environ ();
280
281  test_set_unset_reset ();
282
283  test_vector_clear ();
284
285  test_reinit_from_host_environ ();
286
287  /* Get rid of our test variable.  We won't need it anymore.  */
288  unsetenv ("GDB_SELFTEST_ENVIRON");
289
290  test_set_A_unset_B_unset_A_cannot_find_A_can_find_B ();
291
292  test_std_move ();
293
294  test_move_constructor ();
295
296  test_self_move ();
297}
298} /* namespace gdb_environ */
299} /* namespace selftests */
300
301void _initialize_environ_selftests ();
302void
303_initialize_environ_selftests ()
304{
305  selftests::register_test ("gdb_environ",
306			    selftests::gdb_environ_tests::run_tests);
307}
308