xref: /llvm-project/lldb/test/API/commands/settings/TestSettings.py (revision efc6d33be9f4b4d0f0e8d3d5f198f2616b75792b)
1"""
2Test lldb settings command.
3"""
4
5import json
6import os
7import re
8
9import lldb
10from lldbsuite.test import lldbutil
11from lldbsuite.test.decorators import *
12from lldbsuite.test.lldbtest import *
13
14
15class SettingsCommandTestCase(TestBase):
16    NO_DEBUG_INFO_TESTCASE = True
17
18    def test_apropos_should_also_search_settings_description(self):
19        """Test that 'apropos' command should also search descriptions for the settings variables."""
20
21        self.expect(
22            "apropos 'environment variable'",
23            substrs=[
24                "target.env-vars",
25                "environment variables",
26                "executable's environment",
27            ],
28        )
29
30    def test_set_interpreter_repeat_prev_command(self):
31        """Test the `interpreter.repeat-previous-command` setting."""
32        self.build()
33
34        exe = self.getBuildArtifact("a.out")
35        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
36        setting = "interpreter.repeat-previous-command"
37
38        def cleanup(setting):
39            self.runCmd("settings clear %s" % setting, check=False)
40
41        # Execute the cleanup function during test case tear down.
42        self.addTearDownHook(cleanup(setting))
43
44        # First, check for the setting default value.
45        self.expect(
46            "setting show %s" % setting,
47            substrs=["interpreter.repeat-previous-command (boolean) = true"],
48        )
49
50        # Then, invert the setting, and check that was set correctly
51        self.runCmd("setting set %s false" % setting)
52        self.expect(
53            "setting show %s" % setting,
54            substrs=["interpreter.repeat-previous-command (boolean) = false"],
55        )
56
57        ci = self.dbg.GetCommandInterpreter()
58        self.assertTrue(ci.IsValid(), "Invalid command interpreter.")
59        # Now, test the functionnality
60        res = lldb.SBCommandReturnObject()
61        ci.HandleCommand("breakpoint set -n main", res)
62        self.assertTrue(res.Succeeded(), "Command failed.")
63        ci.HandleCommand("", res)
64        self.assertTrue(res.Succeeded(), "Empty command failed.")
65        self.assertEqual(self.dbg.GetSelectedTarget().GetNumBreakpoints(), 1)
66
67    def test_append_target_env_vars(self):
68        """Test that 'append target.env-vars' works."""
69        # Append the env-vars.
70        self.runCmd("settings append target.env-vars MY_ENV_VAR=YES")
71        # And add hooks to restore the settings during tearDown().
72        self.addTearDownHook(lambda: self.runCmd("settings clear target.env-vars"))
73
74        # Check it immediately!
75        self.expect("settings show target.env-vars", substrs=["MY_ENV_VAR=YES"])
76
77    def test_insert_before_and_after_target_run_args(self):
78        """Test that 'insert-before/after target.run-args' works."""
79        # Set the run-args first.
80        self.runCmd("settings set target.run-args a b c")
81        # And add hooks to restore the settings during tearDown().
82        self.addTearDownHook(lambda: self.runCmd("settings clear target.run-args"))
83
84        # Now insert-before the index-0 element with '__a__'.
85        self.runCmd("settings insert-before target.run-args 0 __a__")
86        # And insert-after the index-1 element with '__A__'.
87        self.runCmd("settings insert-after target.run-args 1 __A__")
88        # Check it immediately!
89        self.expect(
90            "settings show target.run-args",
91            substrs=[
92                "target.run-args",
93                '[0]: "__a__"',
94                '[1]: "a"',
95                '[2]: "__A__"',
96                '[3]: "b"',
97                '[4]: "c"',
98            ],
99        )
100
101    def test_replace_target_run_args(self):
102        """Test that 'replace target.run-args' works."""
103        # Set the run-args and then replace the index-0 element.
104        self.runCmd("settings set target.run-args a b c")
105        # And add hooks to restore the settings during tearDown().
106        self.addTearDownHook(lambda: self.runCmd("settings clear target.run-args"))
107
108        # Now replace the index-0 element with 'A', instead.
109        self.runCmd("settings replace target.run-args 0 A")
110        # Check it immediately!
111        self.expect(
112            "settings show target.run-args",
113            substrs=[
114                "target.run-args (arguments) =",
115                '[0]: "A"',
116                '[1]: "b"',
117                '[2]: "c"',
118            ],
119        )
120
121    def test_set_prompt(self):
122        """Test that 'set prompt' actually changes the prompt."""
123
124        # Set prompt to 'lldb2'.
125        self.runCmd("settings set prompt 'lldb2 '")
126
127        # Immediately test the setting.
128        self.expect(
129            "settings show prompt",
130            SETTING_MSG("prompt"),
131            startstr='prompt (string) = "lldb2 "',
132        )
133
134        # The overall display should also reflect the new setting.
135        self.expect(
136            "settings show",
137            SETTING_MSG("prompt"),
138            substrs=['prompt (string) = "lldb2 "'],
139        )
140
141        # Use '-r' option to reset to the original default prompt.
142        self.runCmd("settings clear prompt")
143
144    def test_set_term_width(self):
145        """Test that 'set term-width' actually changes the term-width."""
146
147        self.runCmd("settings set term-width 70")
148
149        # Immediately test the setting.
150        self.expect(
151            "settings show term-width",
152            SETTING_MSG("term-width"),
153            startstr="term-width (unsigned) = 70",
154        )
155
156        # The overall display should also reflect the new setting.
157        self.expect(
158            "settings show",
159            SETTING_MSG("term-width"),
160            substrs=["term-width (unsigned) = 70"],
161        )
162
163        self.dbg.SetTerminalWidth(60)
164
165        self.expect(
166            "settings show",
167            SETTING_MSG("term-width"),
168            substrs=["term-width (unsigned) = 60"],
169        )
170
171    # rdar://problem/10712130
172    @skipIf(oslist=["windows"], bugnumber="llvm.org/pr44431")
173    def test_set_frame_format(self):
174        """Test that 'set frame-format' with a backtick char in the format string works as well as fullpath."""
175        self.build()
176
177        exe = self.getBuildArtifact("a.out")
178        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
179
180        def cleanup():
181            self.runCmd(
182                "settings set frame-format %s" % self.format_string, check=False
183            )
184
185        # Execute the cleanup function during test case tear down.
186        self.addTearDownHook(cleanup)
187
188        self.runCmd("settings show frame-format")
189        m = re.match('^frame-format \(format-string\) = "(.*)"$', self.res.GetOutput())
190        self.assertTrue(m, "Bad settings string")
191        self.format_string = m.group(1)
192
193        # Change the default format to print function.name rather than
194        # function.name-with-args
195        format_string = "frame #${frame.index}: ${frame.pc}{ ${module.file.basename}\`${function.name}{${function.pc-offset}}}{ at ${line.file.fullpath}:${line.number}}{, lang=${language}}\n"
196        self.runCmd("settings set frame-format %s" % format_string)
197
198        # Immediately test the setting.
199        self.expect(
200            "settings show frame-format",
201            SETTING_MSG("frame-format"),
202            substrs=[format_string],
203        )
204
205        self.runCmd("breakpoint set -n main")
206        self.runCmd(
207            "process launch --working-dir '{0}'".format(
208                self.get_process_working_directory()
209            ),
210            RUN_SUCCEEDED,
211        )
212        self.expect("thread backtrace", substrs=["`main", self.getSourceDir()])
213
214    def test_set_auto_confirm(self):
215        """Test that after 'set auto-confirm true', manual confirmation should not kick in."""
216        self.build()
217
218        exe = self.getBuildArtifact("a.out")
219        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
220
221        self.runCmd("settings set auto-confirm true")
222
223        # Immediately test the setting.
224        self.expect(
225            "settings show auto-confirm",
226            SETTING_MSG("auto-confirm"),
227            startstr="auto-confirm (boolean) = true",
228        )
229
230        # Now 'breakpoint delete' should just work fine without confirmation
231        # prompt from the command interpreter.
232        self.runCmd("breakpoint set -n main")
233        self.expect("breakpoint delete", startstr="All breakpoints removed")
234
235        # Restore the original setting of auto-confirm.
236        self.runCmd("settings clear auto-confirm")
237        self.expect(
238            "settings show auto-confirm",
239            SETTING_MSG("auto-confirm"),
240            startstr="auto-confirm (boolean) = false",
241        )
242
243    @skipIf(archs=no_match(["x86_64", "i386", "i686"]))
244    def test_disassembler_settings(self):
245        """Test that user options for the disassembler take effect."""
246        self.build()
247
248        exe = self.getBuildArtifact("a.out")
249        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
250
251        # AT&T syntax
252        self.runCmd("settings set target.x86-disassembly-flavor att")
253        self.runCmd("settings set target.use-hex-immediates false")
254        self.expect("disassemble -n numberfn", substrs=["$90"])
255        self.runCmd("settings set target.use-hex-immediates true")
256        self.runCmd("settings set target.hex-immediate-style c")
257        self.expect("disassemble -n numberfn", substrs=["$0x5a"])
258        self.runCmd("settings set target.hex-immediate-style asm")
259        self.expect("disassemble -n numberfn", substrs=["$5ah"])
260
261        # Intel syntax
262        self.runCmd("settings set target.x86-disassembly-flavor intel")
263        self.runCmd("settings set target.use-hex-immediates false")
264        self.expect("disassemble -n numberfn", substrs=["90"])
265        self.runCmd("settings set target.use-hex-immediates true")
266        self.runCmd("settings set target.hex-immediate-style c")
267        self.expect("disassemble -n numberfn", substrs=["0x5a"])
268        self.runCmd("settings set target.hex-immediate-style asm")
269        self.expect("disassemble -n numberfn", substrs=["5ah"])
270
271    @skipIfDarwinEmbedded  # <rdar://problem/34446098> debugserver on ios etc can't write files
272    def test_run_args_and_env_vars(self):
273        self.do_test_run_args_and_env_vars(use_launchsimple=False)
274
275    @skipIfDarwinEmbedded  # <rdar://problem/34446098> debugserver on ios etc can't write files
276    def test_launchsimple_args_and_env_vars(self):
277        self.do_test_run_args_and_env_vars(use_launchsimple=True)
278
279    def do_test_run_args_and_env_vars(self, use_launchsimple):
280        """Test that run-args and env-vars are passed to the launched process."""
281        self.build()
282
283        # Set the run-args and the env-vars.
284        # And add hooks to restore the settings during tearDown().
285        self.runCmd("settings set target.run-args A B C")
286        self.addTearDownHook(lambda: self.runCmd("settings clear target.run-args"))
287        self.runCmd('settings set target.env-vars ["MY_ENV_VAR"]=YES')
288        self.addTearDownHook(lambda: self.runCmd("settings clear target.env-vars"))
289
290        exe = self.getBuildArtifact("a.out")
291        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
292
293        target = self.dbg.GetTargetAtIndex(0)
294        launch_info = target.GetLaunchInfo()
295        found_env_var = False
296        for i in range(0, launch_info.GetNumEnvironmentEntries()):
297            if launch_info.GetEnvironmentEntryAtIndex(i) == "MY_ENV_VAR=YES":
298                found_env_var = True
299                break
300        self.assertTrue(found_env_var, "MY_ENV_VAR was not set in LaunchInfo object")
301
302        self.assertEqual(launch_info.GetNumArguments(), 3)
303        self.assertEqual(launch_info.GetArgumentAtIndex(0), "A")
304        self.assertEqual(launch_info.GetArgumentAtIndex(1), "B")
305        self.assertEqual(launch_info.GetArgumentAtIndex(2), "C")
306
307        self.expect("target show-launch-environment", substrs=["MY_ENV_VAR=YES"])
308
309        wd = self.get_process_working_directory()
310        if use_launchsimple:
311            process = target.LaunchSimple(None, None, wd)
312            self.assertTrue(process)
313        else:
314            self.runCmd("process launch --working-dir '{0}'".format(wd), RUN_SUCCEEDED)
315
316        # Read the output file produced by running the program.
317        output = lldbutil.read_file_from_process_wd(self, "output2.txt")
318
319        self.expect(
320            output,
321            exe=False,
322            substrs=[
323                "argv[1] matches",
324                "argv[2] matches",
325                "argv[3] matches",
326                "Environment variable 'MY_ENV_VAR' successfully passed.",
327            ],
328        )
329
330        # Check that env-vars overrides unset-env-vars.
331        self.runCmd("settings set target.unset-env-vars MY_ENV_VAR")
332
333        self.expect(
334            "target show-launch-environment",
335            "env-vars overrides unset-env-vars",
336            substrs=["MY_ENV_VAR=YES"],
337        )
338
339        wd = self.get_process_working_directory()
340        if use_launchsimple:
341            process = target.LaunchSimple(None, None, wd)
342            self.assertTrue(process)
343        else:
344            self.runCmd("process launch --working-dir '{0}'".format(wd), RUN_SUCCEEDED)
345
346        # Read the output file produced by running the program.
347        output = lldbutil.read_file_from_process_wd(self, "output2.txt")
348
349        self.expect(
350            output,
351            exe=False,
352            substrs=["Environment variable 'MY_ENV_VAR' successfully passed."],
353        )
354
355    @skipIfRemote  # it doesn't make sense to send host env to remote target
356    def test_pass_host_env_vars(self):
357        """Test that the host env vars are passed to the launched process."""
358        self.build()
359
360        # Set some host environment variables now.
361        os.environ["MY_HOST_ENV_VAR1"] = "VAR1"
362        os.environ["MY_HOST_ENV_VAR2"] = "VAR2"
363
364        # This is the function to unset the two env variables set above.
365        def unset_env_variables():
366            os.environ.pop("MY_HOST_ENV_VAR1")
367            os.environ.pop("MY_HOST_ENV_VAR2")
368
369        self.addTearDownHook(unset_env_variables)
370
371        exe = self.getBuildArtifact("a.out")
372        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
373
374        # By default, inherit-env is 'true'.
375        self.expect(
376            "settings show target.inherit-env",
377            "Default inherit-env is 'true'",
378            startstr="target.inherit-env (boolean) = true",
379        )
380
381        self.expect(
382            "target show-launch-environment",
383            "Host environment is passed correctly",
384            substrs=["MY_HOST_ENV_VAR1=VAR1", "MY_HOST_ENV_VAR2=VAR2"],
385        )
386        self.runCmd(
387            "process launch --working-dir '{0}'".format(
388                self.get_process_working_directory()
389            ),
390            RUN_SUCCEEDED,
391        )
392
393        # Read the output file produced by running the program.
394        output = lldbutil.read_file_from_process_wd(self, "output1.txt")
395
396        self.expect(
397            output,
398            exe=False,
399            substrs=[
400                "The host environment variable 'MY_HOST_ENV_VAR1' successfully passed.",
401                "The host environment variable 'MY_HOST_ENV_VAR2' successfully passed.",
402            ],
403        )
404
405        # Now test that we can prevent the inferior from inheriting the
406        # environment.
407        self.runCmd("settings set target.inherit-env false")
408
409        self.expect(
410            "target show-launch-environment",
411            "target.inherit-env affects `target show-launch-environment`",
412            matching=False,
413            substrs=["MY_HOST_ENV_VAR1=VAR1", "MY_HOST_ENV_VAR2=VAR2"],
414        )
415
416        self.runCmd(
417            "process launch --working-dir '{0}'".format(
418                self.get_process_working_directory()
419            ),
420            RUN_SUCCEEDED,
421        )
422
423        # Read the output file produced by running the program.
424        output = lldbutil.read_file_from_process_wd(self, "output1.txt")
425
426        self.expect(
427            output,
428            exe=False,
429            matching=False,
430            substrs=[
431                "The host environment variable 'MY_HOST_ENV_VAR1' successfully passed.",
432                "The host environment variable 'MY_HOST_ENV_VAR2' successfully passed.",
433            ],
434        )
435
436        # Now test that we can unset variables from the inherited environment.
437        self.runCmd("settings set target.inherit-env true")
438        self.runCmd("settings set target.unset-env-vars MY_HOST_ENV_VAR1")
439        self.runCmd(
440            "process launch --working-dir '{0}'".format(
441                self.get_process_working_directory()
442            ),
443            RUN_SUCCEEDED,
444        )
445
446        # Read the output file produced by running the program.
447        output = lldbutil.read_file_from_process_wd(self, "output1.txt")
448
449        self.expect(
450            "target show-launch-environment",
451            "MY_HOST_ENV_VAR1 is unset, it shouldn't be in `target show-launch-environment`",
452            matching=False,
453            substrs=["MY_HOST_ENV_VAR1=VAR1"],
454        )
455        self.expect(
456            "target show-launch-environment",
457            "MY_HOST_ENV_VAR2 shouldn be in `target show-launch-environment`",
458            substrs=["MY_HOST_ENV_VAR2=VAR2"],
459        )
460
461        self.expect(
462            output,
463            exe=False,
464            matching=False,
465            substrs=[
466                "The host environment variable 'MY_HOST_ENV_VAR1' successfully passed."
467            ],
468        )
469        self.expect(
470            output,
471            exe=False,
472            substrs=[
473                "The host environment variable 'MY_HOST_ENV_VAR2' successfully passed."
474            ],
475        )
476
477    @skipIfDarwinEmbedded  # <rdar://problem/34446098> debugserver on ios etc can't write files
478    def test_set_error_output_path(self):
479        """Test that setting target.error/output-path for the launched process works."""
480        self.build()
481
482        exe = self.getBuildArtifact("a.out")
483        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
484
485        # Set the error-path and output-path and verify both are set.
486        self.runCmd(
487            "settings set target.error-path '{0}'".format(
488                lldbutil.append_to_process_working_directory(self, "stderr.txt")
489            )
490        )
491        self.runCmd(
492            "settings set target.output-path '{0}".format(
493                lldbutil.append_to_process_working_directory(self, "stdout.txt")
494            )
495        )
496        # And add hooks to restore the original settings during tearDown().
497        self.addTearDownHook(lambda: self.runCmd("settings clear target.output-path"))
498        self.addTearDownHook(lambda: self.runCmd("settings clear target.error-path"))
499
500        self.expect(
501            "settings show target.error-path",
502            SETTING_MSG("target.error-path"),
503            substrs=["target.error-path (file)", 'stderr.txt"'],
504        )
505
506        self.expect(
507            "settings show target.output-path",
508            SETTING_MSG("target.output-path"),
509            substrs=["target.output-path (file)", 'stdout.txt"'],
510        )
511
512        self.runCmd(
513            "process launch --working-dir '{0}'".format(
514                self.get_process_working_directory()
515            ),
516            RUN_SUCCEEDED,
517        )
518
519        output = lldbutil.read_file_from_process_wd(self, "stderr.txt")
520        message = "This message should go to standard error."
521        if lldbplatformutil.hasChattyStderr(self):
522            self.expect(output, exe=False, substrs=[message])
523        else:
524            self.expect(output, exe=False, startstr=message)
525
526        output = lldbutil.read_file_from_process_wd(self, "stdout.txt")
527        self.expect(
528            output, exe=False, startstr="This message should go to standard out."
529        )
530
531    @skipIfDarwinEmbedded  # <rdar://problem/34446098> debugserver on ios etc can't write files
532    def test_same_error_output_path(self):
533        """Test that setting target.error and output-path to the same file path for the launched process works."""
534        self.build()
535
536        exe = self.getBuildArtifact("a.out")
537        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
538
539        # Set the error-path and output-path and verify both are set.
540        self.runCmd(
541            "settings set target.error-path '{0}'".format(
542                lldbutil.append_to_process_working_directory(self, "output.txt")
543            )
544        )
545        self.runCmd(
546            "settings set target.output-path '{0}".format(
547                lldbutil.append_to_process_working_directory(self, "output.txt")
548            )
549        )
550        # And add hooks to restore the original settings during tearDown().
551        self.addTearDownHook(lambda: self.runCmd("settings clear target.output-path"))
552        self.addTearDownHook(lambda: self.runCmd("settings clear target.error-path"))
553
554        self.expect(
555            "settings show target.error-path",
556            SETTING_MSG("target.error-path"),
557            substrs=["target.error-path (file)", 'output.txt"'],
558        )
559
560        self.expect(
561            "settings show target.output-path",
562            SETTING_MSG("target.output-path"),
563            substrs=["target.output-path (file)", 'output.txt"'],
564        )
565
566        self.runCmd(
567            "process launch --working-dir '{0}'".format(
568                self.get_process_working_directory()
569            ),
570            RUN_SUCCEEDED,
571        )
572
573        output = lldbutil.read_file_from_process_wd(self, "output.txt")
574        err_message = "This message should go to standard error."
575        out_message = "This message should go to standard out."
576        # Error msg should get flushed by the output msg
577        self.expect(output, exe=False, substrs=[out_message])
578        self.assertNotIn(
579            err_message,
580            output,
581            "Race condition when both stderr/stdout redirects to the same file",
582        )
583
584    def test_print_dictionary_setting(self):
585        self.runCmd("settings clear target.env-vars")
586        self.runCmd('settings set target.env-vars ["MY_VAR"]=some-value')
587        self.expect("settings show target.env-vars", substrs=["MY_VAR=some-value"])
588        self.runCmd("settings clear target.env-vars")
589
590    def test_print_array_setting(self):
591        self.runCmd("settings clear target.run-args")
592        self.runCmd("settings set target.run-args gobbledy-gook")
593        self.expect("settings show target.run-args", substrs=['[0]: "gobbledy-gook"'])
594        self.runCmd("settings clear target.run-args")
595
596    def test_settings_with_quotes(self):
597        self.runCmd("settings clear target.run-args")
598        self.runCmd("settings set target.run-args a b c")
599        self.expect(
600            "settings show target.run-args",
601            substrs=['[0]: "a"', '[1]: "b"', '[2]: "c"'],
602        )
603        self.runCmd("settings set target.run-args 'a b c'")
604        self.expect("settings show target.run-args", substrs=['[0]: "a b c"'])
605        self.runCmd("settings clear target.run-args")
606        self.runCmd("settings clear target.env-vars")
607        self.runCmd(
608            'settings set target.env-vars ["MY_FILE"]="this is a file name with spaces.txt"'
609        )
610        self.expect(
611            "settings show target.env-vars",
612            substrs=["MY_FILE=this is a file name with spaces.txt"],
613        )
614        self.runCmd("settings clear target.env-vars")
615        # Test and make sure that setting "format-string" settings obeys quotes
616        # if they are provided
617        self.runCmd("settings set thread-format    'abc def'   ")
618        self.expect(
619            "settings show thread-format",
620            startstr='thread-format (format-string) = "abc def"',
621        )
622        self.runCmd('settings set thread-format    "abc def"   ')
623        self.expect(
624            "settings show thread-format",
625            startstr='thread-format (format-string) = "abc def"',
626        )
627        # Make sure when no quotes are provided that we maintain any trailing
628        # spaces
629        self.runCmd("settings set thread-format abc def   ")
630        self.expect(
631            "settings show thread-format",
632            startstr='thread-format (format-string) = "abc def   "',
633        )
634        self.runCmd("settings clear thread-format")
635
636    def test_settings_with_trailing_whitespace(self):
637        # boolean
638        # Set to known value
639        self.runCmd("settings set target.skip-prologue true")
640        # Set to new value with trailing whitespace
641        self.runCmd("settings set target.skip-prologue false ")
642        # Make sure the setting was correctly set to "false"
643        self.expect(
644            "settings show target.skip-prologue",
645            SETTING_MSG("target.skip-prologue"),
646            startstr="target.skip-prologue (boolean) = false",
647        )
648        self.runCmd("settings clear target.skip-prologue", check=False)
649        # integer
650        self.runCmd("settings set term-width 70")  # Set to known value
651        # Set to new value with trailing whitespaces
652        self.runCmd("settings set term-width 60 \t")
653        self.expect(
654            "settings show term-width",
655            SETTING_MSG("term-width"),
656            startstr="term-width (unsigned) = 60",
657        )
658        self.runCmd("settings clear term-width", check=False)
659        # string
660        self.runCmd("settings set target.arg0 abc")  # Set to known value
661        # Set to new value with trailing whitespaces
662        self.runCmd("settings set target.arg0 cde\t ")
663        self.expect(
664            "settings show target.arg0",
665            SETTING_MSG("target.arg0"),
666            startstr='target.arg0 (string) = "cde"',
667        )
668        self.runCmd("settings clear target.arg0", check=False)
669        # file
670        path1 = self.getBuildArtifact("path1.txt")
671        path2 = self.getBuildArtifact("path2.txt")
672        self.runCmd("settings set target.output-path %s" % path1)  # Set to known value
673        self.expect(
674            "settings show target.output-path",
675            SETTING_MSG("target.output-path"),
676            startstr="target.output-path (file) = ",
677            substrs=[path1],
678        )
679        self.runCmd(
680            "settings set target.output-path %s " % path2
681        )  # Set to new value with trailing whitespaces
682        self.expect(
683            "settings show target.output-path",
684            SETTING_MSG("target.output-path"),
685            startstr="target.output-path (file) = ",
686            substrs=[path2],
687        )
688        self.runCmd("settings clear target.output-path", check=False)
689        # enum
690        # Set to known value
691        self.runCmd("settings set stop-disassembly-display never")
692        # Set to new value with trailing whitespaces
693        self.runCmd("settings set stop-disassembly-display always ")
694        self.expect(
695            "settings show stop-disassembly-display",
696            SETTING_MSG("stop-disassembly-display"),
697            startstr="stop-disassembly-display (enum) = always",
698        )
699        self.runCmd("settings clear stop-disassembly-display", check=False)
700        # language
701        # Set to known value
702        self.runCmd("settings set target.language c89")
703        # Set to new value with trailing whitespace
704        self.runCmd("settings set target.language c11 ")
705        self.expect(
706            "settings show target.language",
707            SETTING_MSG("target.language"),
708            startstr="target.language (language) = c11",
709        )
710        self.runCmd("settings clear target.language", check=False)
711        # arguments
712        self.runCmd("settings set target.run-args 1 2 3")  # Set to known value
713        # Set to new value with trailing whitespaces
714        self.runCmd("settings set target.run-args 3 4 5 ")
715        self.expect(
716            "settings show target.run-args",
717            SETTING_MSG("target.run-args"),
718            substrs=[
719                "target.run-args (arguments) =",
720                '[0]: "3"',
721                '[1]: "4"',
722                '[2]: "5"',
723            ],
724        )
725        self.runCmd("settings set target.run-args 1 2 3")  # Set to known value
726        # Set to new value with trailing whitespaces
727        self.runCmd("settings set target.run-args 3 \  \ ")
728        self.expect(
729            "settings show target.run-args",
730            SETTING_MSG("target.run-args"),
731            substrs=[
732                "target.run-args (arguments) =",
733                '[0]: "3"',
734                '[1]: " "',
735                '[2]: " "',
736            ],
737        )
738        self.runCmd("settings clear target.run-args", check=False)
739        # dictionaries
740        self.runCmd("settings clear target.env-vars")  # Set to known value
741        # Set to new value with trailing whitespaces
742        self.runCmd("settings set target.env-vars A=B C=D\t ")
743        self.expect(
744            "settings show target.env-vars",
745            SETTING_MSG("target.env-vars"),
746            substrs=["target.env-vars (dictionary of strings) =", "A=B", "C=D"],
747        )
748        self.runCmd("settings clear target.env-vars", check=False)
749        # regex
750        # Set to known value
751        self.runCmd("settings clear target.process.thread.step-avoid-regexp")
752        # Set to new value with trailing whitespaces
753        self.runCmd("settings set target.process.thread.step-avoid-regexp foo\\ ")
754        self.expect(
755            "settings show target.process.thread.step-avoid-regexp",
756            SETTING_MSG("target.process.thread.step-avoid-regexp"),
757            substrs=["target.process.thread.step-avoid-regexp (regex) = foo\\ "],
758        )
759        self.runCmd(
760            "settings clear target.process.thread.step-avoid-regexp", check=False
761        )
762        # format-string
763        self.runCmd("settings clear disassembly-format")  # Set to known value
764        # Set to new value with trailing whitespaces
765        self.runCmd("settings set disassembly-format foo ")
766        self.expect(
767            "settings show disassembly-format",
768            SETTING_MSG("disassembly-format"),
769            substrs=['disassembly-format (format-string) = "foo "'],
770        )
771        self.runCmd("settings clear disassembly-format", check=False)
772
773    def test_settings_list(self):
774        # List settings (and optionally test the filter to only show 'target' settings).
775        self.expect(
776            "settings list target", substrs=["arg0", "detach-on-error", "language"]
777        )
778        self.expect("settings list target", matching=False, substrs=["packet-timeout"])
779        self.expect(
780            "settings list",
781            substrs=["language", "arg0", "detach-on-error", "packet-timeout"],
782        )
783
784    def test_settings_remove_single(self):
785        # Set some environment variables and use 'remove' to delete them.
786        self.runCmd("settings set target.env-vars a=b c=d")
787        self.expect("settings show target.env-vars", substrs=["a=b", "c=d"])
788        self.runCmd("settings remove target.env-vars a")
789        self.expect("settings show target.env-vars", matching=False, substrs=["a=b"])
790        self.expect("settings show target.env-vars", substrs=["c=d"])
791        self.runCmd("settings remove target.env-vars c")
792        self.expect(
793            "settings show target.env-vars", matching=False, substrs=["a=b", "c=d"]
794        )
795
796    def test_settings_remove_multiple(self):
797        self.runCmd("settings set target.env-vars a=b c=d e=f")
798        self.expect("settings show target.env-vars", substrs=["a=b", "c=d", "e=f"])
799        self.runCmd("settings remove target.env-vars a e")
800        self.expect(
801            "settings show target.env-vars", matching=False, substrs=["a=b", "e=f"]
802        )
803        self.expect("settings show target.env-vars", substrs=["c=d"])
804
805    def test_settings_remove_nonexistent_value(self):
806        self.expect(
807            "settings remove target.env-vars doesntexist",
808            error=True,
809            substrs=["no value found named 'doesntexist'"],
810        )
811
812    def test_settings_remove_nonexistent_settings(self):
813        self.expect(
814            "settings remove doesntexist alsodoesntexist",
815            error=True,
816            substrs=["error: invalid value path 'doesntexist'"],
817        )
818
819    def test_settings_remove_missing_arg(self):
820        self.expect(
821            "settings remove",
822            error=True,
823            substrs=["'settings remove' takes an array or dictionary item, or"],
824        )
825
826    def test_settings_remove_empty_arg(self):
827        self.expect(
828            "settings remove ''",
829            error=True,
830            substrs=["'settings remove' command requires a valid variable name"],
831        )
832
833    def test_settings_clear_all(self):
834        # Change a dictionary.
835        self.runCmd("settings set target.env-vars a=1 b=2 c=3")
836        # Change an array.
837        self.runCmd("settings set target.run-args a1 b2 c3")
838        # Change a single boolean value.
839        self.runCmd("settings set auto-confirm true")
840        # Change a single integer value.
841        self.runCmd("settings set tab-size 4")
842
843        # Clear everything.
844        self.runCmd("settings clear --all")
845
846        # Check that settings have their default values after clearing.
847        self.expect(
848            "settings show target.env-vars",
849            patterns=["^target.env-vars \(dictionary of strings\) =\s*$"],
850        )
851        self.expect(
852            "settings show target.run-args",
853            patterns=["^target.run-args \(arguments\) =\s*$"],
854        )
855        self.expect("settings show auto-confirm", substrs=["false"])
856        self.expect("settings show tab-size", substrs=["2"])
857
858        # Check that the command fails if we combine '--all' option with any arguments.
859        self.expect(
860            "settings clear --all auto-confirm",
861            COMMAND_FAILED_AS_EXPECTED,
862            error=True,
863            substrs=["'settings clear --all' doesn't take any arguments"],
864        )
865
866    def test_all_settings_exist(self):
867        self.expect(
868            "settings show",
869            substrs=[
870                "auto-confirm",
871                "frame-format",
872                "notify-void",
873                "prompt",
874                "script-lang",
875                "stop-disassembly-count",
876                "stop-disassembly-display",
877                "stop-line-count-after",
878                "stop-line-count-before",
879                "stop-show-column",
880                "term-width",
881                "thread-format",
882                "use-external-editor",
883                "target.breakpoints-use-platform-avoid-list",
884                "target.default-arch",
885                "target.disable-aslr",
886                "target.disable-stdio",
887                "target.x86-disassembly-flavor",
888                "target.enable-synthetic-value",
889                "target.env-vars",
890                "target.error-path",
891                "target.exec-search-paths",
892                "target.expr-prefix",
893                "target.hex-immediate-style",
894                "target.inherit-env",
895                "target.input-path",
896                "target.language",
897                "target.max-children-count",
898                "target.max-string-summary-length",
899                "target.move-to-nearest-code",
900                "target.output-path",
901                "target.prefer-dynamic-value",
902                "target.run-args",
903                "target.skip-prologue",
904                "target.source-map",
905                "target.use-hex-immediates",
906                "target.process.disable-memory-cache",
907                "target.process.extra-startup-command",
908                "target.process.thread.trace-thread",
909                "target.process.thread.step-avoid-regexp",
910            ],
911        )
912
913    # settings under an ".experimental" domain should have two properties:
914    #   1. If the name does not exist with "experimental" in the name path,
915    #      the name lookup should try to find it without "experimental".  So
916    #      a previously-experimental setting that has been promoted to a
917    #      "real" setting will still be set by the original name.
918    #   2. Changing a setting with .experimental., name, where the setting
919    #      does not exist either with ".experimental." or without, should
920    #      not generate an error.  So if an experimental setting is removed,
921    #      people who may have that in their ~/.lldbinit files should not see
922    #      any errors.
923    def test_experimental_settings(self):
924        cmdinterp = self.dbg.GetCommandInterpreter()
925        result = lldb.SBCommandReturnObject()
926
927        # Set target.arg0 to a known value, check that we can retrieve it via
928        # the actual name and via .experimental.
929        self.expect("settings set target.arg0 first-value")
930        self.expect("settings show target.arg0", substrs=["first-value"])
931        self.expect(
932            "settings show target.experimental.arg0",
933            substrs=["first-value"],
934            error=False,
935        )
936
937        # Set target.arg0 to a new value via a target.experimental.arg0 name,
938        # verify that we can read it back via both .experimental., and not.
939        self.expect("settings set target.experimental.arg0 second-value", error=False)
940        self.expect("settings show target.arg0", substrs=["second-value"])
941        self.expect(
942            "settings show target.experimental.arg0",
943            substrs=["second-value"],
944            error=False,
945        )
946
947        # showing & setting an undefined .experimental. setting should generate no errors.
948        self.expect(
949            "settings show target.experimental.setting-which-does-not-exist",
950            patterns=["^\s$"],
951            error=False,
952        )
953        self.expect(
954            "settings set target.experimental.setting-which-does-not-exist true",
955            error=False,
956        )
957
958        # A domain component before .experimental. which does not exist should give an error
959        # But the code does not yet do that.
960        # self.expect('settings set target.setting-which-does-not-exist.experimental.arg0 true', error=True)
961
962        # finally, confirm that trying to set a setting that does not exist still fails.
963        # (SHOWING a setting that does not exist does not currently yield an error.)
964        self.expect("settings set target.setting-which-does-not-exist true", error=True)
965
966    def test_settings_set_exists(self):
967        cmdinterp = self.dbg.GetCommandInterpreter()
968
969        # An unknown option should succeed.
970        self.expect("settings set -e foo bar")
971        self.expect("settings set --exists foo bar")
972
973        # A known option should fail if its argument is invalid.
974        self.expect("settings set auto-confirm bogus", error=True)
975
976    def get_setting_json(self, setting_path=None):
977        settings_data = self.dbg.GetSetting(setting_path)
978        stream = lldb.SBStream()
979        settings_data.GetAsJSON(stream)
980        return json.loads(stream.GetData())
981
982    def verify_setting_value_json(self, setting_path, setting_value):
983        self.runCmd("settings set %s %s" % (setting_path, setting_value))
984        settings_json = self.get_setting_json(setting_path)
985        self.assertEqual(settings_json, setting_value)
986
987    def test_settings_api(self):
988        """
989        Test that ensures SBDebugger::GetSetting() APIs
990        can correctly fetch settings.
991        """
992
993        # Test basic values and embedding special JSON escaping characters.
994        self.runCmd("settings set auto-confirm true")
995        self.runCmd("settings set tab-size 4")
996        arg_value = 'hello "world"'
997        self.runCmd("settings set target.arg0 %s" % arg_value)
998
999        settings_json = self.get_setting_json()
1000        self.assertEqual(settings_json["auto-confirm"], True)
1001        self.assertEqual(settings_json["tab-size"], 4)
1002        self.assertEqual(settings_json["target"]["arg0"], arg_value)
1003
1004        settings_data = self.get_setting_json("target.arg0")
1005        self.assertEqual(settings_data, arg_value)
1006
1007        # Test OptionValueFileSpec
1008        self.verify_setting_value_json(
1009            "platform.module-cache-directory", self.getBuildDir()
1010        )
1011
1012        # Test OptionValueArray
1013        setting_path = "target.run-args"
1014        setting_value = ["value1", "value2", "value3"]
1015        self.runCmd("settings set %s %s" % (setting_path, " ".join(setting_value)))
1016        settings_json = self.get_setting_json(setting_path)
1017        self.assertEqual(settings_json, setting_value)
1018
1019        # Test OptionValueFormatEntity
1020        setting_value = """thread #${thread.index}{, name = \\'${thread.name}\\
1021        '}{, queue = ${ansi.fg.green}\\'${thread.queue}\\'${ansi.normal}}{,
1022        activity = ${ansi.fg.green}\\'${thread.info.activity.name}\\'${ansi.normal}}
1023        {, ${thread.info.trace_messages} messages}{, stop reason = ${ansi.fg.red}$
1024        {thread.stop-reason}${ansi.normal}}{\\\\nReturn value: ${thread.return-value}}
1025        {\\\\nCompleted expression: ${thread.completed-expression}}\\\\n"""
1026        self.verify_setting_value_json("thread-stop-format", setting_value)
1027
1028        # Test OptionValueRegex
1029        self.verify_setting_value_json(
1030            "target.process.thread.step-avoid-regexp", "^std::"
1031        )
1032
1033        # Test OptionValueLanguage
1034        self.verify_setting_value_json("repl-lang", "c++")
1035
1036    def test_global_option(self):
1037        # This command used to crash the settings because -g was signaled by a
1038        # NULL execution context (not one with an empty Target...) and in the
1039        # special handling for load-script-from-symbol-file this wasn't checked.
1040        self.runCmd("settings set -g target.load-script-from-symbol-file true")
1041