chromium/chrome/test/base/chromeos/crosier/upstart_test.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/base/chromeos/crosier/upstart.h"

#include "testing/gtest/include/gtest/gtest.h"

// This file contains unit tests for the "upstart" test support helper.

namespace upstart {

// For gtest comparisons.
bool operator==(const JobStatus& a, const JobStatus& b) {
  return std::tie(a.is_valid, a.goal, a.state, a.pid) ==
         std::tie(b.is_valid, b.goal, b.state, b.pid);
}

TEST(UpstartTest, ParseStatus) {
  JobStatus result = internal::ParseStatus("foo", "");
  EXPECT_FALSE(result.is_valid);

  // Normal stopped one.
  result = internal::ParseStatus("boot-splash", " boot-splash stop/waiting\n");
  JobStatus boot_splash{true, Goal::kStop, State::kWaiting, 0};
  EXPECT_EQ(result, boot_splash);

  // Job name doesn't match.
  result = internal::ParseStatus("different-one", "boot-splash stop/waiting");
  EXPECT_FALSE(result.is_valid);

  // Running with a PID.
  result =
      internal::ParseStatus("powerd", "powerd start/running, process 9398\n");
  JobStatus powerd{true, Goal::kStart, State::kRunning, 9398};
  EXPECT_EQ(result, powerd);

  // Multi-line input just gives the first one.
  result = internal::ParseStatus(
      "ureadahead",
      "ureadahead stop/pre-stop, process 227\npre-stop process 5579\n");
  JobStatus ureadahead{true, Goal::kStop, State::kPreStop, 227};
  EXPECT_EQ(result, ureadahead);

  // Instance status.
  result = internal::ParseStatus(
      "ml-service", "ml-service (mojo_service) start/running, process 6820");
  JobStatus mlservice{true, Goal::kStart, State::kRunning, 6820};
  EXPECT_EQ(result, mlservice);

  // Tmpfiles annotation.
  result = internal::ParseStatus("ui",
                                 "ui start/tmpfiles, (tmpfiles) process 19419");
  JobStatus ui{true, Goal::kStart, State::kTmpfiles, 19419};
  EXPECT_EQ(result, ui);
}

TEST(UpstartTest, GetJobStatus) {
  // Random nonexistent job.
  JobStatus result = GetJobStatus("nonexistent-job");
  EXPECT_FALSE(result.is_valid);

  // The "dbus" one should always be running.
  result = GetJobStatus("dbus");
  EXPECT_TRUE(result.is_valid);
  EXPECT_EQ(result.goal, Goal::kStart);
  EXPECT_EQ(result.state, State::kRunning);
  EXPECT_GT(result.pid, 0);
}

TEST(UpstartTest, JobExists) {
  EXPECT_FALSE(JobExists("nonexistent-job"));
  EXPECT_TRUE(JobExists("dbus"));
}

TEST(UpstartTest, WaitForJobStatus) {
  // This is hard to test without making a lot of assumptions or messing with
  // the system. Instead, we just validate some simple cases that don't change
  // anything.
  EXPECT_FALSE(WaitForJobStatus("nonexistent-job", Goal::kStart,
                                State::kRunning, WrongGoalPolicy::kReject));

  // Wait until the current state should always succeed.
  JobStatus dbus = GetJobStatus("dbus");
  EXPECT_TRUE(WaitForJobStatus("dbus", dbus.goal, dbus.state,
                               WrongGoalPolicy::kReject));

  // Wait until the opposite goal should immediately fail.
  Goal opposite_dbus_goal =
      dbus.goal == Goal::kStart ? Goal::kStop : Goal::kStart;
  EXPECT_FALSE(WaitForJobStatus("dbus", opposite_dbus_goal, State::kRunning,
                                WrongGoalPolicy::kReject));

  // Waiting for the opposite goal with "tolerate" option should timeout which
  // we determine by making sure it takes longer than our specified timeout to
  // run.
  base::TimeDelta timeout = base::Milliseconds(100);
  base::TimeTicks begin = base::TimeTicks::Now();
  EXPECT_FALSE(internal::WaitForJobStatusWithTimeout(
      "dbus", opposite_dbus_goal, State::kRunning, WrongGoalPolicy::kTolerate,
      timeout));
  base::TimeTicks end = base::TimeTicks::Now();
  EXPECT_GT(end - begin, timeout);
}

}  // namespace upstart