// Copyright 2014 The Crashpad Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "minidump/minidump_system_info_writer.h"

#include <string.h>

#include <algorithm>
#include <string>
#include <utility>

#include "gtest/gtest.h"
#include "minidump/minidump_file_writer.h"
#include "minidump/test/minidump_file_writer_test_util.h"
#include "minidump/test/minidump_string_writer_test_util.h"
#include "minidump/test/minidump_writable_test_util.h"
#include "snapshot/test/test_system_snapshot.h"
#include "test/gtest_death.h"
#include "util/file/string_file.h"

namespace crashpad {
namespace test {
namespace {

void GetSystemInfoStream(const std::string& file_contents,
                         size_t csd_version_length,
                         const MINIDUMP_SYSTEM_INFO** system_info,
                         const MINIDUMP_STRING** csd_version) {
  // The expected number of bytes for the CSD version’s MINIDUMP_STRING::Buffer.
  [[maybe_unused]] MINIDUMP_STRING* tmp;
  const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp->Buffer[0]);
  const size_t kCSDVersionBytesWithNUL =
      kCSDVersionBytes + sizeof(tmp->Buffer[0]);

  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
  constexpr size_t kSystemInfoStreamOffset =
      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
  constexpr size_t kCSDVersionOffset =
      kSystemInfoStreamOffset + sizeof(MINIDUMP_SYSTEM_INFO);
  const size_t kFileSize =
      kCSDVersionOffset + sizeof(MINIDUMP_STRING) + kCSDVersionBytesWithNUL;

  ASSERT_EQ(file_contents.size(), kFileSize);

  const MINIDUMP_DIRECTORY* directory;
  const MINIDUMP_HEADER* header =
      MinidumpHeaderAtStart(file_contents, &directory);
  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
  ASSERT_TRUE(directory);

  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo);
  EXPECT_EQ(directory[0].Location.Rva, kSystemInfoStreamOffset);

  *system_info = MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>(
      file_contents, directory[0].Location);
  ASSERT_TRUE(system_info);

  EXPECT_EQ((*system_info)->CSDVersionRva, kCSDVersionOffset);

  *csd_version =
      MinidumpStringAtRVA(file_contents, (*system_info)->CSDVersionRva);
  EXPECT_EQ((*csd_version)->Length, kCSDVersionBytes);
}

TEST(MinidumpSystemInfoWriter, Empty) {
  MinidumpFileWriter minidump_file_writer;
  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();

  system_info_writer->SetCSDVersion(std::string());

  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));

  StringFile string_file;
  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));

  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
  const MINIDUMP_STRING* csd_version = nullptr;

  ASSERT_NO_FATAL_FAILURE(
      GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version));

  EXPECT_EQ(system_info->ProcessorArchitecture,
            kMinidumpCPUArchitectureUnknown);
  EXPECT_EQ(system_info->ProcessorLevel, 0u);
  EXPECT_EQ(system_info->ProcessorRevision, 0u);
  EXPECT_EQ(system_info->NumberOfProcessors, 0u);
  EXPECT_EQ(system_info->ProductType, 0u);
  EXPECT_EQ(system_info->MajorVersion, 0u);
  EXPECT_EQ(system_info->MinorVersion, 0u);
  EXPECT_EQ(system_info->BuildNumber, 0u);
  EXPECT_EQ(system_info->PlatformId, 0u);
  EXPECT_EQ(system_info->SuiteMask, 0u);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], 0u);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], 0u);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], 0u);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, 0u);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation, 0u);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures, 0u);

  CPU_INFORMATION other_cpu_info;
  memcpy(&other_cpu_info, &system_info->Cpu, sizeof(other_cpu_info));
  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[0], 0u);
  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1], 0u);

  EXPECT_EQ(csd_version->Buffer[0], '\0');
}

TEST(MinidumpSystemInfoWriter, X86_Win) {
  MinidumpFileWriter minidump_file_writer;
  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();

  constexpr MinidumpCPUArchitecture kCPUArchitecture =
      kMinidumpCPUArchitectureX86;
  constexpr uint16_t kCPULevel = 0x0010;
  constexpr uint16_t kCPURevision = 0x0602;
  constexpr uint8_t kCPUCount = 1;
  constexpr MinidumpOS kOS = kMinidumpOSWin32NT;
  constexpr MinidumpOSType kOSType = kMinidumpOSTypeWorkstation;
  constexpr uint32_t kOSVersionMajor = 6;
  constexpr uint32_t kOSVersionMinor = 1;
  constexpr uint32_t kOSVersionBuild = 7601;
  static constexpr char kCSDVersion[] = "Service Pack 1";
  constexpr uint16_t kSuiteMask = VER_SUITE_SINGLEUSERTS;
  static constexpr char kCPUVendor[] = "AuthenticAMD";
  constexpr uint32_t kCPUVersion = 0x00100f62;
  constexpr uint32_t kCPUFeatures = 0x078bfbff;
  constexpr uint32_t kAMDFeatures = 0xefd3fbff;

  uint32_t cpu_vendor_registers[3];
  ASSERT_EQ(strlen(kCPUVendor), sizeof(cpu_vendor_registers));
  memcpy(cpu_vendor_registers, kCPUVendor, sizeof(cpu_vendor_registers));

  system_info_writer->SetCPUArchitecture(kCPUArchitecture);
  system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision);
  system_info_writer->SetCPUCount(kCPUCount);
  system_info_writer->SetOS(kOS);
  system_info_writer->SetOSType(kMinidumpOSTypeWorkstation);
  system_info_writer->SetOSVersion(
      kOSVersionMajor, kOSVersionMinor, kOSVersionBuild);
  system_info_writer->SetCSDVersion(kCSDVersion);
  system_info_writer->SetSuiteMask(kSuiteMask);
  system_info_writer->SetCPUX86VendorString(kCPUVendor);
  system_info_writer->SetCPUX86VersionAndFeatures(kCPUVersion, kCPUFeatures);
  system_info_writer->SetCPUX86AMDExtendedFeatures(kAMDFeatures);

  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));

  StringFile string_file;
  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));

  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
  const MINIDUMP_STRING* csd_version = nullptr;

  ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(
      string_file.string(), strlen(kCSDVersion), &system_info, &csd_version));

  EXPECT_EQ(system_info->ProcessorArchitecture, kCPUArchitecture);
  EXPECT_EQ(system_info->ProcessorLevel, kCPULevel);
  EXPECT_EQ(system_info->ProcessorRevision, kCPURevision);
  EXPECT_EQ(system_info->NumberOfProcessors, kCPUCount);
  EXPECT_EQ(system_info->ProductType, kOSType);
  EXPECT_EQ(system_info->MajorVersion, kOSVersionMajor);
  EXPECT_EQ(system_info->MinorVersion, kOSVersionMinor);
  EXPECT_EQ(system_info->BuildNumber, kOSVersionBuild);
  EXPECT_EQ(system_info->PlatformId, kOS);
  EXPECT_EQ(system_info->SuiteMask, kSuiteMask);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], cpu_vendor_registers[0]);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], cpu_vendor_registers[1]);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], cpu_vendor_registers[2]);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, kCPUVersion);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation, kCPUFeatures);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures, kAMDFeatures);

  for (size_t index = 0; index < strlen(kCSDVersion); ++index) {
    EXPECT_EQ(csd_version->Buffer[index], kCSDVersion[index]) << index;
  }
}

TEST(MinidumpSystemInfoWriter, AMD64_Mac) {
  MinidumpFileWriter minidump_file_writer;
  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();

  constexpr MinidumpCPUArchitecture kCPUArchitecture =
      kMinidumpCPUArchitectureAMD64;
  constexpr uint16_t kCPULevel = 0x0006;
  constexpr uint16_t kCPURevision = 0x3a09;
  constexpr uint8_t kCPUCount = 8;
  constexpr MinidumpOS kOS = kMinidumpOSMacOSX;
  constexpr MinidumpOSType kOSType = kMinidumpOSTypeWorkstation;
  constexpr uint32_t kOSVersionMajor = 10;
  constexpr uint32_t kOSVersionMinor = 9;
  constexpr uint32_t kOSVersionBuild = 4;
  static constexpr char kCSDVersion[] = "13E28";
  static constexpr uint64_t kCPUFeatures[2] = {0x10427f4c, 0x00000000};

  system_info_writer->SetCPUArchitecture(kCPUArchitecture);
  system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision);
  system_info_writer->SetCPUCount(kCPUCount);
  system_info_writer->SetOS(kOS);
  system_info_writer->SetOSType(kMinidumpOSTypeWorkstation);
  system_info_writer->SetOSVersion(
      kOSVersionMajor, kOSVersionMinor, kOSVersionBuild);
  system_info_writer->SetCSDVersion(kCSDVersion);
  system_info_writer->SetCPUOtherFeatures(kCPUFeatures[0], kCPUFeatures[1]);

  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));

  StringFile string_file;
  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));

  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
  const MINIDUMP_STRING* csd_version;

  ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(
      string_file.string(), strlen(kCSDVersion), &system_info, &csd_version));

  EXPECT_EQ(system_info->ProcessorArchitecture, kCPUArchitecture);
  EXPECT_EQ(system_info->ProcessorLevel, kCPULevel);
  EXPECT_EQ(system_info->ProcessorRevision, kCPURevision);
  EXPECT_EQ(system_info->NumberOfProcessors, kCPUCount);
  EXPECT_EQ(system_info->ProductType, kOSType);
  EXPECT_EQ(system_info->MajorVersion, kOSVersionMajor);
  EXPECT_EQ(system_info->MinorVersion, kOSVersionMinor);
  EXPECT_EQ(system_info->BuildNumber, kOSVersionBuild);
  EXPECT_EQ(system_info->PlatformId, kOS);
  EXPECT_EQ(system_info->SuiteMask, 0u);

  CPU_INFORMATION other_cpu_info;
  memcpy(&other_cpu_info, &system_info->Cpu, sizeof(other_cpu_info));
  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[0], kCPUFeatures[0]);
  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1], kCPUFeatures[1]);
}

TEST(MinidumpSystemInfoWriter, X86_CPUVendorFromRegisters) {
  // MinidumpSystemInfoWriter.X86_Win already tested SetCPUX86VendorString().
  // This test exercises SetCPUX86Vendor() to set the vendor from register
  // values.
  MinidumpFileWriter minidump_file_writer;
  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();

  constexpr MinidumpCPUArchitecture kCPUArchitecture =
      kMinidumpCPUArchitectureX86;
  static constexpr uint32_t kCPUVendor[] = {'uneG', 'Ieni', 'letn'};

  system_info_writer->SetCPUArchitecture(kCPUArchitecture);
  system_info_writer->SetCPUX86Vendor(
      kCPUVendor[0], kCPUVendor[1], kCPUVendor[2]);
  system_info_writer->SetCSDVersion(std::string());

  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));

  StringFile string_file;
  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));

  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
  const MINIDUMP_STRING* csd_version;

  ASSERT_NO_FATAL_FAILURE(
      GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version));

  EXPECT_EQ(system_info->ProcessorArchitecture, kCPUArchitecture);
  EXPECT_EQ(system_info->ProcessorLevel, 0u);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], kCPUVendor[0]);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], kCPUVendor[1]);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], kCPUVendor[2]);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, 0u);
}

TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_X86) {
  MINIDUMP_SYSTEM_INFO expect_system_info = {};

  constexpr uint16_t kCPUFamily = 6;
  constexpr uint8_t kCPUModel = 70;
  constexpr uint8_t kCPUStepping = 1;

  const uint8_t kCPUBasicFamily =
      static_cast<uint8_t>(std::min(kCPUFamily, static_cast<uint16_t>(15)));
  const uint8_t kCPUExtendedFamily = kCPUFamily - kCPUBasicFamily;

  // These checks ensure that even if the constants above change, they represent
  // something that can legitimately be encoded in the form used by cpuid 1 eax.
  EXPECT_LE(kCPUFamily, 270);
  EXPECT_LE(kCPUStepping, 15);
  EXPECT_TRUE(kCPUBasicFamily == 6 || kCPUBasicFamily == 15 || kCPUModel <= 15);

  constexpr uint8_t kCPUBasicModel = kCPUModel & 0xf;
  constexpr uint8_t kCPUExtendedModel = kCPUModel >> 4;
  const uint32_t kCPUSignature =
      (kCPUExtendedFamily << 20) | (kCPUExtendedModel << 16) |
      (kCPUBasicFamily << 8) | (kCPUBasicModel << 4) | kCPUStepping;
  constexpr uint64_t kCPUX86Features = 0x7ffafbffbfebfbff;
  expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureX86;
  expect_system_info.ProcessorLevel = kCPUFamily;
  expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping;
  expect_system_info.NumberOfProcessors = 8;
  expect_system_info.ProductType = kMinidumpOSTypeServer;
  expect_system_info.MajorVersion = 10;
  expect_system_info.MinorVersion = 9;
  expect_system_info.BuildNumber = 5;
  expect_system_info.PlatformId = kMinidumpOSMacOSX;
  expect_system_info.SuiteMask = 0;
  expect_system_info.Cpu.X86CpuInfo.VendorId[0] = 'uneG';
  expect_system_info.Cpu.X86CpuInfo.VendorId[1] = 'Ieni';
  expect_system_info.Cpu.X86CpuInfo.VendorId[2] = 'letn';
  expect_system_info.Cpu.X86CpuInfo.VersionInformation = kCPUSignature;
  expect_system_info.Cpu.X86CpuInfo.FeatureInformation =
      kCPUX86Features & 0xffffffff;
  static constexpr char kCPUVendor[] = "GenuineIntel";
  static constexpr char kOSVersionBuild[] = "13F34";

  TestSystemSnapshot system_snapshot;
  system_snapshot.SetCPUArchitecture(kCPUArchitectureX86);
  system_snapshot.SetCPURevision(
      (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping);
  system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors);
  system_snapshot.SetCPUVendor(kCPUVendor);
  system_snapshot.SetCPUX86Signature(kCPUSignature);
  system_snapshot.SetCPUX86Features(kCPUX86Features);
  system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);
  system_snapshot.SetOSServer(true);
  system_snapshot.SetOSVersion(expect_system_info.MajorVersion,
                               expect_system_info.MinorVersion,
                               expect_system_info.BuildNumber,
                               kOSVersionBuild);

  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();
  system_info_writer->InitializeFromSnapshot(&system_snapshot);

  MinidumpFileWriter minidump_file_writer;
  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));

  StringFile string_file;
  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));

  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
  const MINIDUMP_STRING* csd_version = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(),
                                              strlen(kOSVersionBuild),
                                              &system_info,
                                              &csd_version));

  EXPECT_EQ(system_info->ProcessorArchitecture,
            expect_system_info.ProcessorArchitecture);
  EXPECT_EQ(system_info->ProcessorLevel, expect_system_info.ProcessorLevel);
  EXPECT_EQ(system_info->ProcessorRevision,
            expect_system_info.ProcessorRevision);
  EXPECT_EQ(system_info->NumberOfProcessors,
            expect_system_info.NumberOfProcessors);
  EXPECT_EQ(system_info->ProductType, expect_system_info.ProductType);
  EXPECT_EQ(system_info->MajorVersion, expect_system_info.MajorVersion);
  EXPECT_EQ(system_info->MinorVersion, expect_system_info.MinorVersion);
  EXPECT_EQ(system_info->BuildNumber, expect_system_info.BuildNumber);
  EXPECT_EQ(system_info->PlatformId, expect_system_info.PlatformId);
  EXPECT_EQ(system_info->SuiteMask, expect_system_info.SuiteMask);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0],
            expect_system_info.Cpu.X86CpuInfo.VendorId[0]);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1],
            expect_system_info.Cpu.X86CpuInfo.VendorId[1]);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2],
            expect_system_info.Cpu.X86CpuInfo.VendorId[2]);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation,
            expect_system_info.Cpu.X86CpuInfo.VersionInformation);
  EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation,
            expect_system_info.Cpu.X86CpuInfo.FeatureInformation);

  for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) {
    EXPECT_EQ(csd_version->Buffer[index], kOSVersionBuild[index]) << index;
  }
}

TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_AMD64) {
  MINIDUMP_SYSTEM_INFO expect_system_info = {};

  constexpr uint8_t kCPUFamily = 6;
  constexpr uint8_t kCPUModel = 70;
  constexpr uint8_t kCPUStepping = 1;
  expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureAMD64;
  expect_system_info.ProcessorLevel = kCPUFamily;
  expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping;
  expect_system_info.NumberOfProcessors = 8;
  expect_system_info.ProductType = kMinidumpOSTypeServer;
  expect_system_info.MajorVersion = 10;
  expect_system_info.MinorVersion = 9;
  expect_system_info.BuildNumber = 5;
  expect_system_info.PlatformId = kMinidumpOSMacOSX;
  expect_system_info.SuiteMask = 0;
  expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0] =
      (1 << PF_COMPARE_EXCHANGE_DOUBLE) |
      (1 << PF_MMX_INSTRUCTIONS_AVAILABLE) |
      (1 << PF_XMMI_INSTRUCTIONS_AVAILABLE) |
      (1 << PF_RDTSC_INSTRUCTION_AVAILABLE) |
      (1 << PF_PAE_ENABLED) |
      (1 << PF_XMMI64_INSTRUCTIONS_AVAILABLE) |
      (1 << PF_SSE_DAZ_MODE_AVAILABLE) |
      (1 << PF_NX_ENABLED) |
      (1 << PF_SSE3_INSTRUCTIONS_AVAILABLE) |
      (1 << PF_COMPARE_EXCHANGE128) |
      (1 << PF_XSAVE_ENABLED) |
      (1 << PF_RDWRFSGSBASE_AVAILABLE) |
      (1 << PF_RDRAND_INSTRUCTION_AVAILABLE) |
      (UINT64_C(1) << PF_RDTSCP_INSTRUCTION_AVAILABLE);
  expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
  static constexpr char kOSVersionBuild[] = "13F34";

  TestSystemSnapshot system_snapshot;
  system_snapshot.SetCPUArchitecture(kCPUArchitectureX86_64);
  system_snapshot.SetCPURevision(
      (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping);
  system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors);
  system_snapshot.SetCPUX86Features(0x7ffafbffbfebfbff);
  system_snapshot.SetCPUX86ExtendedFeatures(0x000000212c100900);
  system_snapshot.SetCPUX86Leaf7Features(0x00002fbb);
  system_snapshot.SetCPUX86SupportsDAZ(true);
  system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);
  system_snapshot.SetOSServer(true);
  system_snapshot.SetOSVersion(expect_system_info.MajorVersion,
                               expect_system_info.MinorVersion,
                               expect_system_info.BuildNumber,
                               kOSVersionBuild);
  system_snapshot.SetNXEnabled(true);

  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();
  system_info_writer->InitializeFromSnapshot(&system_snapshot);

  MinidumpFileWriter minidump_file_writer;
  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));

  StringFile string_file;
  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));

  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;
  const MINIDUMP_STRING* csd_version = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(),
                                              strlen(kOSVersionBuild),
                                              &system_info,
                                              &csd_version));

  EXPECT_EQ(system_info->ProcessorArchitecture,
            expect_system_info.ProcessorArchitecture);
  EXPECT_EQ(system_info->ProcessorLevel, expect_system_info.ProcessorLevel);
  EXPECT_EQ(system_info->ProcessorRevision,
            expect_system_info.ProcessorRevision);
  EXPECT_EQ(system_info->NumberOfProcessors,
            expect_system_info.NumberOfProcessors);
  EXPECT_EQ(system_info->ProductType, expect_system_info.ProductType);
  EXPECT_EQ(system_info->MajorVersion, expect_system_info.MajorVersion);
  EXPECT_EQ(system_info->MinorVersion, expect_system_info.MinorVersion);
  EXPECT_EQ(system_info->BuildNumber, expect_system_info.BuildNumber);
  EXPECT_EQ(system_info->PlatformId, expect_system_info.PlatformId);
  EXPECT_EQ(system_info->SuiteMask, expect_system_info.SuiteMask);

  CPU_INFORMATION other_cpu_info;
  memcpy(&other_cpu_info, &system_info->Cpu, sizeof(other_cpu_info));
  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[0],
            expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0]);
  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1],
            expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1]);

  for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) {
    EXPECT_EQ(csd_version->Buffer[index], kOSVersionBuild[index]) << index;
  }
}

TEST(MinidumpSystemInfoWriterDeathTest, NoCSDVersion) {
  MinidumpFileWriter minidump_file_writer;
  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();
  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));

  StringFile string_file;
  ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),
                     "csd_version_");
}

}  // namespace
}  // namespace test
}  // namespace crashpad
