xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/unittests/command-def-selftests.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /* Self tests for GDB command definitions for GDB, the GNU debugger.
2 
3    Copyright (C) 2019-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 "cli/cli-cmds.h"
22 #include "cli/cli-decode.h"
23 #include "gdbsupport/selftest.h"
24 
25 #include <map>
26 
27 namespace selftests {
28 
29 /* Verify some invariants of GDB commands documentation.  */
30 
31 namespace help_doc_tests {
32 
33 static unsigned int nr_failed_invariants;
34 
35 /* Report a broken invariant and increments nr_failed_invariants.  */
36 
37 static void
38 broken_doc_invariant (const char *prefix, const char *name, const char *msg)
39 {
40   fprintf_filtered (gdb_stdout,
41 		    "help doc broken invariant: command '%s%s' help doc %s\n",
42 		    prefix, name, msg);
43   nr_failed_invariants++;
44 }
45 
46 /* Recursively walk the commandlist structures, and check doc invariants:
47    - The first line of the doc must end with a '.'.
48    - the doc must not end with a new line.
49   If an invariant is not respected, produce a message and increment
50   nr_failed_invariants.
51   Note that we do not call SELF_CHECK in this function, as we want
52   all commands to be checked before making the test fail.  */
53 
54 static void
55 check_doc (struct cmd_list_element *commandlist, const char *prefix)
56 {
57   struct cmd_list_element *c;
58 
59   /* Walk through the commands.  */
60   for (c = commandlist; c; c = c->next)
61     {
62       /* Checks the doc has a first line terminated with a '.'.  */
63       const char *p = c->doc;
64 
65       /* Position p on the first LF, or on terminating null byte.  */
66       while (*p && *p != '\n')
67 	p++;
68       if (p == c->doc)
69 	broken_doc_invariant
70 	  (prefix, c->name,
71 	   "is missing the first line terminated with a '.' character");
72       else if (*(p-1) != '.')
73 	broken_doc_invariant
74 	  (prefix, c->name,
75 	   "first line is not terminated with a '.' character");
76 
77       /* Checks the doc is not terminated with a new line.  */
78       if (c->doc[strlen (c->doc) - 1] == '\n')
79 	broken_doc_invariant
80 	  (prefix, c->name,
81 	   "has a superfluous trailing end of line");
82 
83       /* Check if this command has subcommands and is not an
84 	 abbreviation.  We skip checking subcommands of abbreviations
85 	 in order to avoid duplicates in the output.  */
86       if (c->prefixlist != NULL && !c->abbrev_flag)
87 	{
88 	  /* Recursively call ourselves on the subcommand list,
89 	     passing the right prefix in.  */
90 	  check_doc (*c->prefixlist, c->prefixname);
91 	}
92     }
93 }
94 
95 static void
96 help_doc_invariants_tests ()
97 {
98   nr_failed_invariants = 0;
99   check_doc (cmdlist, "");
100   SELF_CHECK (nr_failed_invariants == 0);
101 }
102 
103 } /* namespace help_doc_tests */
104 
105 /* Verify some invariants of GDB command structure.  */
106 
107 namespace command_structure_tests {
108 
109 /* Nr of commands in which a duplicated list is found.  */
110 unsigned int nr_duplicates = 0;
111 /* Nr of commands in a list having no valid prefix cmd.  */
112 unsigned int nr_invalid_prefixcmd = 0;
113 
114 /* A map associating a list with the prefix leading to it.  */
115 
116 std::map<cmd_list_element **, const char *> lists;
117 
118 /* Store each command list in lists, associated with the prefix to reach it.  A
119    list must only be found once.
120 
121    Verifies that all elements of the list have the same non-null prefix
122    command.  */
123 
124 static void
125 traverse_command_structure (struct cmd_list_element **list,
126 			    const char *prefix)
127 {
128   struct cmd_list_element *c, *prefixcmd;
129 
130   auto dupl = lists.find (list);
131   if (dupl != lists.end ())
132     {
133       fprintf_filtered (gdb_stdout,
134 			"list %p duplicated,"
135 			" reachable via prefix '%s' and '%s'."
136 			"  Duplicated list first command is '%s'\n",
137 			list,
138 			prefix, dupl->second,
139 			(*list)->name);
140       nr_duplicates++;
141       return;
142     }
143 
144   lists.insert ({list, prefix});
145 
146   /* All commands of *list must have a prefix command equal to PREFIXCMD,
147      the prefix command of the first command.  */
148   if (*list == nullptr)
149     prefixcmd = nullptr; /* A prefix command with an empty subcommand list.  */
150   else
151     prefixcmd = (*list)->prefix;
152 
153   /* Walk through the commands.  */
154   for (c = *list; c; c = c->next)
155     {
156       /* If this command has subcommands and is not an alias,
157 	 traverse the subcommands.  */
158       if (c->prefixlist != NULL && c->cmd_pointer == nullptr)
159 	{
160 	  /* Recursively call ourselves on the subcommand list,
161 	     passing the right prefix in.  */
162 	  traverse_command_structure (c->prefixlist, c->prefixname);
163 	}
164       if (prefixcmd != c->prefix
165 	  || (prefixcmd == nullptr && *list != cmdlist))
166 	{
167 	  if (c->prefix == nullptr)
168 	    fprintf_filtered (gdb_stdout,
169 			      "list %p reachable via prefix '%s'."
170 			      "  command '%s' has null prefixcmd\n",
171 			      list,
172 			      prefix, c->name);
173 	  else
174 	    fprintf_filtered (gdb_stdout,
175 			      "list %p reachable via prefix '%s'."
176 			      "  command '%s' has a different prefixcmd\n",
177 			      list,
178 			      prefix, c->name);
179 	  nr_invalid_prefixcmd++;
180 	}
181     }
182 }
183 
184 /* Verify that a list of commands is present in the tree only once.  */
185 
186 static void
187 command_structure_invariants_tests ()
188 {
189   nr_duplicates = 0;
190   nr_invalid_prefixcmd = 0;
191 
192   traverse_command_structure (&cmdlist, "");
193 
194   /* Release memory, be ready to be re-run.  */
195   lists.clear ();
196 
197   SELF_CHECK (nr_duplicates == 0);
198   SELF_CHECK (nr_invalid_prefixcmd == 0);
199 }
200 
201 }
202 
203 } /* namespace selftests */
204 
205 void _initialize_command_def_selftests ();
206 void
207 _initialize_command_def_selftests ()
208 {
209   selftests::register_test
210     ("help_doc_invariants",
211      selftests::help_doc_tests::help_doc_invariants_tests);
212 
213   selftests::register_test
214     ("command_structure_invariants",
215      selftests::command_structure_tests::command_structure_invariants_tests);
216 }
217