| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/forensics/feedback/last_reboot.h" |
| |
| #include <fuchsia/feedback/cpp/fidl.h> |
| #include <fuchsia/hardware/power/statecontrol/cpp/fidl.h> |
| #include <lib/inspect/cpp/vmo/types.h> |
| |
| #include <memory> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "src/developer/forensics/feedback/reboot_log/reboot_log.h" |
| #include "src/developer/forensics/last_reboot/reporter.h" |
| #include "src/developer/forensics/testing/stubs/cobalt_logger.h" |
| #include "src/developer/forensics/testing/stubs/cobalt_logger_factory.h" |
| #include "src/developer/forensics/testing/stubs/crash_reporter.h" |
| #include "src/developer/forensics/testing/stubs/reboot_methods_watcher_register.h" |
| #include "src/developer/forensics/testing/unit_test_fixture.h" |
| #include "src/developer/forensics/utils/cobalt/logger.h" |
| #include "src/lib/files/path.h" |
| #include "src/lib/timekeeper/async_test_clock.h" |
| |
| namespace forensics::feedback { |
| namespace { |
| |
| using ::testing::IsEmpty; |
| using ::testing::UnorderedElementsAreArray; |
| |
| class LastRebootTest : public UnitTestFixture { |
| public: |
| LastRebootTest() |
| : clock_(dispatcher()), |
| cobalt_(dispatcher(), services(), &clock_), |
| reboot_watcher_register_server_( |
| std::make_unique<stubs::RebootMethodsWatcherRegisterHangs>()) { |
| SetUpCobaltServer(std::make_unique<stubs::CobaltLoggerFactory>()); |
| InjectServiceProvider(reboot_watcher_register_server_.get()); |
| } |
| |
| void TearDown() override { |
| files::DeletePath("/tmp/has_reported_on_reboot_log.txt", /*recursive=*/false); |
| } |
| |
| protected: |
| void SetUpCrashReporterServer(std::unique_ptr<stubs::CrashReporterBase> crash_reporter_server) { |
| crash_reporter_server_ = std::move(crash_reporter_server); |
| } |
| |
| cobalt::Logger* Cobalt() { return &cobalt_; } |
| RedactorBase* Redactor() { return &redactor_; } |
| |
| fuchsia::feedback::CrashReporter* CrashReporter() { return crash_reporter_server_.get(); } |
| |
| stubs::RebootMethodsWatcherRegisterBase* RebootWatcherRegisterServer() { |
| return reboot_watcher_register_server_.get(); |
| } |
| |
| private: |
| timekeeper::AsyncTestClock clock_; |
| cobalt::Logger cobalt_; |
| IdentityRedactor redactor_{inspect::BoolProperty()}; |
| |
| std::unique_ptr<stubs::RebootMethodsWatcherRegisterBase> reboot_watcher_register_server_; |
| std::unique_ptr<stubs::CrashReporterBase> crash_reporter_server_; |
| }; |
| |
| TEST_F(LastRebootTest, FirstInstance) { |
| const zx::duration oom_crash_reporting_delay = zx::sec(90); |
| const RebootLog reboot_log(RebootReason::kOOM, "reboot log", zx::sec(1), std::nullopt); |
| |
| SetUpCrashReporterServer( |
| std::make_unique<stubs::CrashReporter>(stubs::CrashReporter::Expectations{ |
| .crash_signature = ToCrashSignature(reboot_log.RebootReason()), |
| .reboot_log = reboot_log.RebootLogStr(), |
| .uptime = reboot_log.Uptime(), |
| .is_fatal = IsFatal(reboot_log.RebootReason()), |
| })); |
| |
| LastReboot last_reboot(dispatcher(), services(), Cobalt(), Redactor(), CrashReporter(), |
| LastReboot::Options{ |
| .is_first_instance = true, |
| .reboot_log = reboot_log, |
| .graceful_reboot_reason_write_path = "n/a", |
| .oom_crash_reporting_delay = oom_crash_reporting_delay, |
| }); |
| |
| RunLoopFor(oom_crash_reporting_delay); |
| |
| EXPECT_TRUE(RebootWatcherRegisterServer()->IsBound()); |
| EXPECT_THAT( |
| ReceivedCobaltEvents(), |
| UnorderedElementsAreArray({ |
| cobalt::Event(cobalt::LastRebootReason::kSystemOutOfMemory, zx::sec(1).to_usecs()), |
| })); |
| } |
| |
| TEST_F(LastRebootTest, IsNotFirstInstance) { |
| const zx::duration oom_crash_reporting_delay = zx::sec(90); |
| const RebootLog reboot_log(RebootReason::kOOM, "reboot log", zx::sec(1), std::nullopt); |
| |
| SetUpCrashReporterServer(std::make_unique<stubs::CrashReporterNoFileExpected>()); |
| |
| LastReboot last_reboot(dispatcher(), services(), Cobalt(), Redactor(), CrashReporter(), |
| LastReboot::Options{ |
| .is_first_instance = false, |
| .reboot_log = reboot_log, |
| .graceful_reboot_reason_write_path = "n/a", |
| .oom_crash_reporting_delay = oom_crash_reporting_delay, |
| }); |
| |
| RunLoopFor(oom_crash_reporting_delay); |
| |
| EXPECT_TRUE(RebootWatcherRegisterServer()->IsBound()); |
| EXPECT_THAT(ReceivedCobaltEvents(), IsEmpty()); |
| } |
| |
| TEST_F(LastRebootTest, ReportsOnReboot) { |
| const zx::duration oom_crash_reporting_delay = zx::sec(90); |
| const RebootLog reboot_log(RebootReason::kOOM, "reboot log", zx::sec(1), std::nullopt); |
| |
| LastReboot last_reboot(dispatcher(), services(), Cobalt(), Redactor(), CrashReporter(), |
| LastReboot::Options{ |
| .is_first_instance = false, |
| .reboot_log = reboot_log, |
| .graceful_reboot_reason_write_path = "n/a", |
| .oom_crash_reporting_delay = oom_crash_reporting_delay, |
| }); |
| |
| bool error_handler_called = false; |
| ::fidl::InterfaceRequestHandler<fuchsia::feedback::LastRebootInfoProvider> handler( |
| [&](::fidl::InterfaceRequest<fuchsia::feedback::LastRebootInfoProvider> request) { |
| last_reboot.Handle(std::move(request), [&](zx_status_t) { error_handler_called = true; }); |
| }); |
| AddHandler(std::move(handler)); |
| |
| fuchsia::feedback::LastRebootInfoProviderPtr last_reboot_info_ptr; |
| services()->Connect(last_reboot_info_ptr.NewRequest(dispatcher())); |
| |
| bool called = false; |
| last_reboot_info_ptr->Get([&](::fuchsia::feedback::LastReboot) { called = true; }); |
| |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(called); |
| |
| last_reboot_info_ptr.Unbind(); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(error_handler_called); |
| } |
| |
| } // namespace |
| } // namespace forensics::feedback |