This is xnu-11215.1.10. See this file in:
##
# Copyright (c) 2023 Apple Inc. All rights reserved.
#
# @APPLE_OSREFERENCE_LICENSE_HEADER_START@
#
# This file contains Original Code and/or Modifications of Original Code
# as defined in and that are subject to the Apple Public Source License
# Version 2.0 (the 'License'). You may not use this file except in
# compliance with the License. The rights granted to you under the License
# may not be used to create, or enable the creation or redistribution of,
# unlawful or unlicensed copies of an Apple operating system, or to
# circumvent, violate, or enable the circumvention or violation of, any
# terms of an Apple operating system software license agreement.
#
# Please obtain a copy of the License at
# http://www.opensource.apple.com/apsl/ and read it before using this file.
#
# The Original Code and all software distributed under the License are
# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
# Please see the License for the specific language governing rights and
# limitations under the License.
#
# @APPLE_OSREFERENCE_LICENSE_HEADER_END@
##

# pylint: disable=invalid-name

""" Unit test examples

    This is not a real test suite. It only demonstrates various approaches developers
    can use to write a test.
"""

import contextlib
import io
import unittest.mock
from lldbtest.testcase import LLDBTestCase
import lldb

from lldbmock.utils import lookup_type

# Import macro function to be tested.
from process import ShowTask, P_LHASTASK, TF_HAS_PROC


class TestExamples(LLDBTestCase):
    """ Unit test examples. """

    ROUNDED_UP_PROC_SIZE = 2048

    # Mock global variable value (accessed by the macro being)
    @unittest.mock.patch('xnu.kern.globals.proc_struct_size', ROUNDED_UP_PROC_SIZE)
    def test_function(self):
        """ This test shows how to run complex function against a mock. """

        self.reset_mocks()

        PROC_ADDR = 0xffffffff90909090
        TASK_ADDR = PROC_ADDR + self.ROUNDED_UP_PROC_SIZE
        PROC_RO_ADDR = 0xffffff0040404040

        # Create fake proc_t instance at 0xffffffff90909090
        proc = self.create_mock('proc', PROC_ADDR).fromDict({
            'p_pid': 12345,
            'p_lflag': P_LHASTASK,
            'p_comm': b'test-proc\0'
        })

        # Create task which is expected to be placed + sizeof(proc)
        task = self.create_mock('task', TASK_ADDR).fromDict({
            'effective_policy': {
                'tep_sup_active': 0,
                'tep_darwinbg': 0,
                'tep_lowpri_cpu': 1
            },
            't_flags': TF_HAS_PROC
        })

        # Created shared proc_ro reference from both task/proc
        self.create_mock('proc_ro', PROC_RO_ADDR)
        proc.p_proc_ro = PROC_RO_ADDR
        task.bsd_info_ro = PROC_RO_ADDR

        # Capture stdout and check expected output
        stdout = io.StringIO()
        with contextlib.redirect_stdout(stdout):
            ShowTask([f'{TASK_ADDR:#x}'])

        # Note: Not the best way of writing a unit test.
        expected = (
            'task                 vm_map               ipc_space            #acts flags'
            '    pid   process              io_policy  wq_state  command'
            f'                         \n{TASK_ADDR:#x}   0x0                  0x0    '
            f'                  0        12345   {PROC_ADDR:#x}           L  0  0  0'
            '   test-proc                       \n'
        )
        self.assertEqual(stdout.getvalue(), expected)

    def test_command(self):
        """ Test a simple LLDB command from user's CLI.

            Creates mock of a structure and prints out member by using LLDB
            expression.
        """

        self.reset_mocks()

        PROC_ADDR = 0xffffffff90909090

        self.create_mock('proc', PROC_ADDR).fromDict({
            'p_pid': 12345,
            'p_lflag': P_LHASTASK,
            'p_comm': b'unit-test-proc\0'
        })

        res = self.run_command(f'p/x ((proc_t){PROC_ADDR:#x})->p_comm')
        self.assertEqual(res.GetOutput(), '(command_t) "unit-test-proc"\n')
        self.assertTrue(res.Succeeded())

    @unittest.skipIf(LLDBTestCase.kernel().startswith('mach.release'),
                     "Not available in RELEASE embedded")
    def test_sbapi(self):
        """ Test SBAPI on top of a mocked target. """

        DOFHELP_ADDR = 0xffffffff11220000

        # Construct simple data structure mock.
        self.create_mock('struct dof_helper', DOFHELP_ADDR).fromDict({
            'dofhp_mod': b'mock-mod',
            'dofhp_addr': 0x1234,
            'dofhp_dof': 0x5678
        })

        # Construct SBValue on top of the mock.
        addr = self.target.ResolveLoadAddress(DOFHELP_ADDR)
        sbv = self.target.CreateValueFromAddress('test',
                            addr, lookup_type('dof_helper_t'))

        self.assertTrue(sbv.IsValid() and sbv.error.success)

        # Check that LLDB SBAPI returns correct values from mock.
        err = lldb.SBError()
        self.assertEqual(
            sbv.GetChildMemberWithName('dofhp_mod').GetData().GetString(err, 0),
            "mock-mod"
        )
        self.assertEqual(
            sbv.GetChildMemberWithName('dofhp_addr').GetValueAsUnsigned(),
            0x1234
        )
        self.assertEqual(
            sbv.GetChildMemberWithName('dofhp_dof').GetValueAsUnsigned(),
            0x5678
        )

    @unittest.skipIf(LLDBTestCase.arch() != 'arm64e', "Only on arm64e")
    def test_skip_arch(self):
        """ Example of architecture specific test. """

        self.assertEqual(self.target.triple.split('-', 1)[0], 'arm64e')

    @unittest.skipIf(LLDBTestCase.variant() != 'DEVELOPMENT', "DEVELOPMENT kernel only")
    def test_skip_development(self):
        """ Test that runs only on release kernel. """

        self.assertEqual(LLDBTestCase.variant(), "DEVELOPMENT")