| // Copyright 2015 The Chromium 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 <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "chromeos/dbus/fake_session_manager_client.h" |
| #include "components/arc/arc_session_runner.h" |
| #include "components/arc/arc_util.h" |
| #include "components/arc/test/fake_arc_session.h" |
| #include "mojo/public/cpp/system/message_pipe.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace arc { |
| |
| namespace { |
| |
| constexpr int kContainerStarting = |
| static_cast<int>(ArcContainerLifetimeEvent::CONTAINER_STARTING); |
| constexpr int kContainerFailedToStart = |
| static_cast<int>(ArcContainerLifetimeEvent::CONTAINER_FAILED_TO_START); |
| constexpr int kContainerCrashedEarly = |
| static_cast<int>(ArcContainerLifetimeEvent::CONTAINER_CRASHED_EARLY); |
| constexpr int kContainerCrashed = |
| static_cast<int>(ArcContainerLifetimeEvent::CONTAINER_CRASHED); |
| |
| ArcSession::UpgradeParams DefaultUpgradeParams() { |
| ArcSession::UpgradeParams params; |
| params.locale = "en-US"; |
| return params; |
| } |
| |
| class DoNothingObserver : public ArcSessionRunner::Observer { |
| public: |
| void OnSessionStopped(ArcStopReason reason, bool restarting) override { |
| // Do nothing. |
| } |
| void OnSessionRestarting() override { |
| // Do nothing. |
| } |
| }; |
| |
| } // namespace |
| |
| class ArcSessionRunnerTest : public testing::Test, |
| public ArcSessionRunner::Observer { |
| public: |
| ArcSessionRunnerTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::UI) {} |
| |
| void SetUp() override { |
| chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient( |
| std::make_unique<chromeos::FakeSessionManagerClient>()); |
| chromeos::DBusThreadManager::Initialize(); |
| |
| stop_reason_ = ArcStopReason::SHUTDOWN; |
| restarting_ = false; |
| stopped_called_ = false; |
| restarting_called_ = false; |
| |
| // We inject FakeArcSession here so we do not need task_runner. |
| arc_session_runner_ = |
| std::make_unique<ArcSessionRunner>(base::Bind(FakeArcSession::Create)); |
| arc_session_runner_->AddObserver(this); |
| } |
| |
| void TearDown() override { |
| arc_session_runner_->RemoveObserver(this); |
| arc_session_runner_.reset(); |
| |
| chromeos::DBusThreadManager::Shutdown(); |
| } |
| |
| ArcSessionRunner* arc_session_runner() { return arc_session_runner_.get(); } |
| |
| FakeArcSession* arc_session() { |
| return static_cast<FakeArcSession*>( |
| arc_session_runner_->GetArcSessionForTesting()); |
| } |
| |
| ArcStopReason stop_reason() { |
| EXPECT_TRUE(stopped_called()); |
| return stop_reason_; |
| } |
| |
| bool restarting() { |
| EXPECT_TRUE(stopped_called()); |
| return restarting_; |
| } |
| |
| bool stopped_called() { return stopped_called_; } |
| bool restarting_called() { return restarting_called_; } |
| |
| void ResetArcSessionFactory( |
| const ArcSessionRunner::ArcSessionFactory& factory) { |
| arc_session_runner_->RemoveObserver(this); |
| arc_session_runner_ = std::make_unique<ArcSessionRunner>(factory); |
| arc_session_runner_->AddObserver(this); |
| } |
| |
| static std::unique_ptr<ArcSession> CreateSuspendedArcSession() { |
| auto arc_session = std::make_unique<FakeArcSession>(); |
| arc_session->SuspendBoot(); |
| return std::move(arc_session); |
| } |
| |
| static std::unique_ptr<ArcSession> CreateBootFailureArcSession( |
| ArcStopReason reason) { |
| auto arc_session = std::make_unique<FakeArcSession>(); |
| arc_session->EnableBootFailureEmulation(reason); |
| return std::move(arc_session); |
| } |
| |
| private: |
| // ArcSessionRunner::Observer: |
| void OnSessionStopped(ArcStopReason stop_reason, bool restarting) override { |
| // The instance is already destructed in |
| // ArcSessionRunner::OnSessionStopped(). |
| stop_reason_ = stop_reason; |
| restarting_ = restarting; |
| stopped_called_ = true; |
| restarting_called_ = false; |
| } |
| void OnSessionRestarting() override { restarting_called_ = true; } |
| |
| ArcStopReason stop_reason_; |
| bool restarting_; |
| bool stopped_called_; |
| bool restarting_called_; |
| std::unique_ptr<ArcSessionRunner> arc_session_runner_; |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ArcSessionRunnerTest); |
| }; |
| |
| // Exercises the basic functionality of the ArcSessionRunner. Observer should |
| // be notified. |
| TEST_F(ArcSessionRunnerTest, Basic) { |
| class Observer : public ArcSessionRunner::Observer { |
| public: |
| Observer() = default; |
| |
| bool stopped_called() const { return stopped_called_; } |
| |
| // ArcSessionRunner::Observer: |
| void OnSessionStopped(ArcStopReason reason, bool restarting) override { |
| stopped_called_ = true; |
| } |
| void OnSessionRestarting() override {} |
| |
| private: |
| bool stopped_called_ = false; |
| |
| DISALLOW_COPY_AND_ASSIGN(Observer); |
| }; |
| |
| Observer observer; |
| arc_session_runner()->AddObserver(&observer); |
| base::ScopedClosureRunner teardown(base::BindOnce( |
| [](ArcSessionRunner* arc_session_runner, Observer* observer) { |
| arc_session_runner->RemoveObserver(observer); |
| }, |
| arc_session_runner(), &observer)); |
| |
| EXPECT_FALSE(arc_session()); |
| |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_TRUE(arc_session()->is_running()); |
| |
| arc_session_runner()->RequestStop(); |
| EXPECT_FALSE(arc_session()); |
| EXPECT_TRUE(observer.stopped_called()); |
| } |
| |
| // If the ArcSessionRunner accepts a request to stop ARC instance, it should |
| // stop it, even mid-startup. |
| TEST_F(ArcSessionRunnerTest, StopMidStartup) { |
| ResetArcSessionFactory( |
| base::Bind(&ArcSessionRunnerTest::CreateSuspendedArcSession)); |
| EXPECT_FALSE(arc_session()); |
| |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_FALSE(arc_session()->is_running()); |
| |
| arc_session_runner()->RequestStop(); |
| EXPECT_FALSE(arc_session()); |
| EXPECT_FALSE(restarting()); |
| } |
| |
| // Does the same for mini instance. |
| TEST_F(ArcSessionRunnerTest, StopMidStartup_MiniInstance) { |
| ResetArcSessionFactory( |
| base::Bind(&ArcSessionRunnerTest::CreateSuspendedArcSession)); |
| EXPECT_FALSE(arc_session()); |
| |
| arc_session_runner()->RequestStartMiniInstance(); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_FALSE(arc_session()->is_running()); |
| |
| arc_session_runner()->RequestStop(); |
| EXPECT_FALSE(arc_session()); |
| } |
| |
| // If the boot procedure is failed, then restarting mechanism should not |
| // triggered. |
| TEST_F(ArcSessionRunnerTest, BootFailure) { |
| ResetArcSessionFactory( |
| base::Bind(&ArcSessionRunnerTest::CreateBootFailureArcSession, |
| ArcStopReason::GENERIC_BOOT_FAILURE)); |
| EXPECT_FALSE(arc_session()); |
| |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| EXPECT_EQ(ArcStopReason::GENERIC_BOOT_FAILURE, stop_reason()); |
| EXPECT_FALSE(arc_session()); |
| EXPECT_FALSE(restarting()); |
| } |
| |
| // Does the same with the mini instance. |
| TEST_F(ArcSessionRunnerTest, BootFailure_MiniInstance) { |
| ResetArcSessionFactory( |
| base::Bind(&ArcSessionRunnerTest::CreateBootFailureArcSession, |
| ArcStopReason::GENERIC_BOOT_FAILURE)); |
| EXPECT_FALSE(arc_session()); |
| |
| // If starting the mini instance fails, arc_session_runner()'s state goes back |
| // to STOPPED, but its observers won't be notified. |
| arc_session_runner()->RequestStartMiniInstance(); |
| arc_session()->EmulateMiniContainerStart(); |
| EXPECT_FALSE(arc_session()); |
| EXPECT_FALSE(stopped_called()); |
| |
| // Also make sure that RequestUpgrade() works just fine after the boot |
| // failure. |
| ResetArcSessionFactory(base::Bind(FakeArcSession::Create)); |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_TRUE(arc_session()->is_running()); |
| } |
| |
| // Similary, CRASH should do same for GENERIC_BOOT_FAILURE case, because |
| // in mini instance, Mojo connection should not be established. |
| TEST_F(ArcSessionRunnerTest, Crash_MiniInstance) { |
| ResetArcSessionFactory( |
| base::Bind(&ArcSessionRunnerTest::CreateBootFailureArcSession, |
| ArcStopReason::CRASH)); |
| EXPECT_FALSE(arc_session()); |
| |
| // If starting the mini instance fails, arc_session_runner()'s state goes back |
| // to STOPPED, but its observers won't be notified. |
| arc_session_runner()->RequestStartMiniInstance(); |
| arc_session()->EmulateMiniContainerStart(); |
| EXPECT_FALSE(arc_session()); |
| EXPECT_FALSE(stopped_called()); |
| } |
| |
| // Tests that RequestUpgrade works after calling RequestStart. |
| TEST_F(ArcSessionRunnerTest, Upgrade) { |
| EXPECT_FALSE(arc_session()); |
| |
| arc_session_runner()->RequestStartMiniInstance(); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_FALSE(arc_session()->is_running()); |
| |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_TRUE(arc_session()->is_running()); |
| } |
| |
| // If the instance is stopped, it should be re-started. |
| TEST_F(ArcSessionRunnerTest, Restart) { |
| arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta()); |
| EXPECT_FALSE(arc_session()); |
| |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_TRUE(arc_session()->is_running()); |
| |
| // Simulate a connection loss. |
| ASSERT_TRUE(arc_session()); |
| arc_session()->StopWithReason(ArcStopReason::CRASH); |
| EXPECT_FALSE(arc_session()); |
| EXPECT_TRUE(restarting()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(restarting_called()); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_TRUE(arc_session()->is_running()); |
| |
| arc_session_runner()->RequestStop(); |
| EXPECT_FALSE(arc_session()); |
| } |
| |
| TEST_F(ArcSessionRunnerTest, GracefulStop) { |
| arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta()); |
| EXPECT_FALSE(arc_session()); |
| |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_TRUE(arc_session()->is_running()); |
| |
| // Graceful stop. |
| arc_session_runner()->RequestStop(); |
| EXPECT_EQ(ArcStopReason::SHUTDOWN, stop_reason()); |
| EXPECT_FALSE(restarting()); |
| EXPECT_FALSE(restarting_called()); |
| EXPECT_FALSE(arc_session()); |
| } |
| |
| TEST_F(ArcSessionRunnerTest, Shutdown) { |
| arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta()); |
| EXPECT_FALSE(arc_session()); |
| |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| ASSERT_TRUE(arc_session()); |
| EXPECT_TRUE(arc_session()->is_running()); |
| |
| // Simulate shutdown. |
| arc_session_runner()->OnShutdown(); |
| EXPECT_EQ(ArcStopReason::SHUTDOWN, stop_reason()); |
| EXPECT_FALSE(arc_session()); |
| } |
| |
| // Removing the same observer more than once should be okay. |
| TEST_F(ArcSessionRunnerTest, RemoveObserverTwice) { |
| EXPECT_FALSE(arc_session()); |
| |
| DoNothingObserver do_nothing_observer; |
| arc_session_runner()->AddObserver(&do_nothing_observer); |
| // Call RemoveObserver() twice. |
| arc_session_runner()->RemoveObserver(&do_nothing_observer); |
| arc_session_runner()->RemoveObserver(&do_nothing_observer); |
| } |
| |
| // Removing an unknown observer should be allowed. |
| TEST_F(ArcSessionRunnerTest, RemoveUnknownObserver) { |
| EXPECT_FALSE(arc_session()); |
| |
| DoNothingObserver do_nothing_observer; |
| arc_session_runner()->RemoveObserver(&do_nothing_observer); |
| } |
| |
| // Tests UMA recording on mini instance -> full instance -> shutdown case. |
| TEST_F(ArcSessionRunnerTest, UmaRecording_StartUpgradeShutdown) { |
| base::HistogramTester tester; |
| |
| arc_session_runner()->RequestStartMiniInstance(); |
| tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting, |
| 1 /* count of the sample */); |
| |
| // Boot continue should not increase the count. |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting, |
| 1); |
| |
| // "0" should be recorded as a restart count on shutdown. |
| arc_session_runner()->OnShutdown(); |
| tester.ExpectUniqueSample("Arc.ContainerRestartAfterCrashCount", |
| 0 /* sample */, 1 /* count of the sample */); |
| } |
| |
| // Tests UMA recording on full instance -> shutdown case. |
| TEST_F(ArcSessionRunnerTest, UmaRecording_StartShutdown) { |
| base::HistogramTester tester; |
| |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting, |
| 1); |
| // "0" should be recorded as a restart count on shutdown. |
| arc_session_runner()->OnShutdown(); |
| tester.ExpectUniqueSample("Arc.ContainerRestartAfterCrashCount", 0, 1); |
| } |
| |
| // Tests UMA recording on mini instance -> full instance -> crash -> shutdown |
| // case. |
| TEST_F(ArcSessionRunnerTest, UmaRecording_CrashTwice) { |
| base::HistogramTester tester; |
| |
| arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta()); |
| EXPECT_FALSE(arc_session()); |
| |
| arc_session_runner()->RequestStartMiniInstance(); |
| tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting, |
| 1); |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| |
| // Stop the instance with CRASH. |
| arc_session()->StopWithReason(ArcStopReason::CRASH); |
| tester.ExpectBucketCount("Arc.ContainerLifetimeEvent", kContainerCrashed, 1); |
| tester.ExpectTotalCount("Arc.ContainerLifetimeEvent", 2); |
| |
| // Restart the instance, then crash the instance again. The second CRASH |
| // should not affect Arc.ContainerLifetimeEvent. |
| base::RunLoop().RunUntilIdle(); |
| arc_session()->StopWithReason(ArcStopReason::CRASH); |
| tester.ExpectBucketCount("Arc.ContainerLifetimeEvent", kContainerCrashed, 1); |
| tester.ExpectTotalCount("Arc.ContainerLifetimeEvent", 2); |
| |
| // However, "2" should be recorded as a restart count on shutdown. |
| base::RunLoop().RunUntilIdle(); |
| arc_session_runner()->OnShutdown(); |
| tester.ExpectUniqueSample("Arc.ContainerRestartAfterCrashCount", 2, 1); |
| } |
| |
| // Tests UMA recording on mini instance -> crash -> shutdown case. |
| TEST_F(ArcSessionRunnerTest, UmaRecording_CrashMini) { |
| base::HistogramTester tester; |
| |
| arc_session_runner()->RequestStartMiniInstance(); |
| tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting, |
| 1); |
| |
| // Stop the instance with CRASH. |
| arc_session()->StopWithReason(ArcStopReason::CRASH); |
| tester.ExpectBucketCount("Arc.ContainerLifetimeEvent", kContainerCrashedEarly, |
| 1); |
| tester.ExpectTotalCount("Arc.ContainerLifetimeEvent", 2); |
| |
| // In this case, no restart happened. "0" should be recorded. |
| arc_session_runner()->OnShutdown(); |
| tester.ExpectUniqueSample("Arc.ContainerRestartAfterCrashCount", 0, 1); |
| } |
| |
| // Tests UMA recording on mini instance -> boot fail -> shutdown case. |
| TEST_F(ArcSessionRunnerTest, UmaRecording_BootFail) { |
| base::HistogramTester tester; |
| |
| arc_session_runner()->RequestStartMiniInstance(); |
| tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting, |
| 1); |
| |
| arc_session()->StopWithReason(ArcStopReason::GENERIC_BOOT_FAILURE); |
| tester.ExpectBucketCount("Arc.ContainerLifetimeEvent", |
| kContainerFailedToStart, 1); |
| tester.ExpectTotalCount("Arc.ContainerLifetimeEvent", 2); |
| |
| // No restart happened. "0" should be recorded. |
| arc_session_runner()->OnShutdown(); |
| tester.ExpectUniqueSample("Arc.ContainerRestartAfterCrashCount", 0, 1); |
| } |
| |
| // Tests UMA recording on full instance -> low disk -> shutdown case. |
| TEST_F(ArcSessionRunnerTest, UmaRecording_LowDisk) { |
| base::HistogramTester tester; |
| |
| arc_session_runner()->RequestUpgrade(DefaultUpgradeParams()); |
| tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting, |
| 1); |
| |
| // We don't record UMA for LOW_DISK_SPACE. |
| arc_session()->StopWithReason(ArcStopReason::LOW_DISK_SPACE); |
| tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting, |
| 1); |
| |
| // No restart happened. "0" should be recorded. |
| arc_session_runner()->OnShutdown(); |
| tester.ExpectUniqueSample("Arc.ContainerRestartAfterCrashCount", 0, 1); |
| } |
| |
| } // namespace arc |