| """ | |
| Test DarwinLog "source include debug-level" functionality provided by the | |
| StructuredDataDarwinLog plugin. | |
| These tests are currently only supported when running against Darwin | |
| targets. | |
| """ | |
| import lldb | |
| import platform | |
| import re | |
| import sys | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbtest_config | |
| class DarwinNSLogOutputTestCase(TestBase): | |
| SHARED_BUILD_TESTCASE = False | |
| NO_DEBUG_INFO_TESTCASE = True | |
| def setUp(self): | |
| # Call super's setUp(). | |
| TestBase.setUp(self) | |
| self.child = None | |
| self.child_prompt = "(lldb) " | |
| self.strict_sources = False | |
| # Source filename. | |
| self.source = "main.m" | |
| # Output filename. | |
| self.exe_name = self.getBuildArtifact("a.out") | |
| self.d = {"OBJC_SOURCES": self.source, "EXE": self.exe_name} | |
| # Locate breakpoint. | |
| self.line = line_number(self.source, "// break here") | |
| def tearDown(self): | |
| # Shut down the process if it's still running. | |
| if self.child: | |
| self.runCmd("process kill") | |
| self.expect_prompt() | |
| self.runCmd("quit") | |
| # Let parent clean up | |
| super(DarwinNSLogOutputTestCase, self).tearDown() | |
| def run_lldb_to_breakpoint(self, exe, source_file, line, settings_commands=None): | |
| # Set self.child_prompt, which is "(lldb) ". | |
| prompt = self.child_prompt | |
| # So that the child gets torn down after the test. | |
| import pexpect | |
| self.child = pexpect.spawn( | |
| "%s %s %s" % (lldbtest_config.lldbExec, self.lldbOption, exe), | |
| encoding="utf-8", | |
| ) | |
| child = self.child | |
| # Turn on logging for what the child sends back. | |
| if self.TraceOn(): | |
| child.logfile_read = sys.stdout | |
| # Disable showing of source lines at our breakpoint. | |
| # This is necessary for the logging tests, because the very | |
| # text we want to match for output from the running inferior | |
| # will show up in the source as well. We don't want the source | |
| # output to erroneously make a match with our expected output. | |
| self.runCmd("settings set stop-line-count-before 0") | |
| self.expect_prompt() | |
| self.runCmd("settings set stop-line-count-after 0") | |
| self.expect_prompt() | |
| # Run any test-specific settings commands now. | |
| if settings_commands is not None: | |
| for setting_command in settings_commands: | |
| self.runCmd(setting_command) | |
| self.expect_prompt() | |
| # Set the breakpoint, and run to it. | |
| child.sendline("breakpoint set -f %s -l %d" % (source_file, line)) | |
| child.expect_exact(prompt) | |
| child.sendline("run") | |
| child.expect_exact(prompt) | |
| # Ensure we stopped at a breakpoint. | |
| self.runCmd("thread list") | |
| self.expect(re.compile(r"stop reason = .*breakpoint")) | |
| def runCmd(self, cmd): | |
| if self.child: | |
| self.child.sendline(cmd) | |
| def expect_prompt(self, exactly=True): | |
| self.expect(self.child_prompt, exactly=exactly) | |
| def expect(self, pattern, exactly=False, *args, **kwargs): | |
| if exactly: | |
| return self.child.expect_exact(pattern, *args, **kwargs) | |
| return self.child.expect(pattern, *args, **kwargs) | |
| def do_test(self, expect_regexes=None, settings_commands=None): | |
| """Run a test.""" | |
| self.build(dictionary=self.d) | |
| self.setTearDownCleanup(dictionary=self.d) | |
| exe = self.getBuildArtifact(self.exe_name) | |
| self.run_lldb_to_breakpoint( | |
| exe, self.source, self.line, settings_commands=settings_commands | |
| ) | |
| self.expect_prompt() | |
| # Now go. | |
| self.runCmd("process continue") | |
| self.expect(expect_regexes) | |
| @skipIfAsan # avoid dealing with pexpect timeout flakyness on bots | |
| @skipIf(oslist=["linux"], archs=["arm$", "aarch64"]) | |
| @requireDarwin | |
| @skipIfRemote # this test is currently written using lldb commands & assumes running on local system | |
| def test_nslog_output_is_displayed(self): | |
| """Test that NSLog() output shows up in the command-line debugger.""" | |
| self.do_test( | |
| expect_regexes=[ | |
| re.compile(r"(This is a message from NSLog)"), | |
| re.compile(r"Process \d+ exited with status"), | |
| ] | |
| ) | |
| self.assertIsNotNone(self.child.match) | |
| self.assertGreater(len(self.child.match.groups()), 0) | |
| self.assertEqual("This is a message from NSLog", self.child.match.group(1)) | |
| @skipIfAsan # avoid dealing with pexpect timeout flakyness on bots | |
| @skipIf(oslist=["linux"], archs=["arm$", "aarch64"]) | |
| @requireDarwin | |
| @skipIfRemote # this test is currently written using lldb commands & assumes running on local system | |
| def test_nslog_output_is_suppressed_with_env_var(self): | |
| """Test that NSLog() output does not show up with the ignore env var.""" | |
| # This test will only work properly on macOS 10.12+. Skip it on earlier versions. | |
| # This will require some tweaking on iOS. | |
| match = re.match(r"^\d+\.(\d+)", platform.mac_ver()[0]) | |
| if match is None or int(match.group(1)) < 12: | |
| self.skipTest("requires macOS 10.12 or higher") | |
| self.do_test( | |
| expect_regexes=[ | |
| re.compile(r"(This is a message from NSLog)"), | |
| re.compile(r"Process \d+ exited with status"), | |
| ], | |
| settings_commands=[ | |
| "settings set target.env-vars " '"IDE_DISABLED_OS_ACTIVITY_DT_MODE=1"' | |
| ], | |
| ) | |
| self.assertIsNotNone(self.child.match) | |
| self.assertEqual(len(self.child.match.groups()), 0) |