| /* Self tests for gdb_environ for GDB, the GNU debugger. | 
 |  | 
 |    Copyright (C) 2017-2025 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 "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 */ | 
 |  | 
 | INIT_GDB_FILE (environ_selftests) | 
 | { | 
 |   selftests::register_test ("gdb_environ", | 
 | 			    selftests::gdb_environ_tests::run_tests); | 
 | } |