|  | /* Self tests for gdb_environ for GDB, the GNU debugger. | 
|  |  | 
|  | Copyright (C) 2017-2023 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "defs.h" | 
|  | #include "gdbsupport/selftest.h" | 
|  | #include "gdbsupport/environ.h" | 
|  | #include "diagnostics.h" | 
|  |  | 
|  | static const char gdb_selftest_env_var[] = "GDB_SELFTEST_ENVIRON"; | 
|  |  | 
|  | static bool | 
|  | set_contains (const std::set<std::string> &set, std::string key) | 
|  | { | 
|  | return set.find (key) != set.end (); | 
|  | } | 
|  |  | 
|  | namespace selftests { | 
|  | namespace gdb_environ_tests { | 
|  |  | 
|  | /* Test if the vector is initialized in a correct way.  */ | 
|  |  | 
|  | static void | 
|  | test_vector_initialization () | 
|  | { | 
|  | gdb_environ env; | 
|  |  | 
|  | /* When the vector is initialized, there should always be one NULL | 
|  | element in it.  */ | 
|  | SELF_CHECK (env.envp ()[0] == NULL); | 
|  | SELF_CHECK (env.user_set_env ().size () == 0); | 
|  | SELF_CHECK (env.user_unset_env ().size () == 0); | 
|  |  | 
|  | /* Make sure that there is no other element.  */ | 
|  | SELF_CHECK (env.get ("PWD") == NULL); | 
|  | } | 
|  |  | 
|  | /* Test initialization using the host's environ.  */ | 
|  |  | 
|  | static void | 
|  | test_init_from_host_environ () | 
|  | { | 
|  | /* Initialize the environment vector using the host's environ.  */ | 
|  | gdb_environ env = gdb_environ::from_host_environ (); | 
|  |  | 
|  | /* The user-set and user-unset lists must be empty.  */ | 
|  | SELF_CHECK (env.user_set_env ().size () == 0); | 
|  | SELF_CHECK (env.user_unset_env ().size () == 0); | 
|  |  | 
|  | /* Our test environment variable should be present at the | 
|  | vector.  */ | 
|  | SELF_CHECK (strcmp (env.get (gdb_selftest_env_var), "1") == 0); | 
|  | } | 
|  |  | 
|  | /* Test reinitialization using the host's environ.  */ | 
|  |  | 
|  | static void | 
|  | test_reinit_from_host_environ () | 
|  | { | 
|  | /* Reinitialize our environ vector using the host environ.  We | 
|  | should be able to see one (and only one) instance of the test | 
|  | variable.  */ | 
|  | gdb_environ env = gdb_environ::from_host_environ (); | 
|  | env = gdb_environ::from_host_environ (); | 
|  | char **envp = env.envp (); | 
|  | int num_found = 0; | 
|  |  | 
|  | for (size_t i = 0; envp[i] != NULL; ++i) | 
|  | if (strcmp (envp[i], "GDB_SELFTEST_ENVIRON=1") == 0) | 
|  | ++num_found; | 
|  | SELF_CHECK (num_found == 1); | 
|  | } | 
|  |  | 
|  | /* Test the case when we set a variable A, then set a variable B, | 
|  | then unset A, and make sure that we cannot find A in the environ | 
|  | vector, but can still find B.  */ | 
|  |  | 
|  | static void | 
|  | test_set_A_unset_B_unset_A_cannot_find_A_can_find_B () | 
|  | { | 
|  | gdb_environ env; | 
|  |  | 
|  | env.set ("GDB_SELFTEST_ENVIRON_1", "aaa"); | 
|  | SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_1"), "aaa") == 0); | 
|  | /* User-set environ var list must contain one element.  */ | 
|  | SELF_CHECK (env.user_set_env ().size () == 1); | 
|  | SELF_CHECK (set_contains (env.user_set_env (), | 
|  | std::string ("GDB_SELFTEST_ENVIRON_1=aaa"))); | 
|  |  | 
|  | env.set ("GDB_SELFTEST_ENVIRON_2", "bbb"); | 
|  | SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0); | 
|  |  | 
|  | env.unset ("GDB_SELFTEST_ENVIRON_1"); | 
|  | SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON_1") == NULL); | 
|  | SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0); | 
|  |  | 
|  | /* The user-set environ var list must contain only one element | 
|  | now.  */ | 
|  | SELF_CHECK (set_contains (env.user_set_env (), | 
|  | std::string ("GDB_SELFTEST_ENVIRON_2=bbb"))); | 
|  | SELF_CHECK (env.user_set_env ().size () == 1); | 
|  | } | 
|  |  | 
|  | /* Check if unset followed by a set in an empty vector works.  */ | 
|  |  | 
|  | static void | 
|  | test_unset_set_empty_vector () | 
|  | { | 
|  | gdb_environ env; | 
|  |  | 
|  | env.set ("PWD", "test"); | 
|  | SELF_CHECK (strcmp (env.get ("PWD"), "test") == 0); | 
|  | SELF_CHECK (set_contains (env.user_set_env (), std::string ("PWD=test"))); | 
|  | SELF_CHECK (env.user_unset_env ().size () == 0); | 
|  | /* The second element must be NULL.  */ | 
|  | SELF_CHECK (env.envp ()[1] == NULL); | 
|  | SELF_CHECK (env.user_set_env ().size () == 1); | 
|  | env.unset ("PWD"); | 
|  | SELF_CHECK (env.envp ()[0] == NULL); | 
|  | SELF_CHECK (env.user_set_env ().size () == 0); | 
|  | SELF_CHECK (env.user_unset_env ().size () == 1); | 
|  | SELF_CHECK (set_contains (env.user_unset_env (), std::string ("PWD"))); | 
|  | } | 
|  |  | 
|  | /* When we clear our environ vector, there should be only one | 
|  | element on it (NULL), and we shouldn't be able to get our test | 
|  | variable.  */ | 
|  |  | 
|  | static void | 
|  | test_vector_clear () | 
|  | { | 
|  | gdb_environ env; | 
|  |  | 
|  | env.set (gdb_selftest_env_var, "1"); | 
|  |  | 
|  | env.clear (); | 
|  | SELF_CHECK (env.envp ()[0] == NULL); | 
|  | SELF_CHECK (env.user_set_env ().size () == 0); | 
|  | SELF_CHECK (env.user_unset_env ().size () == 0); | 
|  | SELF_CHECK (env.get (gdb_selftest_env_var) == NULL); | 
|  | } | 
|  |  | 
|  | /* Test that after a std::move the moved-from object is left at a | 
|  | valid state (i.e., its only element is NULL).  */ | 
|  |  | 
|  | static void | 
|  | test_std_move () | 
|  | { | 
|  | gdb_environ env; | 
|  | gdb_environ env2; | 
|  |  | 
|  | env.set ("A", "1"); | 
|  | SELF_CHECK (strcmp (env.get ("A"), "1") == 0); | 
|  | SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1"))); | 
|  | SELF_CHECK (env.user_set_env ().size () == 1); | 
|  |  | 
|  | env2 = std::move (env); | 
|  | SELF_CHECK (env.envp ()[0] == NULL); | 
|  | SELF_CHECK (strcmp (env2.get ("A"), "1") == 0); | 
|  | SELF_CHECK (env2.envp ()[1] == NULL); | 
|  | SELF_CHECK (env.user_set_env ().size () == 0); | 
|  | SELF_CHECK (set_contains (env2.user_set_env (), std::string ("A=1"))); | 
|  | SELF_CHECK (env2.user_set_env ().size () == 1); | 
|  | env.set ("B", "2"); | 
|  | SELF_CHECK (strcmp (env.get ("B"), "2") == 0); | 
|  | SELF_CHECK (env.envp ()[1] == NULL); | 
|  | } | 
|  |  | 
|  | /* Test that the move constructor leaves everything at a valid | 
|  | state.  */ | 
|  |  | 
|  | static void | 
|  | test_move_constructor () | 
|  | { | 
|  | gdb_environ env; | 
|  |  | 
|  | env.set ("A", "1"); | 
|  | SELF_CHECK (strcmp (env.get ("A"), "1") == 0); | 
|  | SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1"))); | 
|  |  | 
|  | gdb_environ env2 = std::move (env); | 
|  | SELF_CHECK (env.envp ()[0] == NULL); | 
|  | SELF_CHECK (env.user_set_env ().size () == 0); | 
|  | SELF_CHECK (strcmp (env2.get ("A"), "1") == 0); | 
|  | SELF_CHECK (env2.envp ()[1] == NULL); | 
|  | SELF_CHECK (set_contains (env2.user_set_env (), std::string ("A=1"))); | 
|  | SELF_CHECK (env2.user_set_env ().size () == 1); | 
|  |  | 
|  | env.set ("B", "2"); | 
|  | SELF_CHECK (strcmp (env.get ("B"), "2") == 0); | 
|  | SELF_CHECK (env.envp ()[1] == NULL); | 
|  | SELF_CHECK (set_contains (env.user_set_env (), std::string ("B=2"))); | 
|  | SELF_CHECK (env.user_set_env ().size () == 1); | 
|  | } | 
|  |  | 
|  | /* Test that self-moving works.  */ | 
|  |  | 
|  | static void | 
|  | test_self_move () | 
|  | { | 
|  | gdb_environ env; | 
|  |  | 
|  | /* Test self-move.  */ | 
|  | env.set ("A", "1"); | 
|  | SELF_CHECK (strcmp (env.get ("A"), "1") == 0); | 
|  | SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1"))); | 
|  | SELF_CHECK (env.user_set_env ().size () == 1); | 
|  |  | 
|  | /* Some compilers warn about moving to self, but that's precisely what we want | 
|  | to test here, so turn this warning off.  */ | 
|  | DIAGNOSTIC_PUSH | 
|  | DIAGNOSTIC_IGNORE_SELF_MOVE | 
|  | env = std::move (env); | 
|  | DIAGNOSTIC_POP | 
|  |  | 
|  | SELF_CHECK (strcmp (env.get ("A"), "1") == 0); | 
|  | SELF_CHECK (strcmp (env.envp ()[0], "A=1") == 0); | 
|  | SELF_CHECK (env.envp ()[1] == NULL); | 
|  | SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1"))); | 
|  | SELF_CHECK (env.user_set_env ().size () == 1); | 
|  | } | 
|  |  | 
|  | /* Test if set/unset/reset works.  */ | 
|  |  | 
|  | static void | 
|  | test_set_unset_reset () | 
|  | { | 
|  | gdb_environ env = gdb_environ::from_host_environ (); | 
|  |  | 
|  | /* Our test variable should already be present in the host's environment.  */ | 
|  | SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") != NULL); | 
|  |  | 
|  | /* Set our test variable to another value.  */ | 
|  | env.set ("GDB_SELFTEST_ENVIRON", "test"); | 
|  | SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "test") == 0); | 
|  | SELF_CHECK (env.user_set_env ().size () == 1); | 
|  | SELF_CHECK (env.user_unset_env ().size () == 0); | 
|  |  | 
|  | /* And unset our test variable.  The variable still exists in the | 
|  | host's environment, but doesn't exist in our vector.  */ | 
|  | env.unset ("GDB_SELFTEST_ENVIRON"); | 
|  | SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL); | 
|  | SELF_CHECK (env.user_set_env ().size () == 0); | 
|  | SELF_CHECK (env.user_unset_env ().size () == 1); | 
|  | SELF_CHECK (set_contains (env.user_unset_env (), | 
|  | std::string ("GDB_SELFTEST_ENVIRON"))); | 
|  |  | 
|  | /* Re-set the test variable.  */ | 
|  | env.set ("GDB_SELFTEST_ENVIRON", "1"); | 
|  | SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "1") == 0); | 
|  | } | 
|  |  | 
|  | static void | 
|  | run_tests () | 
|  | { | 
|  | /* Set a test environment variable.  */ | 
|  | if (setenv ("GDB_SELFTEST_ENVIRON", "1", 1) != 0) | 
|  | error (_("Could not set environment variable for testing.")); | 
|  |  | 
|  | test_vector_initialization (); | 
|  |  | 
|  | test_unset_set_empty_vector (); | 
|  |  | 
|  | test_init_from_host_environ (); | 
|  |  | 
|  | test_set_unset_reset (); | 
|  |  | 
|  | test_vector_clear (); | 
|  |  | 
|  | test_reinit_from_host_environ (); | 
|  |  | 
|  | /* Get rid of our test variable.  We won't need it anymore.  */ | 
|  | unsetenv ("GDB_SELFTEST_ENVIRON"); | 
|  |  | 
|  | test_set_A_unset_B_unset_A_cannot_find_A_can_find_B (); | 
|  |  | 
|  | test_std_move (); | 
|  |  | 
|  | test_move_constructor (); | 
|  |  | 
|  | test_self_move (); | 
|  | } | 
|  | } /* namespace gdb_environ */ | 
|  | } /* namespace selftests */ | 
|  |  | 
|  | void _initialize_environ_selftests (); | 
|  | void | 
|  | _initialize_environ_selftests () | 
|  | { | 
|  | selftests::register_test ("gdb_environ", | 
|  | selftests::gdb_environ_tests::run_tests); | 
|  | } |