llvm/lldb/test/API/macosx/nslog/TestDarwinNSLogOutput.py

"""
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):
    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"])
    @skipUnlessDarwin
    @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"])
    @skipUnlessDarwin
    @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)