"""
Test breakpoint hit count features.
"""


import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class BreakpointHitCountTestCase(TestBase):
    NO_DEBUG_INFO_TESTCASE = True

    @add_test_categories(["pyapi"])
    def test_breakpoint_location_hit_count(self):
        """Use Python APIs to check breakpoint hit count."""
        self.build()
        self.do_test_breakpoint_location_hit_count()

    def test_breakpoint_one_shot(self):
        """Check that one-shot breakpoints trigger only once."""
        self.build()
        target = self.createTestTarget()

        self.runCmd("tb a")
        process = target.LaunchSimple(None, None, self.get_process_working_directory())
        self.assertTrue(process, PROCESS_IS_VALID)

        from lldbsuite.test.lldbutil import get_stopped_thread

        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
        self.assertTrue(
            thread.IsValid(), "There should be a thread stopped due to breakpoint"
        )

        frame0 = thread.GetFrameAtIndex(0)
        self.assertTrue(
            frame0.GetFunctionName() == "a(int)"
            or frame0.GetFunctionName() == "int a(int)"
        )

        process.Continue()
        self.assertState(process.GetState(), lldb.eStateExited)

    def setUp(self):
        # Call super's setUp().
        TestBase.setUp(self)
        self.a_int_body_line_no = line_number("main.cpp", "// Breakpoint Location 1")
        self.a_float_body_line_no = line_number("main.cpp", "// Breakpoint Location 2")

    def do_test_breakpoint_location_hit_count(self):
        """Use Python APIs to check breakpoint hit count."""
        target = self.createTestTarget()

        # Create a breakpoint in main.cpp by name 'a',
        # there should be two locations.
        breakpoint = target.BreakpointCreateByName("a", "a.out")
        self.assertTrue(
            breakpoint and breakpoint.GetNumLocations() == 2, VALID_BREAKPOINT
        )

        # Verify all breakpoint locations are enabled.
        location1 = breakpoint.GetLocationAtIndex(0)
        self.assertTrue(location1 and location1.IsEnabled(), VALID_BREAKPOINT_LOCATION)

        location2 = breakpoint.GetLocationAtIndex(1)
        self.assertTrue(location2 and location2.IsEnabled(), VALID_BREAKPOINT_LOCATION)

        # Launch the process, and do not stop at entry point.
        process = target.LaunchSimple(None, None, self.get_process_working_directory())
        self.assertTrue(process, PROCESS_IS_VALID)

        # Verify 1st breakpoint location is hit.
        from lldbsuite.test.lldbutil import get_stopped_thread

        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
        self.assertTrue(
            thread.IsValid(), "There should be a thread stopped due to breakpoint"
        )

        frame0 = thread.GetFrameAtIndex(0)
        location1 = breakpoint.FindLocationByAddress(frame0.GetPC())
        self.assertEqual(
            frame0.GetLineEntry().GetLine(),
            self.a_int_body_line_no,
            "Stopped in int a(int)",
        )
        self.assertTrue(location1)
        self.assertEqual(location1.GetHitCount(), 1)
        self.assertEqual(breakpoint.GetHitCount(), 1)

        process.Continue()

        # Verify 2nd breakpoint location is hit.
        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
        self.assertTrue(
            thread.IsValid(), "There should be a thread stopped due to breakpoint"
        )

        frame0 = thread.GetFrameAtIndex(0)
        location2 = breakpoint.FindLocationByAddress(frame0.GetPC())
        self.assertEqual(
            frame0.GetLineEntry().GetLine(),
            self.a_float_body_line_no,
            "Stopped in float a(float)",
        )
        self.assertTrue(location2)
        self.assertEqual(location2.GetHitCount(), 1)
        self.assertEqual(location1.GetHitCount(), 1)
        self.assertEqual(breakpoint.GetHitCount(), 2)

        process.Continue()

        # Verify 2nd breakpoint location is hit again.
        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
        self.assertTrue(
            thread.IsValid(), "There should be a thread stopped due to breakpoint"
        )

        self.assertEqual(location2.GetHitCount(), 2)
        self.assertEqual(location1.GetHitCount(), 1)
        self.assertEqual(breakpoint.GetHitCount(), 3)

        process.Continue()
