"""
Test lldb data formatter subsystem.
"""


import lldb
from lldbsuite.test.lldbtest import *
import lldbsuite.test.lldbutil as lldbutil
import re


class AdvDataFormatterTestCase(TestBase):
    def setUp(self):
        # Call super's setUp().
        TestBase.setUp(self)
        # Find the line number to break at.
        self.line = line_number("main.cpp", "// Set break point at this line.")

    def test_with_run_command(self):
        """Test that that file and class static variables display correctly."""
        self.build()
        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)

        lldbutil.run_break_set_by_file_and_line(
            self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True
        )

        self.runCmd("run", RUN_SUCCEEDED)

        # The stop reason of the thread should be breakpoint.
        self.expect(
            "thread list",
            STOPPED_DUE_TO_BREAKPOINT,
            substrs=["stopped", "stop reason = breakpoint"],
        )

        # This is the function to remove the custom formats in order to have a
        # clean slate for the next test case.
        def cleanup():
            self.runCmd("type format clear", check=False)
            self.runCmd("type summary clear", check=False)
            self.runCmd("settings set target.max-children-count 24", check=False)

        # Execute the cleanup function during test case tear down.
        self.addTearDownHook(cleanup)

        self.runCmd('type summary add --summary-string "pippo" "i_am_cool"')

        self.runCmd('type summary add --summary-string "pluto" -x "i_am_cool[a-z]*"')

        self.expect("frame variable cool_boy", substrs=["pippo"])

        self.expect("frame variable cooler_boy", substrs=["pluto"])

        self.runCmd("type summary delete i_am_cool")

        self.expect("frame variable cool_boy", substrs=["pluto"])

        self.runCmd("type summary clear")

        self.runCmd('type summary add --summary-string "${var[]}" -x "^int\\[[0-9]\\]')

        self.expect("frame variable int_array", substrs=["1,2,3,4,5"])
        self.expect("frame variable const_int_array", substrs=["11,12,13,14,15"])

        # this will fail if we don't do [] as regex correctly
        self.runCmd('type summary add --summary-string "${var[].integer}" "i_am_cool[]')

        self.expect("frame variable cool_array", substrs=["1,1,1,1,6"])

        self.runCmd("type summary clear")

        self.runCmd('type summary add --summary-string "${var[1-0]%x}" "int"')

        self.expect("frame variable iAmInt", substrs=["01"])

        self.runCmd('type summary add --summary-string "${var[0-1]%x}" "int"')

        self.expect("frame variable iAmInt", substrs=["01"])

        self.runCmd("type summary clear")

        self.runCmd('type summary add --summary-string "${var[0-1]%x}" int')
        self.runCmd('type summary add --summary-string "${var[0-31]%x}" float')

        self.expect("frame variable *pointer", substrs=["0x", "2"])

        # check fix for <rdar://problem/11338654> LLDB crashes when using a
        # "type summary" that uses bitfields with no format
        self.runCmd('type summary add --summary-string "${var[0-1]}" int')
        self.expect("frame variable iAmInt", substrs=["9 1"])

        self.expect("frame variable cool_array[3].floating", substrs=["0x"])

        self.runCmd(
            'type summary add --summary-string "low bits are ${*var[0-1]} tgt is ${*var}" "int *"'
        )

        self.expect("frame variable pointer", substrs=["low bits are", "tgt is 6"])

        self.expect(
            'frame variable int_array --summary-string "${*var[0-1]}"', substrs=["3"]
        )

        self.runCmd("type summary clear")

        self.runCmd(
            r'type summary add --summary-string "${var[0-1]}" -x "int\[[0-9]\]"'
        )

        self.expect("frame variable int_array", substrs=["1,2"])

        self.runCmd('type summary add --summary-string "${var[0-1]}" "int[]"')

        self.expect("frame variable int_array", substrs=["1,2"])

        # Test the patterns are matched in reverse-chronological order.
        self.runCmd('type summary add --summary-string "${var[2-3]}" "int[]"')

        self.expect("frame variable int_array", substrs=["3,4"])

        self.runCmd("type summary clear")

        self.runCmd(r'type summary add -c -x "i_am_cool\[[0-9]\]"')
        self.runCmd("type summary add -c i_am_cool")

        self.expect(
            "frame variable cool_array",
            substrs=[
                "[0]",
                "integer",
                "floating",
                "character",
                "[1]",
                "integer",
                "floating",
                "character",
                "[2]",
                "integer",
                "floating",
                "character",
                "[3]",
                "integer",
                "floating",
                "character",
                "[4]",
                "integer",
                "floating",
                "character",
            ],
        )

        self.runCmd(
            'type summary add --summary-string "int = ${*var.int_pointer}, float = ${*var.float_pointer}" IWrapPointers'
        )

        self.expect("frame variable wrapper", substrs=["int = 4", "float = 1.1"])

        self.runCmd(
            'type summary add --summary-string "low bits = ${*var.int_pointer[2]}" IWrapPointers -p'
        )

        self.expect("frame variable wrapper", substrs=["low bits = 1"])

        self.expect("frame variable *wrap_pointer", substrs=["low bits = 1"])

        self.runCmd("type summary clear")

        self.expect(
            'frame variable int_array --summary-string "${var[0][0-2]%hex}"',
            substrs=["0x", "7"],
        )

        self.runCmd("type summary clear")

        self.runCmd(
            r'type summary add --summary-string "${*var[].x[0-3]%hex} is a bitfield on a set of integers" -x "SimpleWithPointers\[[0-9]\]"'
        )

        self.expect(
            'frame variable couple --summary-string "${*var.sp.x[0-2]} are low bits of integer ${*var.sp.x}. If I pretend it is an array I get ${var.sp.x[0-5]}"',
            substrs=[
                "1 are low bits of integer 9.",
                "If I pretend it is an array I get [9,",
            ],
        )

        # if the summary has an error, we still display the value
        self.expect(
            'frame variable couple --summary-string "${*var.sp.foo[0-2]"',
            substrs=["(Couple) couple = {", "x = 0x", "y = 0x", "z = 0x", "s = 0x"],
        )

        self.runCmd(
            'type summary add --summary-string "${*var.sp.x[0-2]} are low bits of integer ${*var.sp.x}. If I pretend it is an array I get ${var.sp.x[0-5]}" Couple'
        )

        self.expect(
            "frame variable sparray", substrs=["[0x0000000f,0x0000000c,0x00000009]"]
        )

        # check that we can format a variable in a summary even if a format is
        # defined for its datatype
        self.runCmd("type format add -f hex int")
        self.runCmd('type summary add --summary-string "x=${var.x%d}" Simple')

        self.expect("frame variable a_simple_object", substrs=["x=3"])

        self.expect("frame variable a_simple_object", matching=False, substrs=["0x0"])

        # now check that the default is applied if we do not hand out a format
        self.runCmd('type summary add --summary-string "x=${var.x}" Simple')

        self.expect("frame variable a_simple_object", matching=False, substrs=["x=3"])

        self.expect(
            "frame variable a_simple_object", matching=True, substrs=["x=0x00000003"]
        )

        self.expect_var_path("constInt", value="0x0000002a")

        self.expect_var_path("volatileInt", value="0x0000002b")

        self.expect_var_path("constVolatileInt", value="0x0000002c")

        # check that we can correctly cap the number of children shown
        self.runCmd("settings set target.max-children-count 5")

        self.expect(
            "frame variable a_long_guy",
            matching=True,
            substrs=["a_1", "b_1", "c_1", "d_1", "e_1", "..."],
        )

        # check that no further stuff is printed (not ALL values are checked!)
        self.expect(
            "frame variable a_long_guy",
            matching=False,
            substrs=[
                "f_1",
                "g_1",
                "h_1",
                "i_1",
                "j_1",
                "q_1",
                "a_2",
                "f_2",
                "t_2",
                "w_2",
            ],
        )

        self.runCmd("settings set target.max-children-count 1")
        self.expect("frame variable a_long_guy", matching=True, substrs=["a_1", "..."])
        self.expect(
            "frame variable a_long_guy",
            matching=False,
            substrs=["b_1", "c_1", "d_1", "e_1"],
        )
        self.expect(
            "frame variable a_long_guy",
            matching=False,
            substrs=[
                "f_1",
                "g_1",
                "h_1",
                "i_1",
                "j_1",
                "q_1",
                "a_2",
                "f_2",
                "t_2",
                "w_2",
            ],
        )

        self.runCmd("settings set target.max-children-count 30")
        self.expect(
            "frame variable a_long_guy",
            matching=True,
            substrs=[
                "a_1",
                "b_1",
                "c_1",
                "d_1",
                "e_1",
                "z_1",
                "a_2",
                "b_2",
                "c_2",
                "d_2",
                "...",
            ],
        )
        self.expect(
            "frame variable a_long_guy",
            matching=False,
            substrs=["e_2", "n_2", "r_2", "i_2", "k_2", "o_2"],
        )

        self.runCmd("settings set target.max-string-summary-length 5")
        some_string = self.frame().FindVariable("some_string")
        some_string_summary = some_string.GetSummary()
        if re.match(r"^std::__\w+::", some_string.GetTypeName()):
            self.assertEqual(some_string_summary, '"01234"...')
        else:
            # libstdc++ string formatter suffers from the same problem as some_cstring below
            pass

        some_carr = self.frame().FindVariable("some_carr")
        some_carr_summary = some_carr.GetSummary()
        self.assertEqual(some_carr_summary, '"01234"...')

        # FIXME: c-strings should honor the target.max-string-summary-length
        # setting. Currently a C-string will be truncated at 64 (an internal
        # implementation detail) instead of the value specified in the setting.
        some_cstring = self.frame().FindVariable("some_cstring")
        some_cstring_summary = some_cstring.GetSummary()
        self.assertEqual(len(some_cstring_summary), 66)  # 64 + 2 (for quotation marks)
        self.assertFalse(some_cstring_summary.endswith("..."))

        # override the cap
        self.expect(
            "frame variable a_long_guy --show-all-children",
            matching=True,
            substrs=[
                "a_1",
                "b_1",
                "c_1",
                "d_1",
                "e_1",
                "z_1",
                "a_2",
                "b_2",
                "c_2",
                "d_2",
            ],
        )
        self.expect(
            "frame variable a_long_guy --show-all-children",
            matching=True,
            substrs=[
                "e_2",
                "i_2",
                "k_2",
                "n_2",
                "o_2",
                "r_2",
            ],
        )
        self.expect(
            "frame variable a_long_guy --show-all-children",
            matching=False,
            substrs=["..."],
        )
