]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: add remote argument passing unit tests
authorAndrew Burgess <aburgess@redhat.com>
Thu, 9 Nov 2023 15:28:49 +0000 (15:28 +0000)
committerAndrew Burgess <aburgess@redhat.com>
Thu, 24 Apr 2025 15:45:51 +0000 (16:45 +0100)
This commit adds some remote argument passing unit tests.  There are
not many tests right now -- there are known bugs in the remote
argument passing mechanism (see PR gdb/28392) -- but some simple cases
are covered here, and I plan to add additional tests once I've fixed
more of the problems with the existing argument handling code.

The tests take an inferior argument string, this is the string that
GDB would carry around as inferior::m_args.  This string is then split
using gdb::remote_args::split, this gives a vector of strings, these
are the strings that are passed over the remote protocol.  These split
strings are validated as part of the test.

The split strings are then combined using gdb::remote_args::join which
gives the inferior argument string that gdbserver will use, this is
held in server.cc as program_args, this joined string is then checked
as part of the test.

There are no changes to GDB's behaviour as part of this commit, other
than adding the new tests which can be run with:

  (gdb) maintenance selftest remote-args
  Running selftest remote-args.
  Ran 1 unit tests, 0 failed

Tested-By: Guinevere Larsen <guinevere@redhat.com>
gdb/Makefile.in
gdb/unittests/remote-arg-selftests.c [new file with mode: 0644]

index 68b5e56c8da863521d3d0de16531248c92d121d8..a43f1c925ea248702521dac3c537711021e1e281 100644 (file)
@@ -479,6 +479,7 @@ SELFTESTS_SRCS = \
        unittests/ptid-selftests.c \
        unittests/main-thread-selftests.c \
        unittests/mkdir-recursive-selftests.c \
+       unittests/remote-arg-selftests.c \
        unittests/rsp-low-selftests.c \
        unittests/scoped_fd-selftests.c \
        unittests/scoped_ignore_signal-selftests.c \
diff --git a/gdb/unittests/remote-arg-selftests.c b/gdb/unittests/remote-arg-selftests.c
new file mode 100644 (file)
index 0000000..70f8a39
--- /dev/null
@@ -0,0 +1,166 @@
+/* Self tests for GDB's argument splitting and merging.
+
+   Copyright (C) 2023-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 "defs.h"
+#include "gdbsupport/selftest.h"
+#include "gdbsupport/buildargv.h"
+#include "gdbsupport/common-inferior.h"
+#include "gdbsupport/remote-args.h"
+#include "gdbsupport/gdb_argv_vec.h"
+
+namespace selftests {
+namespace remote_args_tests {
+
+/* The data needed to perform a single remote argument test.  */
+struct arg_test_desc
+{
+  /* The original inferior argument string.  */
+  std::string input;
+
+  /* The individual arguments once they have been split.  */
+  std::vector<std::string> split;
+
+  /* The new inferior argument string, created by joining SPLIT.  */
+  std::string joined;
+};
+
+/* The list of tests.  */
+arg_test_desc desc[] = {
+  { "abc", { "abc" }, "abc" },
+  { "a b c", { "a", "b", "c" }, "a b c" },
+  { "\"a b\" 'c d'", { "a b", "c d" }, "a\\ b c\\ d" },
+  { "\\' \\\"", { "'", "\"" }, "\\' \\\"" },
+  { "'\\'", { "\\" }, "\\\\" },
+  { "\"\\\\\" \"\\\\\\\"\"", { "\\", "\\\"" }, "\\\\ \\\\\\\"" },
+  { "\\  \" \" ' '", { " ", " ", " "}, "\\  \\  \\ " },
+  { "\"'\"", { "'" }, "\\'" },
+  { "'\"' '\\\"'", { "\"", "\\\"" } , "\\\" \\\\\\\""},
+  { "\"first arg\" \"\" \"third-arg\" \"'\" \"\\\"\" \"\\\\\\\"\" \" \" \"\"",
+    { "first arg", "", "third-arg", "'", "\"", "\\\""," ", "" },
+    "first\\ arg '' third-arg \\' \\\" \\\\\\\" \\  ''"},
+  { "\"\\a\" \"\\&\" \"\\#\" \"\\<\" \"\\^\"",
+    { "\\a", "\\&", "\\#" , "\\<" , "\\^"},
+    "\\\\a \\\\\\& \\\\\\# \\\\\\< \\\\\\^" },
+  { "1 '\n' 3", { "1", "\n", "3" }, "1 '\n' 3" },
+};
+
+/* Run the remote argument passing self tests.  */
+
+static void
+self_test ()
+{
+  int failure_count = 0;
+  for (const auto &d : desc)
+    {
+      if (run_verbose ())
+       {
+         if (&d != &desc[0])
+           debug_printf ("------------------------------\n");
+         debug_printf ("Input (%s)\n", d.input.c_str ());
+       }
+
+      /* Split argument string into individual arguments.  */
+      std::vector<std::string> split_args = gdb::remote_args::split (d.input);
+
+      if (run_verbose ())
+       {
+         debug_printf ("Split:\n");
+
+         size_t len = std::max (split_args.size (), d.split.size ());
+         for (size_t i = 0; i < len; ++i)
+           {
+             const char *got = "N/A";
+             const char *expected = got;
+
+             if (i < split_args.size ())
+               got = split_args[i].c_str ();
+
+             if (i < d.split.size ())
+               expected = d.split[i].c_str ();
+
+             debug_printf ("  got (%s), expected (%s)\n", got, expected);
+           }
+       }
+
+      if (split_args != d.split)
+       {
+         ++failure_count;
+         if (run_verbose ())
+           debug_printf ("FAIL\n");
+         continue;
+       }
+
+      /* Now join the arguments.  */
+      gdb::argv_vec split_args_c_str;
+      for (const std::string &s : split_args)
+       split_args_c_str.push_back (xstrdup (s.c_str ()));
+      std::string joined_args
+       = gdb::remote_args::join (split_args_c_str.get ());
+
+      if (run_verbose ())
+       debug_printf ("Joined (%s), expected (%s)\n",
+                     joined_args.c_str (), d.joined.c_str ());
+
+      if (joined_args != d.joined)
+       {
+         ++failure_count;
+         if (run_verbose ())
+           debug_printf ("FAIL\n");
+         continue;
+       }
+
+      /* The contents of JOINED_ARGS will not be identical to D.INPUT.
+        There are multiple ways that an argument can be escaped, and our
+        join function just picks one.  However, if we split JOINED_ARGS
+        again then each individual argument should be the same as those in
+        SPLIT_ARGS.  So test that next.  */
+      std::vector<std::string> split_args_v2
+       = gdb::remote_args::split (joined_args);
+
+      if (split_args_v2 != split_args)
+       {
+         ++failure_count;
+         if (run_verbose ())
+           {
+             debug_printf ("Re-split:\n");
+             for (const auto &a : split_args_v2)
+               debug_printf ("  got (%s)\n", a.c_str ());
+             debug_printf ("FAIL\n");
+           }
+         continue;
+       }
+
+      if (run_verbose ())
+       debug_printf ("PASS\n");
+    }
+
+  SELF_CHECK (failure_count == 0);
+}
+
+} /* namespace remote_args_tests */
+} /* namespace selftests */
+
+void _initialize_remote_arg_selftests ();
+
+void
+_initialize_remote_arg_selftests ()
+{
+  selftests::register_test ("remote-args",
+                           selftests::remote_args_tests::self_test);
+}