本页提供了有关如何通过 fx test
命令使用 Fuchsia 调试程序 (zxdb
) 的详细信息和示例。
从 fx 测试进入调试程序
fx test
命令支持 --break-on-failure
和 --breakpoint
标志,让您可以使用 zxdb
调试测试。如果您的测试使用兼容的测试运行程序(即目前的 gTest、gUnit 或 Rust),添加 --break-on-failure
标志会导致测试失败情况暂停测试执行并进入 zxdb
调试环境,例如:
$ fx test --break-on-failure rust_crasher_test.cm
<...fx test startup...>
Running 1 tests
Starting: fuchsia-pkg://fuchsia.com/crasher_test#meta/rust_crasher_test.cm
Command: fx ffx test run --max-severity-logs WARN --break-on-failure fuchsia-pkg://fuchsia.com/crasher_test?hash=1cceb326c127e245f0052367142aee001f82a73c6f33091fe7999d43a94b1b34#meta/rust_crasher_test.cm
Status: [duration: 13.3s] [tasks: 3 running, 15/18 complete]
Running 1 tests [ ] 0.0%
⚠️ zxdb caught test failure in rust_crasher_test.cm, type `frame` to get started.
14 LLVM_LIBC_FUNCTION(void, abort, ()) {
15 for (;;) {
▶ 16 CRASH_WITH_UNIQUE_BACKTRACE();
17 _zx_process_exit(ZX_TASK_RETCODE_EXCEPTION_KILL);
18 }
══════════════════════════
Invalid opcode exception
══════════════════════════
Process 1 (koid=107752) thread 1 (koid=107754)
Faulting instruction: 0x4159210ab797
🛑 process 1 __llvm_libc::__abort_impl__() • abort.cc:16
[zxdb]
从现在开始,您可以照常使用 zxdb
。不过,由于线程已处于严重异常状态,step
、next
和 until
等典型的执行命令不可用。print
、frame
和 backtrace
等检查命令在调试会话期间可用。
并行执行测试用例
根据为 fx test
提供的选项或测试运行程序默认配置,多个测试用例可能会并行失败。受支持的测试运行程序会为每个测试用例生成单独的进程,默认配置可能允许多个测试进程同时运行。
使用 fx test
运行时,zxdb
会附加到测试领域中的所有进程。如果测试用例失败,则系统将仅停止该特定进程。
仅当测试用例失败数量等于允许的并行测试用例数量时,才会停止并行执行。一旦任何进程与 zxdb
分离,系统会立即开始另一个测试用例进程。
zxdb
旨在处理多进程调试。您可以使用 process
名词或 status
命令检查当前连接的进程及其执行状态。目前的“活跃”进程标有“▶”符号。
如需了解详情,请参阅互动模型(或使用 help
命令)。
关闭调试程序
检查完测试失败情况后,您可以通过退出测试流程(例如,使用 kill
、detach
或 continue
)来恢复测试作业。
如上一部分所述,多个测试用例可能会并行失败。如果您未明确与所有已连接的进程分离,zxdb
仍会位于前台。您可以使用 process
名词查看所有附加的进程。
您还可以使用某些命令与所有进程(例如 quit
、detach *
和 ctrl+d
)分离并立即恢复测试套件的执行。
教程
本教程介绍了使用 fx test
命令和 Fuchsia 调试程序 (zxdb
) 的调试工作流。
1. 了解测试用例
Rust
Rust 测试由 Rust 测试运行程序执行。与用于 C++ 测试的 gTest 或 gUnit 运行程序不同,Rust 测试运行程序默认并行运行测试用例。这会在使用 --break-on-failure
功能时产生不同的体验。如需了解调试并行测试流程时的预期,请参阅关于调试并行流程的部分。这种情况受支持,并且适用于 zxdb
。
以下是一些 rust 测试代码示例(在原始版本的基础上进行了修改),为简洁起见,已采用缩写:
...
let mut log_helper2 = LogSinkHelper::new(&directory);
log_helper2.write_log("my msg1");
log_helper.write_log("my msg2");
let mut expected = vec!["my msg1".to_owned(), "my msg3".to_owned()];
expected.sort();
let mut actual = vec![recv_logs.next().await.unwrap(), recv_logs.next().await.unwrap()];
actual.sort();
assert_eq!(expected, actual);
...
C++
默认情况下,gTest 测试运行程序会依次执行测试用例,因此一次只能调试一个测试失败项。支持并行执行测试用例,可以通过向 fx test
命令添加 --parallel-cases
标志来实现。我们来看一些使用 gTest(为简洁而缩写)的 C++ 测试代码示例:
// Inject 1 process.
auto process1 = std::make_unique<MockProcess>(nullptr, kProcessKoid1, kProcessName1);
process1->AddThread(kProcess1ThreadKoid1);
harness.debug_agent()->InjectProcessForTest(std::move(process1));
// And another, with 2 threads.
auto process2 = std::make_unique<MockProcess>(nullptr, kProcessKoid2, kProcessName2);
process2->AddThread(kProcess2ThreadKoid1);
process2->AddThread(kProcess2ThreadKoid2);
harness.debug_agent()->InjectProcessForTest(std::move(process2));
reply = {};
remote_api->OnStatus(request, &reply);
ASSERT_EQ(reply.processes.size(), 3u); // <-- This will fail, since reply.processes.size() == 2
EXPECT_EQ(reply.processes[0].process_koid, kProcessKoid1);
EXPECT_EQ(reply.processes[0].process_name, kProcessName1);
...
2. 执行测试
Rust
使用 fx test --break-on-failure
命令执行测试,例如:
$ fx test -o --break-on-failure archivist-unittests
<...fx test startup...>
Running 1 tests
Starting: fuchsia-pkg://fuchsia.com/archivist-tests#meta/archivist-unittests.cm
Command: fx ffx test run --max-severity-logs WARN --break-on-failure fuchsia-pkg://fuchsia.com/archivist-tests?hash=9a531e48fe82d86edef22f86f7e9b819d18a7d678f0823912d9224dd91f8926f#meta/archivist-unittests.cm
Running test 'fuchsia-pkg://fuchsia.com/archivist-tests?hash=9a531e48fe82d86edef22f86f7e9b819d18a7d678f0823912d9224dd91f8926f#meta/archivist-unittests.cm'
[RUNNING] archivist::tests::can_log_and_retrive_log
[101430.272555][5631048][5631050][<root>][can_log_and_retrive_log] WARN: Failed to create event source for log sink requests err=Error connecting to protocol path: /events/log_sink_requested_event_stream
Caused by:
NOT_FOUND
[101430.277339][5631048][5631050][<root>][can_log_and_retrive_log] WARN: Failed to create event source for InspectSink requests err=Error connecting to protocol path: /events/inspect_sink_requested_event_stream
[101430.336160][5631048][5631050][<root>][can_log_and_retrive_log] INFO: archivist: Entering core loop.
[101430.395986][5631048][5631050][<root>][can_log_and_retrive_log] ERROR: [src/lib/diagnostics/log/rust/src/lib.rs(62)] PANIC info=panicked at ../../src/diagnostics/archivist/src/archivist.rs:544:9:
assertion `left == right` failed
left: ["my msg1", "my msg2"]
right: ["my msg1", "my msg3"]
👋 zxdb is loading symbols to debug test failure in archivist-unittests.cm, please wait.
⚠️ test failure in archivist-unittests.cm, type `frame` or `help` to get started.
11 namespace LIBC_NAMESPACE {
12
▶ 13 LLVM_LIBC_FUNCTION(void, abort, ()) { CRASH_WITH_UNIQUE_BACKTRACE(); }
14
15 } // namespace LIBC_NAMESPACE
══════════════════════════
Invalid opcode exception
══════════════════════════
Process 10 (koid=5495424) thread 1 (koid=5495428)
Faulting instruction: 0x41a5680114d7
🛑 process 10 __llvm_libc::__abort_impl__() • abort.cc:13
[zxdb]
请注意,测试的输出是混合的,这是因为 Rust 测试运行程序默认以并行方式运行测试用例。您可以通过将此 --parallel-cases
选项用于 fx test
来避免这种情况,例如:fx test --parallel-cases 1 --break-on-failure archivist-unittests
C++
使用 fx test --break-on-failure
命令执行测试,例如:
$ fx test -o --break-on-failure debug_agent_unit_tests
<...fx test startup...>
Starting: fuchsia-pkg://fuchsia.com/debug_agent_unit_tests#meta/debug_agent_unit_tests.cm (NOT HERMETIC)
Command: fx ffx test run --realm /core/testing:system-tests --max-severity-logs WARN --break-on-failure fuchsia-pkg://fuchsia.com/debug_agent_unit_tests?hash=3f6d97801bb147034a344e3fe1bb69291a7b690b9d3d075246ddcba59397ac12#meta/debug_agent_unit_tests.cm
Status: [duration: 30.9s] [tasks: 3 running, 15/19 complete]
Running 2 tests [ ] 0.0%
⚠️ zxdb caught test failure in debug_agent_unit_tests.cm, type `frame` to get started.
5381 (defined(__x86_64__) || defined(__i386__)))
5382 // with clang/gcc we can achieve the same effect on x86 by invoking int3
▶ 5383 asm("int3");
5384 #elif GTEST_HAS_BUILTIN(__builtin_trap)
5385 __builtin_trap();
🛑 thread 1 testing::UnitTest::AddTestPartResult(testing::UnitTest*, testing::TestPartResult::Type, const char*, int, std::__2::string const&, std::__2::string const&) • gtest.cc:5383
[zxdb]
3. 检查失败情况
Rust
我们捕获了测试失败的情况,Rust 测试会在失败时发出 abort
,zxdb
会通知并报告失败情况。我们可以通过 list
查看当前帧的代码,例如:
[zxdb] list
8
9 #include "src/__support/common.h"
10
11 namespace LIBC_NAMESPACE {
12
▶ 13 LLVM_LIBC_FUNCTION(void, abort, ()) { CRASH_WITH_UNIQUE_BACKTRACE(); }
14
15 } // namespace LIBC_NAMESPACE
[zxdb]
但这不是我们感兴趣的代码。当我们查看堆栈轨迹时,会看到测试中的代码位于帧 17 中:
[zxdb] frame
▶ 0…12 «Rust library» (-r expands)
13 std::panicking::begin_panic_handler(…) • library/std/src/panicking.rs:645
14 core::panicking::panic_fmt(…) • library/core/src/panicking.rs:72
15 core::panicking::assert_failed_inner(…) • library/core/src/panicking.rs:402
16 core::panicking::assert_failed<…>(…) • /b/s/w/ir/x/w/fuchsia-third_party-rust/library/core/src/panicking.rs:357
17 archivist_lib_lib_test::archivist::tests::can_log_and_retrive_log::test_entry_point::λ(…) • archivist.rs:544
18 core::future::future::«impl»::poll<…>(…) • future/future.rs:123
19 fuchsia_async::test_support::«impl»::run_singlethreaded::λ::λ(…) • test_support.rs:26
20 fuchsia_async::test_support::«impl»::run_singlethreaded::λ::λ(…) • test_support.rs:121
21 fuchsia_async::atomic_future::«impl»::poll<…>(…) • atomic_future.rs:78
22 fuchsia_async::atomic_future::AtomicFuture::try_poll(…) • atomic_future.rs:223
23 fuchsia_async::runtime::fuchsia::executor::common::Inner::try_poll(…) • executor/common.rs:588
24 fuchsia_async::runtime::fuchsia::executor::common::Inner::poll_ready_tasks(…) • executor/common.rs:148
25 fuchsia_async::runtime::fuchsia::executor::common::Inner::worker_lifecycle<…>(…) • executor/common.rs:448
26 fuchsia_async::runtime::fuchsia::executor::local::LocalExecutor::run<…>(…) • executor/local.rs:100
27 fuchsia_async::runtime::fuchsia::executor::local::LocalExecutor::run_singlethreaded<…>(…) • executor/local.rs:68
28 fuchsia_async::test_support::«impl»::run_singlethreaded::λ() • test_support.rs:119
29 fuchsia_async::test_support::Config::in_parallel(…) • test_support.rs:214
30 fuchsia_async::test_support::«impl»::run_singlethreaded(…) • test_support.rs:116
31 fuchsia_async::test_support::run_singlethreaded_test<…>(…) • test_support.rs:226
32 fuchsia::test_singlethreaded<…>(…) • fuchsia/src/lib.rs:188
33 archivist_lib_lib_test::archivist::tests::can_log_and_retrive_log() • archivist.rs:519
34 archivist_lib_lib_test::archivist::tests::can_log_and_retrive_log::λ(…) • archivist.rs:520
35 core::ops::function::FnOnce::call_once<…>(…) • /b/s/w/ir/x/w/fuchsia-third_party-rust/library/core/src/ops/function.rs:250
36 core::ops::function::FnOnce::call_once<…>(…) • library/core/src/ops/function.rs:250 (inline)
37 test::__rust_begin_short_backtrace<…>(…) • library/test/src/lib.rs:621
38 test::run_test_in_spawned_subprocess(…) • library/test/src/lib.rs:749
39 test::test_main_static_abort(…) • library/test/src/lib.rs:197
40 archivist_lib_lib_test::main() • archivist/src/lib.rs:1
41…58 «Rust startup» (-r expands)
[zxdb]
我们可以使用 frame
名词作为 list
命令的前缀来查看测试的源代码,例如:
[zxdb] frame 17 list
539 expected.sort();
540
541 let mut actual = vec![recv_logs.next().await.unwrap(), recv_logs.next().await.unwrap()];
542 actual.sort();
543
▶ 544 assert_eq!(expected, actual);
545
546 // can log after killing log sink proxy
547 log_helper.kill_log_sink();
548 log_helper.write_log("my msg1");
549 log_helper.write_log("my msg2");
550
551 assert_eq!(
552 expected,
553 vec! {recv_logs.next().await.unwrap(),recv_logs.next().await.unwrap()}
554 );
这样很不便,我们必须在每个命令之前输入 frame 17
,才能与我们感兴趣的代码部分进行交互。请注意 frame
输出中的“▶”。它指向帧 0,表明它是“活动”帧。让我们选择帧作为“活动”帧,其中仅包含 frame
名词以及帧的上述索引,以便我们可以直接处理要查看的内容:
[zxdb] frame 17
archivist_lib_lib_test::archivist::tests::can_log_and_retrive_log::test_entry_point::λ(…) • archivist.rs:528
[zxdb] frame
0…12 «Rust library» (-r expands)
13 std::panicking::begin_panic_handler(…) • library/std/src/panicking.rs:645
14 core::panicking::panic_fmt(…) • library/core/src/panicking.rs:72
15 core::panicking::assert_failed_inner(…) • library/core/src/panicking.rs:402
16 core::panicking::assert_failed<…>(…) • /b/s/w/ir/x/w/fuchsia-third_party-rust/library/core/src/panicking.rs:357
▶ 17 archivist_lib_lib_test::archivist::tests::can_log_and_retrive_log::test_entry_point::λ(…) • archivist.rs:544
18 core::future::future::«impl»::poll<…>(…) • future/future.rs:123
19 fuchsia_async::test_support::«impl»::run_singlethreaded::λ::λ(…) • test_support.rs:26
20 fuchsia_async::test_support::«impl»::run_singlethreaded::λ::λ(…) • test_support.rs:121
...
现在,我们运行的所有命令都将在 17 帧的上下文中。我们再次列出源代码,并提供一些额外的背景信息:
[zxdb] list -c 10
534 let mut log_helper2 = LogSinkHelper::new(&directory);
535 log_helper2.write_log("my msg1");
536 log_helper.write_log("my msg2");
537
538 let mut expected = vec!["my msg1".to_owned(), "my msg3".to_owned()];
539 expected.sort();
540
541 let mut actual = vec![recv_logs.next().await.unwrap(), recv_logs.next().await.unwrap()];
542 actual.sort();
543
▶ 544 assert_eq!(expected, actual);
545
546 // can log after killing log sink proxy
547 log_helper.kill_log_sink();
548 log_helper.write_log("my msg1");
549 log_helper.write_log("my msg2");
550
551 assert_eq!(
552 expected,
553 vec! {recv_logs.next().await.unwrap(),recv_logs.next().await.unwrap()}
554 );
太好了!现在,测试为什么失败了?我们输出一些变量,看看会发生什么。此框架中有一个局部变量 actual
,它应该包含一些字符串,我们通过对 log_helper
和 log_helper2
实例调用 write_log
,并使用 mpsc 通道 recv_logs
接收这些字符串:
[zxdb] print expected
vec!["my msg1", "my msg3"]
[zxdb] print actual
vec!["my msg1", "my msg2"]
啊哈,我们的测试预期有点错误。我们预期 "my msg3"
为第二个字符串,但实际上记录的是 "my msg2"
。我们可以更正测试,预期会出现 "my msg2"
。现在,我们可以从测试中分离出来,继续完成测试套件:
[zxdb] quit
<...fx test output continues...>
Failed tests: archivist::tests::can_log_and_retrive_log
122 out of 123 tests passed...
Test fuchsia-pkg://fuchsia.com/archivist-tests?hash=8bcb30a2bfb923a4b42d1f0ea590af613ab0b1aa1ac67ada56ae4d325f3330a0#meta/archivist-unittests.cm produced unexpected high-severity logs:
----------------xxxxx----------------
[105255.347070][5853309][5853311][<root>][can_log_and_retrive_log] ERROR: [src/lib/diagnostics/log/rust/src/lib.rs(62)] PANIC info=panicked at ../../src/diagnostics/archivist/src/archivist.rs:544:9:
assertion `left == right` failed
left: ["my msg1", "my msg2"]
right: ["my msg1", "my msg3"]
----------------xxxxx----------------
Failing this test. See: https://fuchsia.dev/fuchsia-src/development/diagnostics/test_and_logs#restricting_log_severity
fuchsia-pkg://fuchsia.com/archivist-tests?hash=8bcb30a2bfb923a4b42d1f0ea590af613ab0b1aa1ac67ada56ae4d325f3330a0#meta/archivist-unittests.cm completed with result: FAILED
The test was executed in the hermetic realm. If your test depends on system capabilities, pass in correct realm. See https://fuchsia.dev/go/components/non-hermetic-tests
Tests failed.
Deleting 1 files at /tmp/tmpgr0otc3w: ffx_logs/ffx.log
To keep these files, set --ffx-output-directory.
现在,我们可以修正测试:
- let mut expected = vec!["my msg1".to_owned(), "my msg3".to_owned()];
+ let mut expected = vec!["my msg1".to_owned(), "my msg2".to_owned()];
然后再次运行测试:
$ fx test --break-on-failure archivist-unittests
<...fx test startup...>
Running 1 tests
Starting: fuchsia-pkg://fuchsia.com/archivist-tests#meta/archivist-unittests.cm
Command: fx ffx test run --max-severity-logs WARN --break-on-failure fuchsia-pkg://fuchsia.com/archivist-tests?hash=454897cb1be6b88c2aeb4b5abf474894b629d30ca50f7dfaa23497fd3848a566#meta/archivist-unittests.cm
Running test 'fuchsia-pkg://fuchsia.com/archivist-tests?hash=454897cb1be6b88c2aeb4b5abf474894b629d30ca50f7dfaa23497fd3848a566#meta/archivist-unittests.cm'
[RUNNING] accessor::tests::accessor_skips_invalid_selectors
[RUNNING] accessor::tests::batch_iterator_on_ready_is_called
[RUNNING] accessor::tests::batch_iterator_terminates_on_client_disconnect
[RUNNING] accessor::tests::buffered_iterator_handles_peer_closed
[RUNNING] accessor::tests::buffered_iterator_handles_two_consecutive_buffer_waits
[RUNNING] accessor::tests::logs_only_accept_basic_component_selectors
[RUNNING] accessor::tests::socket_writer_does_not_handle_cbor
[RUNNING] accessor::tests::socket_writer_handles_closed_socket
[RUNNING] accessor::tests::socket_writer_handles_text
[RUNNING] archivist::tests::can_log_and_retrive_log
[PASSED] accessor::tests::socket_writer_handles_text
[RUNNING] archivist::tests::log_from_multiple_sock
[PASSED] accessor::tests::buffered_iterator_handles_two_consecutive_buffer_waits
<...lots of tests...>
[RUNNING] logs::tests::unfiltered_stats
[PASSED] logs::tests::test_debuglog_drainer
[RUNNING] utils::tests::drop_test
[PASSED] logs::tests::test_filter_by_pid
[PASSED] logs::tests::test_filter_by_min_severity
[PASSED] logs::tests::test_filter_by_tags
[PASSED] logs::tests::test_filter_by_tid
[PASSED] logs::tests::test_log_manager_dump
[PASSED] logs::tests::test_log_manager_simple
[PASSED] logs::tests::unfiltered_stats
[PASSED] logs::tests::test_structured_log
[PASSED] logs::tests::attributed_inspect_two_mixed_streams_different_identities
[PASSED] utils::tests::drop_test
123 out of 123 tests passed...
fuchsia-pkg://fuchsia.com/archivist-tests?hash=454897cb1be6b88c2aeb4b5abf474894b629d30ca50f7dfaa23497fd3848a566#meta/archivist-unittests.cm completed with result: PASSED
Deleting 1 files at /tmp/tmpho9yjjz9: ffx_logs/ffx.log
To keep these files, set --ffx-output-directory.
Status: [duration: 36.4s] [tests: PASS: 1 FAIL: 0 SKIP: 0]
Running 1 tests [====================================================================================================================] 100.0%
C++
我们捕获了测试失败情况,可以使用 gTest 选项在测试失败的路径中插入软件断点,而该路径已内嵌在我们的测试中。我们可以通过 list
查看当前帧的代码,例如:
[zxdb] list
...
5381 (defined(__x86_64__) || defined(__i386__)))
5382 // with clang/gcc we can achieve the same effect on x86 by invoking int3
▶ 5383 asm("int3");
5384 #elif GTEST_HAS_BUILTIN(__builtin_trap)
5385 __builtin_trap();
...
但这不是我们感兴趣的代码。当我们查看堆栈轨迹时,看到测试中的代码位于第 2 帧中:
[zxdb] frame
▶ 0 testing::UnitTest::AddTestPartResult(…) • gtest.cc:5383
1 testing::internal::AssertHelper::operator=(…) • gtest.cc:476
2 debug_agent::DebugAgentTests_OnGlobalStatus_Test::TestBody(…) • debug_agent_unittest.cc:105 <-- This is the test's source code.
3 testing::internal::HandleSehExceptionsInMethodIfSupported<…>(…) • gtest.cc:2635
4 testing::internal::HandleExceptionsInMethodIfSupported<…>(…) • gtest.cc:2690
5 testing::Test::Run(…) • gtest.cc:2710
6 testing::TestInfo::Run(…) • gtest.cc:2859
7 testing::TestSuite::Run(…) • gtest.cc:3038
8 testing::internal::UnitTestImpl::RunAllTests(…) • gtest.cc:5942
9 testing::internal::HandleSehExceptionsInMethodIfSupported<…>(…) • gtest.cc:2635
10 testing::internal::HandleExceptionsInMethodIfSupported<…>(…) • gtest.cc:2690
11 testing::UnitTest::Run(…) • gtest.cc:5506
12 RUN_ALL_TESTS() • gtest.h:2318
13 main(…) • run_all_unittests.cc:20
14…17 «libc startup» (-r expands)
[zxdb]
我们可以使用 frame
名词并将索引作为 list
命令的前缀来查看测试的源代码,例如:
[zxdb] frame 2 list
100 harness.debug_agent()->InjectProcessForTest(std::move(process2));
101
102 reply = {};
103 remote_api->OnStatus(request, &reply);
104
▶ 105 ASSERT_EQ(reply.processes.size(), 3u); <-- This assertion failed.
106 EXPECT_EQ(reply.processes[0].process_koid, kProcessKoid1);
107 EXPECT_EQ(reply.processes[0].process_name, kProcessName1);
108 ASSERT_EQ(reply.processes[0].threads.size(), 1u);
109 EXPECT_EQ(reply.processes[0].threads[0].id.process, kProcessKoid1);
110 EXPECT_EQ(reply.processes[0].threads[0].id.thread, kProcess1ThreadKoid1);
111
112 EXPECT_EQ(reply.processes[1].process_koid, kProcessKoid2);
113 EXPECT_EQ(reply.processes[1].process_name, kProcessName2);
114 ASSERT_EQ(reply.processes[1].threads.size(), 2u);
115 EXPECT_EQ(reply.processes[1].threads[0].id.process, kProcessKoid2);
这样很不便,我们必须在每个命令之前输入 frame 2
,才能与所需的代码部分进行交互。请注意 frame
输出中的“▶”。它指向帧 0,表明这是“活动”帧。让我们选择帧作为“活动”帧,其中仅包含 frame
名词和帧在上面的索引,这样我们就可以直接处理想要查看的内容:
[zxdb] frame 2
debug_agent::DebugAgentTests_OnGlobalStatus_Test::TestBody(…) • debug_agent_unittest.cc:105
[zxdb] frame
0 testing::UnitTest::AddTestPartResult(…) • gtest.cc:5383
1 testing::internal::AssertHelper::operator=(…) • gtest.cc:476
▶ 2 debug_agent::DebugAgentTests_OnGlobalStatus_Test::TestBody(…) • debug_agent_unittest.cc:105
3 testing::internal::HandleSehExceptionsInMethodIfSupported<…>(…) • gtest.cc:2635
4 testing::internal::HandleExceptionsInMethodIfSupported<…>(…) • gtest.cc:2690
5 testing::Test::Run(…) • gtest.cc:2710
6 testing::TestInfo::Run(…) • gtest.cc:2859
7 testing::TestSuite::Run(…) • gtest.cc:3038
8 testing::internal::UnitTestImpl::RunAllTests(…) • gtest.cc:5942
9 testing::internal::HandleSehExceptionsInMethodIfSupported<…>(…) • gtest.cc:2635
10 testing::internal::HandleExceptionsInMethodIfSupported<…>(…) • gtest.cc:2690
11 testing::UnitTest::Run(…) • gtest.cc:5506
12 RUN_ALL_TESTS() • gtest.h:2318
13 main(…) • run_all_unittests.cc:20
14…17 «libc startup» (-r expands)
现在,我们运行的所有命令都将在 2 帧的上下文中。我们再次列出源代码,以确保:
[zxdb] list
100 harness.debug_agent()->InjectProcessForTest(std::move(process2));
101
102 reply = {};
103 remote_api->OnStatus(request, &reply);
104
▶ 105 ASSERT_EQ(reply.processes.size(), 3u);
106 EXPECT_EQ(reply.processes[0].process_koid, kProcessKoid1);
107 EXPECT_EQ(reply.processes[0].process_name, kProcessName1);
108 ASSERT_EQ(reply.processes[0].threads.size(), 1u);
109 EXPECT_EQ(reply.processes[0].threads[0].id.process, kProcessKoid1);
110 EXPECT_EQ(reply.processes[0].threads[0].id.thread, kProcess1ThreadKoid1);
111
112 EXPECT_EQ(reply.processes[1].process_koid, kProcessKoid2);
113 EXPECT_EQ(reply.processes[1].process_name, kProcessName2);
114 ASSERT_EQ(reply.processes[1].threads.size(), 2u);
115 EXPECT_EQ(reply.processes[1].threads[0].id.process, kProcessKoid2);
棒极了!现在,测试为什么失败了?我们输出一些变量,看看会发生什么。此帧中有一个局部变量 reply
,它应该由对 remote_api->OnStatus
的函数调用填充:
[zxdb] print reply
{
processes = {
[0] = {
process_koid = 4660
process_name = "process-1"
components = {}
threads = {
[0] = {
id = {process = 4660, thread = 1}
name = "test thread"
state = kRunning
blocked_reason = kNotBlocked
stack_amount = kNone
frames = {}
}
}
}
[1] = {
process_koid = 22136
process_name = "process-2"
components = {}
threads = {
[0] = {
id = {process = 22136, thread = 1}
name = "test thread"
state = kRunning
blocked_reason = kNotBlocked
stack_amount = kNone
frames = {}
}
[1] = {
id = {process = 22136, thread = 2}
name = "test thread"
state = kRunning
blocked_reason = kNotBlocked
stack_amount = kNone
frames = {}
}
}
}
}
limbo = {}
breakpoints = {}
filters = {}
}
现在,reply
变量中已经填充了一些信息,预期是 processes
矢量的大小应该等于 3。我们只需输出 reply
的成员变量即可更清晰地了解相关信息。我们还可以输出该矢量的大小方法(尚未实现常规函数调用支持):
[zxdb] print reply.processes
{
[0] = {
process_koid = 4660
process_name = "process-1"
components = {}
threads = {
[0] = {
id = {process = 4660, thread = 1}
name = "test thread"
state = kRunning
blocked_reason = kNotBlocked
stack_amount = kNone
frames = {}
}
}
}
[1] = {
process_koid = 22136
process_name = "process-2"
components = {}
threads = {
[0] = {
id = {process = 22136, thread = 1}
name = "test thread"
state = kRunning
blocked_reason = kNotBlocked
stack_amount = kNone
frames = {}
}
[1] = {
id = {process = 22136, thread = 2}
name = "test thread"
state = kRunning
blocked_reason = kNotBlocked
stack_amount = kNone
frames = {}
}
}
}
}
[zxdb] print reply.processes.size()
2
啊哈,测试预期是错误的,我们在测试中只注入了 2 个模拟进程,但应该有 3 个。只需更新测试即可预期 reply.processes
矢量的大小为 2 而不是 3。我们现在可以关闭调试程序以完成测试,然后修复测试:
[zxdb] quit
<...fx test output continues...>
Failed tests: DebugAgentTests.OnGlobalStatus <-- Failed test that we debugged.
175 out of 176 attempted tests passed, 2 tests skipped...
fuchsia-pkg://fuchsia.com/debug_agent_unit_tests?hash=3f6d97801bb147034a344e3fe1bb69291a7b690b9d3d075246ddcba59397ac12#meta/debug_agent_unit_tests.cm completed with result: FAILED
Tests failed.
FAILED: fuchsia-pkg://fuchsia.com/debug_agent_unit_tests#meta/debug_agent_unit_tests.cm
现在已经找到了测试失败的来源,接下来可以修复测试:
-ASSERT_EQ(reply.processes.size(), 3u)
+ASSERT_EQ(reply.processes.size(), 2u)
然后再次运行 fx test
:
$ fx test --break-on-failure debug_agent_unit_tests
You are using the new fx test, which is currently ready for general use ✅
See details here: https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/scripts/fxtest/rewrite
To go back to the old fx test, use `fx --enable=legacy_fxtest test`, and please file a bug under b/293917801.
Default flags loaded from /usr/local/google/home/jruthe/.fxtestrc:
[]
Logging all output to: /usr/local/google/home/jruthe/upstream/fuchsia/out/workbench_eng.x64/fxtest-2024-03-25T15:56:31.874893.log.json.gz
Use the `--logpath` argument to specify a log location or `--no-log` to disable
🛑 Debugger integration is currently experimental, follow https://fxbug.dev/319320287 for updates 🛑
To show all output, specify the `-o/--output` flag.
Found 913 total tests in //out/workbench_eng.x64/tests.json
Plan to run 1 test
Refreshing 1 target
> fx build src/developer/debug/debug_agent:debug_agent_unit_tests host_x64/debug_agent_unit_tests
Use --no-build to skip building
Executing build. Status output suspended.
ninja: Entering directory `/usr/local/google/home/jruthe/upstream/fuchsia/out/workbench_eng.x64'
[22/22](0) STAMP obj/src/developer/debug/debug_agent/debug_agent_unit_tests.stamp
Running 1 test
Starting: fuchsia-pkg://fuchsia.com/debug_agent_unit_tests#meta/debug_agent_unit_tests.cm (NOT HERMETIC)
Command: fx ffx test run --realm /core/testing:system-tests --max-severity-logs WARN --break-on-failure fuchsia-pkg://fuchsia.com/debug_agent_unit_tests?hash=399ff8d9871a6f0d53557c3d7c233cad645061016d44a7855dcea2c7b8af8101#meta/debug_agent_unit_tests.cm
Deleting 1 files at /tmp/tmp8m56ht95: ffx_logs/ffx.log
To keep these files, set --ffx-output-directory.
PASSED: fuchsia-pkg://fuchsia.com/debug_agent_unit_tests#meta/debug_agent_unit_tests.cm
Status: [duration: 16.9s] [tests: PASS: 1 FAIL: 0 SKIP: 0]
Running 1 tests [=====================================================================================================] 100.0%
调试程序不再显示,因为我们没有任何其他测试失败情况!哇哦 \o/