| 1 | /* Self tests for gdb_environ for GDB, the GNU debugger. |
| 2 | |
| 3 | Copyright (C) 2017-2019 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 | |
| 25 | static const char gdb_selftest_env_var[] = "GDB_SELFTEST_ENVIRON"; |
| 26 | |
| 27 | static bool |
| 28 | set_contains (const std::set<std::string> &set, std::string key) |
| 29 | { |
| 30 | return set.find (key) != set.end (); |
| 31 | } |
| 32 | |
| 33 | namespace selftests { |
| 34 | namespace gdb_environ_tests { |
| 35 | |
| 36 | /* Test if the vector is initialized in a correct way. */ |
| 37 | |
| 38 | static void |
| 39 | test_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 | |
| 55 | static void |
| 56 | test_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 | |
| 72 | static void |
| 73 | test_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 | |
| 93 | static void |
| 94 | test_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 | |
| 121 | static void |
| 122 | test_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 | |
| 144 | static void |
| 145 | test_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 | |
| 161 | static void |
| 162 | test_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 | |
| 187 | static void |
| 188 | test_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 | |
| 213 | static void |
| 214 | test_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 | |
| 240 | static void |
| 241 | test_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 | |
| 268 | static void |
| 269 | run_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 | |
| 301 | void |
| 302 | _initialize_environ_selftests () |
| 303 | { |
| 304 | selftests::register_test ("gdb_environ", |
| 305 | selftests::gdb_environ_tests::run_tests); |
| 306 | } |