xref: /xnu-11215.1.10/tools/lldbmacros/tests/runtests.py (revision 8d741a5de7ff4191bf97d57b9f54c2f6d4a15585)
1#!/usr/bin/env python3
2
3##
4# Copyright (c) 2023 Apple Inc. All rights reserved.
5#
6# @APPLE_OSREFERENCE_LICENSE_HEADER_START@
7#
8# This file contains Original Code and/or Modifications of Original Code
9# as defined in and that are subject to the Apple Public Source License
10# Version 2.0 (the 'License'). You may not use this file except in
11# compliance with the License. The rights granted to you under the License
12# may not be used to create, or enable the creation or redistribution of,
13# unlawful or unlicensed copies of an Apple operating system, or to
14# circumvent, violate, or enable the circumvention or violation of, any
15# terms of an Apple operating system software license agreement.
16#
17# Please obtain a copy of the License at
18# http://www.opensource.apple.com/apsl/ and read it before using this file.
19#
20# The Original Code and all software distributed under the License are
21# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25# Please see the License for the specific language governing rights and
26# limitations under the License.
27#
28# @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29##
30
31"""
32LLDB unit-test runner.
33
34Discovers all unit-test that require LLDB instance and runs them from within
35LLDB testing environment.
36"""
37import atexit
38import argparse
39import unittest
40from unittest.mock import patch, MagicMock
41import sys
42import json
43from pathlib import Path
44
45from lldbtest.coverage import cov_html
46from lldbtest.unittest import LLDBTextTestRunner, LLDBJSONTestRunner
47
48#
49# Handle arguments
50#
51parser = argparse.ArgumentParser(
52    prog='runtests',
53    description='Runs lldb macro unit-tests against selected kernel'
54)
55parser.add_argument('kernel')
56parser.add_argument('-v', '--verbose', action='store_true')
57parser.add_argument('-d', '--debug', action='store_true')
58parser.add_argument('-c', '--coverage')
59parser.add_argument('-s', '--standalone', action='store_true')
60parser.add_argument('-j', '--json')
61
62args = parser.parse_args()
63
64# To avoid duplicates call this each time a script exists.
65def exit_handler():
66    if args.coverage:
67        print('writing out coverage report ...')
68        cov_html(directory=args.coverage)
69
70    print('done.')
71
72atexit.register(exit_handler)
73
74SCRIPT_PATH = Path(__file__).parent
75
76# Select test runner class
77RunnerClass = LLDBJSONTestRunner if args.json else LLDBTextTestRunner
78
79#
80# A unit-test discovery requires to import tests as a module. This in turns
81# imports XNU which re-exports all dSYM modules. This results in failure to
82# discover tests.
83#
84# For now mock away lldb and lldb_wrap which are not available for standalone
85# unit tests.
86#
87print("Running standalone unit tests\n")
88
89with patch.dict('sys.modules', { 'lldb': MagicMock(), 'core.lldbwrap': MagicMock() }):
90
91    # Discover unit-tests
92    tests = unittest.TestLoader().discover(SCRIPT_PATH / "standalone_tests")
93
94    # Open output file if requested
95    if args.json:
96        with open(f"{args.json}-standalone.json", 'wt') as outfile:
97            runner = RunnerClass(verbosity=2 if args.verbose else 1, debug=args.debug,
98                                 stream=outfile)
99            runner.run(tests)
100    else:
101        runner = RunnerClass(verbosity=2 if args.verbose else 1, debug=args.debug)
102        runner.run(tests)
103
104if args.standalone:
105    sys.exit()
106
107#
108# Discover and run LLDB tests
109#
110print('Running LLDB unit tests\n')
111
112try:
113    import lldb
114except ImportError:
115    print("LLDB not available skipping tests.")
116    sys.exit()
117
118# Create Debugger instance
119debugger = lldb.SBDebugger.Create()
120
121# Created ScriptedProcess target for selected kernel binary
122error = lldb.SBError()
123target = debugger.CreateTargetWithFileAndArch(args.kernel, None)
124
125# Load scripts
126ret = lldb.SBCommandReturnObject()
127ci = debugger.GetCommandInterpreter()
128
129print('Loading scripted process plugin')
130ci.HandleCommand(f'command script import {SCRIPT_PATH / "lldb_test_process.py"}',
131    ret)
132
133#
134# Create Scripted Process for testing.
135# Python running this script and Python running inside LLDB may not match.
136# It is prefered to not shared anything across this boundary.
137#
138structured_data = lldb.SBStructuredData()
139structured_data.SetFromJSON(json.dumps({
140    "verbose": args.verbose,
141    "debug": args.debug,
142    "json": args.json
143}))
144
145launch_info = lldb.SBLaunchInfo(None)
146launch_info.SetScriptedProcessDictionary(structured_data)
147launch_info.SetLaunchFlags(lldb.eLaunchFlagStopAtEntry)
148launch_info.SetWorkingDirectory(".")
149launch_info.SetProcessPluginName("ScriptedProcess")
150launch_info.SetScriptedProcessClassName('lldb_test_process.TestProcess')
151
152process = target.Launch(launch_info, error)
153if error.fail:
154    print(error.description)
155if not error.Success():
156    sys.exit()
157
158ci.HandleCommand('run-unit-tests', ret)
159