xref: /llvm-project/lldb/test/API/macosx/nslog/TestDarwinNSLogOutput.py (revision fa9e96a2e55226b1f9f9744f42ac5e925297f819)
1"""
2Test DarwinLog "source include debug-level" functionality provided by the
3StructuredDataDarwinLog plugin.
4
5These tests are currently only supported when running against Darwin
6targets.
7"""
8
9
10import lldb
11import platform
12import re
13import sys
14
15from lldbsuite.test.decorators import *
16from lldbsuite.test.lldbtest import *
17from lldbsuite.test import lldbtest_config
18
19
20class DarwinNSLogOutputTestCase(TestBase):
21    NO_DEBUG_INFO_TESTCASE = True
22
23    def setUp(self):
24        # Call super's setUp().
25        TestBase.setUp(self)
26        self.child = None
27        self.child_prompt = "(lldb) "
28        self.strict_sources = False
29
30        # Source filename.
31        self.source = "main.m"
32
33        # Output filename.
34        self.exe_name = self.getBuildArtifact("a.out")
35        self.d = {"OBJC_SOURCES": self.source, "EXE": self.exe_name}
36
37        # Locate breakpoint.
38        self.line = line_number(self.source, "// break here")
39
40    def tearDown(self):
41        # Shut down the process if it's still running.
42        if self.child:
43            self.runCmd("process kill")
44            self.expect_prompt()
45            self.runCmd("quit")
46
47        # Let parent clean up
48        super(DarwinNSLogOutputTestCase, self).tearDown()
49
50    def run_lldb_to_breakpoint(self, exe, source_file, line, settings_commands=None):
51        # Set self.child_prompt, which is "(lldb) ".
52        prompt = self.child_prompt
53
54        # So that the child gets torn down after the test.
55        import pexpect
56
57        self.child = pexpect.spawn(
58            "%s %s %s" % (lldbtest_config.lldbExec, self.lldbOption, exe),
59            encoding="utf-8",
60        )
61        child = self.child
62
63        # Turn on logging for what the child sends back.
64        if self.TraceOn():
65            child.logfile_read = sys.stdout
66
67        # Disable showing of source lines at our breakpoint.
68        # This is necessary for the logging tests, because the very
69        # text we want to match for output from the running inferior
70        # will show up in the source as well.  We don't want the source
71        # output to erroneously make a match with our expected output.
72        self.runCmd("settings set stop-line-count-before 0")
73        self.expect_prompt()
74        self.runCmd("settings set stop-line-count-after 0")
75        self.expect_prompt()
76
77        # Run any test-specific settings commands now.
78        if settings_commands is not None:
79            for setting_command in settings_commands:
80                self.runCmd(setting_command)
81                self.expect_prompt()
82
83        # Set the breakpoint, and run to it.
84        child.sendline("breakpoint set -f %s -l %d" % (source_file, line))
85        child.expect_exact(prompt)
86        child.sendline("run")
87        child.expect_exact(prompt)
88
89        # Ensure we stopped at a breakpoint.
90        self.runCmd("thread list")
91        self.expect(re.compile(r"stop reason = .*breakpoint"))
92
93    def runCmd(self, cmd):
94        if self.child:
95            self.child.sendline(cmd)
96
97    def expect_prompt(self, exactly=True):
98        self.expect(self.child_prompt, exactly=exactly)
99
100    def expect(self, pattern, exactly=False, *args, **kwargs):
101        if exactly:
102            return self.child.expect_exact(pattern, *args, **kwargs)
103        return self.child.expect(pattern, *args, **kwargs)
104
105    def do_test(self, expect_regexes=None, settings_commands=None):
106        """Run a test."""
107        self.build(dictionary=self.d)
108        self.setTearDownCleanup(dictionary=self.d)
109
110        exe = self.getBuildArtifact(self.exe_name)
111        self.run_lldb_to_breakpoint(
112            exe, self.source, self.line, settings_commands=settings_commands
113        )
114        self.expect_prompt()
115
116        # Now go.
117        self.runCmd("process continue")
118        self.expect(expect_regexes)
119
120    @skipIfAsan # avoid dealing with pexpect timeout flakyness on bots
121    @skipIf(oslist=["linux"], archs=["arm", "aarch64"])
122    @skipUnlessDarwin
123    @skipIfRemote  # this test is currently written using lldb commands & assumes running on local system
124    def test_nslog_output_is_displayed(self):
125        """Test that NSLog() output shows up in the command-line debugger."""
126        self.do_test(
127            expect_regexes=[
128                re.compile(r"(This is a message from NSLog)"),
129                re.compile(r"Process \d+ exited with status"),
130            ]
131        )
132        self.assertIsNotNone(self.child.match)
133        self.assertGreater(len(self.child.match.groups()), 0)
134        self.assertEqual("This is a message from NSLog", self.child.match.group(1))
135
136    @skipIfAsan # avoid dealing with pexpect timeout flakyness on bots
137    @skipIf(oslist=["linux"], archs=["arm", "aarch64"])
138    @skipUnlessDarwin
139    @skipIfRemote  # this test is currently written using lldb commands & assumes running on local system
140    def test_nslog_output_is_suppressed_with_env_var(self):
141        """Test that NSLog() output does not show up with the ignore env var."""
142        # This test will only work properly on macOS 10.12+.  Skip it on earlier versions.
143        # This will require some tweaking on iOS.
144        match = re.match(r"^\d+\.(\d+)", platform.mac_ver()[0])
145        if match is None or int(match.group(1)) < 12:
146            self.skipTest("requires macOS 10.12 or higher")
147
148        self.do_test(
149            expect_regexes=[
150                re.compile(r"(This is a message from NSLog)"),
151                re.compile(r"Process \d+ exited with status"),
152            ],
153            settings_commands=[
154                "settings set target.env-vars " '"IDE_DISABLED_OS_ACTIVITY_DT_MODE=1"'
155            ],
156        )
157        self.assertIsNotNone(self.child.match)
158        self.assertEqual(len(self.child.match.groups()), 0)
159