chromium/components/stability_report/user_stream_data_source_win_unittest.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 "components/stability_report/user_stream_data_source_win.h"

#include <memory>
#include <tuple>
#include <utility>

#include "base/process/memory.h"
#include "base/process/process.h"
#include "components/stability_report/stability_report.pb.h"
#include "components/stability_report/test/stability_report_reader.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/crashpad/crashpad/snapshot/test/test_exception_snapshot.h"
#include "third_party/crashpad/crashpad/snapshot/test/test_process_snapshot.h"

namespace stability_report {

namespace {

constexpr uint64_t kExpectedAllocationAttempt = 12345;

using ::testing::Bool;
using ::testing::Combine;
using ::testing::TestWithParam;
using ::testing::Values;

enum class ExceptionCode {
  // No ExceptionSnapshot.
  kNone,
  // ExceptionSnapshot has a non-OOM code.
  kNotOOM,
  // ExceptionSnapshot with kOomExceptionCode and allocation info in Codes().
  kOOMWithAllocation,
  // ExceptionSnapshot with kOomExceptionCode but no allocation info in Codes().
  kOOMWithoutAllocation,
};

class StabilityReportUserStreamDataSourceTest
    : public TestWithParam<std::tuple<bool, ExceptionCode>> {
 protected:
  StabilityReportUserStreamDataSourceTest() {
    std::tie(is_valid_process_, exception_code_) = GetParam();
  }

  bool is_valid_process_;
  ExceptionCode exception_code_;
};

INSTANTIATE_TEST_SUITE_P(All,
                         StabilityReportUserStreamDataSourceTest,
                         Combine(Bool(),
                                 Values(ExceptionCode::kNone,
                                        ExceptionCode::kNotOOM,
                                        ExceptionCode::kOOMWithAllocation,
                                        ExceptionCode::kOOMWithoutAllocation)));

TEST_P(StabilityReportUserStreamDataSourceTest, ReadProcess) {
  crashpad::test::TestProcessSnapshot process_snapshot;
  process_snapshot.SetProcessID(is_valid_process_
                                    ? base::Process::Current().Pid()
                                    : base::kNullProcessId);
  if (exception_code_ != ExceptionCode::kNone) {
    auto exception_snapshot =
        std::make_unique<crashpad::test::TestExceptionSnapshot>();
    switch (exception_code_) {
      case ExceptionCode::kNotOOM:
        // Set an arbitrary error code.
        exception_snapshot->SetException(ERROR_INVALID_HANDLE);
        exception_snapshot->SetCodes({kExpectedAllocationAttempt});
        break;
      case ExceptionCode::kOOMWithAllocation:
        exception_snapshot->SetException(base::win::kOomExceptionCode);
        exception_snapshot->SetCodes({kExpectedAllocationAttempt});
        break;
      case ExceptionCode::kOOMWithoutAllocation:
        exception_snapshot->SetException(base::win::kOomExceptionCode);
        exception_snapshot->SetCodes({});
        break;
      case ExceptionCode::kNone:
        // Should not be reached.
        FAIL();
    }
    process_snapshot.SetException(std::move(exception_snapshot));
  }

  // Collect a StabilityReport from `process_snapshot`.
  UserStreamDataSourceWin source;
  std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource> data_source =
      source.ProduceStreamData(&process_snapshot);
  ASSERT_TRUE(data_source);

  // Read the StabilityReport back out of the stream data.
  test::StabilityReportReader reader;
  ASSERT_TRUE(data_source->ReadStreamData(&reader));

  // Validate ProcessState.
  ASSERT_EQ(reader.report().process_states_size(), 1);
  const ProcessState& process_state = reader.report().process_states(0);
  EXPECT_EQ(process_state.process_id(), process_snapshot.ProcessID());
  ASSERT_TRUE(process_state.has_memory_state());
  ASSERT_TRUE(process_state.memory_state().has_windows_memory());
  const ProcessState::MemoryState::WindowsMemory& process_memory =
      process_state.memory_state().windows_memory();
  const ProcessState::FileSystemState::WindowsFileSystemState& process_fs =
      process_state.file_system_state().windows_file_system_state();

  // Memory stats are only filled in if the process could be queried.
  if (is_valid_process_) {
    EXPECT_GT(process_memory.process_private_usage(), 0U);
    EXPECT_GT(process_memory.process_peak_workingset_size(), 0U);
    EXPECT_GT(process_memory.process_peak_pagefile_usage(), 0U);
    EXPECT_GT(process_fs.process_handle_count(), 0U);
  } else {
    EXPECT_FALSE(process_memory.has_process_private_usage());
    EXPECT_FALSE(process_memory.has_process_peak_workingset_size());
    EXPECT_FALSE(process_memory.has_process_peak_pagefile_usage());
    EXPECT_FALSE(process_fs.has_process_handle_count());
  }

  // Allocation attempt is only set on OOM when all info is available.
  if (exception_code_ == ExceptionCode::kOOMWithAllocation) {
    EXPECT_EQ(process_memory.process_allocation_attempt(),
              kExpectedAllocationAttempt);
  } else {
    EXPECT_FALSE(process_memory.has_process_allocation_attempt());
  }

  // Validate SystemMemoryState.
  ASSERT_TRUE(reader.report().has_system_memory_state());
  ASSERT_TRUE(reader.report().system_memory_state().has_windows_memory());
  const SystemMemoryState::WindowsMemory& system_memory =
      reader.report().system_memory_state().windows_memory();
  EXPECT_GT(system_memory.system_commit_limit(), 0U);
  EXPECT_GT(system_memory.system_commit_remaining(), 0U);
  EXPECT_GT(system_memory.system_handle_count(), 0U);
}

}  // namespace

}  // namespace stability_report