| // Copyright 2021 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "minios/utils.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/test/mock_log.h> |
| #include <brillo/secure_blob.h> |
| #include <brillo/udev/mock_udev.h> |
| #include <brillo/udev/mock_udev_device.h> |
| #include <brillo/udev/mock_udev_enumerate.h> |
| #include <brillo/udev/mock_udev_list_entry.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <libcrossystem/crossystem.h> |
| #include <libcrossystem/crossystem_fake.h> |
| #include <libhwsec-foundation/crypto/secure_blob_util.h> |
| #include <vpd/fake_vpd.h> |
| #include <vpd/vpd.h> |
| |
| #include "minios/log_store_manifest.h" |
| #include "minios/mock_cgpt_util.h" |
| #include "minios/mock_process_manager.h" |
| |
| namespace minios { |
| |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::ElementsAre; |
| using ::testing::HasSubstr; |
| using ::testing::NiceMock; |
| using ::testing::Optional; |
| using ::testing::Return; |
| using ::testing::SetArgPointee; |
| using ::testing::StrEq; |
| using ::testing::StrictMock; |
| |
| const brillo::SecureBlob kValidKey{"thisisa32bytestring1234567890abc"}; |
| const brillo::SecureBlob kTestData{ |
| "test data to verify encryption and decryption"}; |
| |
| class UtilTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir()); |
| file_path_ = tmp_dir_.GetPath().Append("file"); |
| content_ = "line1\nline2\n" + std::string(100, 'a') + "\nb"; |
| ASSERT_TRUE(base::WriteFile(file_path_, content_)); |
| ASSERT_TRUE( |
| CreateDirectory(tmp_dir_.GetPath().Append("sys/firmware/vpd/ro"))); |
| } |
| |
| protected: |
| base::ScopedTempDir tmp_dir_; |
| base::FilePath file_path_; |
| std::string content_; |
| }; |
| |
| TEST_F(UtilTest, ReadFileContentOffsets) { |
| auto [success, content] = ReadFileContentWithinRange( |
| file_path_, /*start_offset=*/0, /*end_offset=*/1, /*num_cols=*/1); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(content, "l\n"); |
| } |
| |
| TEST_F(UtilTest, ReadFileContentOffsets2) { |
| auto [success, content] = ReadFileContentWithinRange( |
| file_path_, /*start_offset=*/0, /*end_offset=*/1, /*num_cols=*/2); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(content, "l"); |
| } |
| |
| TEST_F(UtilTest, ReadFileContentOffsets3) { |
| auto [success, content] = ReadFileContentWithinRange( |
| file_path_, /*start_offset=*/4, /*end_offset=*/6, /*num_cols=*/1); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(content, "1\n"); |
| } |
| |
| TEST_F(UtilTest, ReadFileContentOffsets4) { |
| auto [success, content] = ReadFileContentWithinRange( |
| file_path_, /*start_offset=*/2, /*end_offset=*/7, /*num_cols=*/2); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(content, "ne\n1\nl"); |
| } |
| |
| TEST_F(UtilTest, ReadFileContentMissingFile) { |
| auto [success, content, bytes_read] = |
| ReadFileContent(base::FilePath("/a/b/foobar"), 1, 1, 1); |
| EXPECT_FALSE(success); |
| } |
| |
| TEST_F(UtilTest, ReadFileContentWrappedTextCutoff) { |
| auto [success, content, bytes_read] = ReadFileContent( |
| file_path_, /*offset=*/0, /*num_lines=*/3, /*num_cols=*/4); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(content, "line\n1\nline\n"); |
| EXPECT_LT(bytes_read, content.size()); |
| } |
| |
| TEST_F(UtilTest, ReadFileContentWrappedTextPerfectAlignmentColumns) { |
| auto [success, content, bytes_read] = ReadFileContent( |
| file_path_, /*offset=*/0, /*num_lines=*/5, /*num_cols=*/5); |
| EXPECT_TRUE(success); |
| // There should be no double newlining. |
| EXPECT_EQ(content, "line1\nline2\naaaaa\naaaaa\naaaaa\n"); |
| EXPECT_LT(bytes_read, content.size()); |
| } |
| |
| TEST_F(UtilTest, ReadFileContentWrappedTextExceedsColumnLimit) { |
| auto [success, content, bytes_read] = ReadFileContent( |
| file_path_, /*offset=*/0, /*num_lines=*/5, /*num_cols=*/6); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(content, "line1\nline2\naaaaaa\naaaaaa\naaaaaa\n"); |
| EXPECT_LT(bytes_read, content.size()); |
| } |
| |
| TEST_F(UtilTest, ReadFileContentZeroLimits) { |
| auto [success, content, bytes_read] = ReadFileContent( |
| file_path_, /*offset=*/0, /*num_lines=*/0, /*num_cols=*/0); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(content, ""); |
| EXPECT_EQ(bytes_read, 0); |
| } |
| |
| TEST_F(UtilTest, ReadFileContent) { |
| auto [success, content, bytes_read] = |
| ReadFileContent(file_path_, /*offset=*/0, /*num_lines=*/4, |
| /*num_cols=*/200); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(content, content_); |
| EXPECT_EQ(bytes_read, content_.size()); |
| } |
| |
| TEST_F(UtilTest, ReadFileContentStartOffset) { |
| auto [success, content, bytes_read] = |
| ReadFileContent(file_path_, /*offset=*/12, /*num_lines=*/3, |
| /*num_cols=*/1); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(content, "a\na\na\n"); |
| EXPECT_EQ(bytes_read, 3); |
| } |
| |
| TEST_F(UtilTest, GetKeyboardLayoutFailure) { |
| auto mock_process_manager_ = std::make_shared<MockProcessManager>(); |
| EXPECT_CALL(*mock_process_manager_, RunCommandWithOutput(_, _, _, _)) |
| .WillOnce( |
| testing::DoAll(testing::SetArgPointee<2>(""), testing::Return(true))); |
| EXPECT_EQ(GetKeyboardLayout(mock_process_manager_), "us"); |
| |
| // Badly formatted. |
| EXPECT_CALL(*mock_process_manager_, RunCommandWithOutput(_, _, _, _)) |
| .WillOnce(testing::DoAll(testing::SetArgPointee<2>("xkbeng:::"), |
| testing::Return(true))); |
| EXPECT_EQ(GetKeyboardLayout(mock_process_manager_), "us"); |
| |
| // Failed. |
| EXPECT_CALL(*mock_process_manager_, RunCommandWithOutput(_, _, _, _)) |
| .WillOnce(testing::DoAll(testing::Return(false))); |
| EXPECT_EQ(GetKeyboardLayout(mock_process_manager_), "us"); |
| } |
| |
| TEST_F(UtilTest, GetKeyboardLayout) { |
| auto mock_process_manager_ = std::make_shared<MockProcessManager>(); |
| EXPECT_CALL(*mock_process_manager_, RunCommandWithOutput(_, _, _, _)) |
| .WillOnce(testing::DoAll(testing::SetArgPointee<2>("xkb:en::eng"), |
| testing::Return(true))); |
| EXPECT_EQ(GetKeyboardLayout(mock_process_manager_), "en"); |
| } |
| |
| TEST(UtilsTest, AlertLogTagCreationTest) { |
| auto category = "test_category"; |
| auto default_component = "CoreServicesAlert"; |
| EXPECT_EQ(base::StringPrintf("[%s<%s>] ", default_component, category), |
| AlertLogTag(category)); |
| } |
| |
| TEST(UtilsTest, AlertLogTagLogTest) { |
| base::test::MockLog mock_log; |
| mock_log.StartCapturingLogs(); |
| |
| auto category = "test_category"; |
| auto test_msg = "Test Error Message: "; |
| auto test_id = 10; |
| auto expected_log = base::StringPrintf( |
| "%s%s%d", AlertLogTag(category).c_str(), test_msg, test_id); |
| |
| EXPECT_CALL(mock_log, |
| Log(::logging::LOGGING_ERROR, _, _, _, HasSubstr(expected_log))); |
| |
| LOG(ERROR) << AlertLogTag(category) << test_msg << test_id; |
| } |
| |
| TEST(UtilsTest, MountStatefulPartitionTest) { |
| auto mock_process_manager_ = |
| std::make_shared<StrictMock<MockProcessManager>>(); |
| |
| std::vector<std::string> expected_args = { |
| "/usr/bin/stateful_partition_for_recovery", "--mount"}; |
| |
| EXPECT_CALL(*mock_process_manager_, RunCommand(expected_args, _)) |
| .WillOnce(::testing::Return(0)); |
| EXPECT_TRUE(MountStatefulPartition(mock_process_manager_)); |
| |
| // Verify error results. |
| EXPECT_CALL(*mock_process_manager_, RunCommand) |
| .WillOnce(::testing::Return(1)); |
| EXPECT_FALSE(MountStatefulPartition(mock_process_manager_)); |
| EXPECT_FALSE(MountStatefulPartition(nullptr)); |
| } |
| |
| TEST(UtilsTest, UnmountPathTest) { |
| auto mock_process_manager_ = |
| std::make_shared<StrictMock<MockProcessManager>>(); |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| std::vector<std::string> expected_args = {"/bin/busybox", "umount", |
| temp_dir.GetPath().value()}; |
| |
| EXPECT_CALL(*mock_process_manager_, RunCommand(expected_args, _)) |
| .WillOnce(::testing::Return(0)); |
| |
| EXPECT_TRUE(UnmountPath(mock_process_manager_, temp_dir.GetPath())); |
| |
| // Verify invalid process manager. |
| EXPECT_FALSE(UnmountPath(nullptr, temp_dir.GetPath())); |
| } |
| |
| TEST(UtilsTest, CompressLogsTest) { |
| auto mock_process_manager = std::make_shared<MockProcessManager>(); |
| const auto archive_path = "/path/to/store/archive"; |
| std::vector<std::string> expected_cmd = {"/bin/tar", |
| "-czhf", |
| archive_path, |
| "/var/log/update_engine.log", |
| "/var/log/upstart.log", |
| "/var/log/minios.log"}; |
| EXPECT_CALL(*mock_process_manager, RunCommand(expected_cmd, _)); |
| |
| CompressLogs(mock_process_manager, base::FilePath{archive_path}); |
| } |
| |
| TEST(UtilsTest, KernelSizeTest) { |
| auto mock_process_manager = std::make_shared<MockProcessManager>(); |
| const auto device_path = "/dev/device0p1"; |
| std::vector<std::string> expected_cmd = {"/usr/bin/futility", "show", "-P", |
| device_path}; |
| const std::string futility_output = |
| std::string{"kernel_partition::/dev/nvme0n1p9\n"} + |
| std::string{"kernel::keyblock::size::1\n"} + |
| std::string{"kernel::preamble::size::10\n"} + |
| std::string{"kernel::preamble::body::load_address::0x100000\n"} + |
| std::string{"kernel::body::size::100\n"}; |
| |
| EXPECT_CALL(*mock_process_manager, |
| RunCommandWithOutput(expected_cmd, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<1>(0), SetArgPointee<2>(futility_output), |
| testing::Return(true))); |
| |
| EXPECT_THAT(KernelSize(mock_process_manager, base::FilePath{device_path}), |
| Optional(111)); |
| } |
| |
| TEST(UtilsTest, KernelSizeFailuresTest) { |
| auto mock_process_manager = std::make_shared<MockProcessManager>(); |
| const auto device_path = "/dev/device0p1"; |
| std::vector<std::string> expected_cmd = {"/usr/bin/futility", "show", "-P", |
| device_path}; |
| // Test out empty string. |
| std::string futility_output = ""; |
| EXPECT_CALL(*mock_process_manager, |
| RunCommandWithOutput(expected_cmd, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<1>(0), SetArgPointee<2>(futility_output), |
| testing::Return(true))); |
| EXPECT_EQ(KernelSize(mock_process_manager, base::FilePath{device_path}), |
| std::nullopt); |
| |
| // Missing kernel body size. |
| futility_output = std::string{"kernel::keyblock::size::2232\n"} + |
| std::string{"kernel::preamble::size::63304\n"}; |
| mock_process_manager = std::make_unique<MockProcessManager>(); |
| EXPECT_CALL(*mock_process_manager, |
| RunCommandWithOutput(expected_cmd, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<1>(0), SetArgPointee<2>(futility_output), |
| testing::Return(true))); |
| EXPECT_EQ(KernelSize(mock_process_manager, base::FilePath{device_path}), |
| std::nullopt); |
| |
| // 0 kernel body size. |
| futility_output = std::string{"kernel::keyblock::size::2232\n"} + |
| std::string{"kernel::preamble::size::63304\n"} + |
| std::string{"kernel::body::size::0\n"}; |
| mock_process_manager = std::make_unique<MockProcessManager>(); |
| EXPECT_CALL(*mock_process_manager, |
| RunCommandWithOutput(expected_cmd, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<1>(0), SetArgPointee<2>(futility_output), |
| testing::Return(true))); |
| EXPECT_EQ(KernelSize(mock_process_manager, base::FilePath{device_path}), |
| std::nullopt); |
| |
| // Non number value for keyblock size. |
| futility_output = std::string{"keyblock::size::bad_val\n"} + |
| std::string{"kernel::preamble::size::63304\n"} + |
| std::string{"kernel::preamble::body::size::50\n"}; |
| mock_process_manager = std::make_unique<MockProcessManager>(); |
| EXPECT_CALL(*mock_process_manager, |
| RunCommandWithOutput(expected_cmd, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<1>(0), SetArgPointee<2>(futility_output), |
| testing::Return(true))); |
| EXPECT_EQ(KernelSize(mock_process_manager, base::FilePath{device_path}), |
| std::nullopt); |
| } |
| |
| TEST(UtilsTest, GetRemovableDevices) { |
| auto device_list_entry = |
| std::make_unique<NiceMock<brillo::MockUdevListEntry>>(); |
| auto device = std::make_unique<StrictMock<brillo::MockUdevDevice>>(); |
| |
| auto mock_udev_enumerate = |
| std::make_unique<StrictMock<brillo::MockUdevEnumerate>>(); |
| auto mock_udev = std::make_unique<StrictMock<brillo::MockUdev>>(); |
| constexpr auto& device_node = "/dev/sda1"; |
| |
| EXPECT_CALL(*mock_udev_enumerate, AddMatchSubsystem(StrEq("block"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*mock_udev_enumerate, |
| AddMatchProperty(StrEq("ID_FS_USAGE"), StrEq("filesystem"))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*mock_udev_enumerate, ScanDevices()).WillOnce(Return(true)); |
| |
| // Setup device to be removable. |
| EXPECT_CALL(*device, GetSysAttributeValue(_)).WillOnce(Return("1")); |
| EXPECT_CALL(*device, GetDeviceNode()).WillOnce(Return(device_node)); |
| EXPECT_CALL(*mock_udev_enumerate, GetListEntry()) |
| .WillOnce(Return(std::move(device_list_entry))); |
| EXPECT_CALL(*mock_udev, CreateDeviceFromSysPath(_)) |
| .WillOnce(Return(std::move(device))); |
| EXPECT_CALL(*mock_udev, CreateEnumerate()) |
| .WillOnce(Return(std::move(mock_udev_enumerate))); |
| |
| std::vector<base::FilePath> removable_devices; |
| EXPECT_TRUE(GetRemovableDevices(removable_devices, std::move(mock_udev))); |
| // Expect to get back the one device path we have setup. |
| EXPECT_THAT(removable_devices, ElementsAre(base::FilePath{device_node})); |
| } |
| |
| TEST(UtilsTest, GetLogStoreKeyTest) { |
| auto vpd = std::make_shared<vpd::Vpd>(std::make_unique<vpd::FakeVpd>()); |
| const auto kValidHexKey = |
| brillo::SecureBlobToSecureHex(kValidKey).to_string(); |
| ASSERT_TRUE( |
| vpd->WriteValues(vpd::VpdRw, {{"minios_log_store_key", kValidHexKey}})); |
| |
| const auto log_store_key = GetLogStoreKey(vpd); |
| |
| ASSERT_TRUE(log_store_key.has_value()); |
| EXPECT_EQ(log_store_key.value(), kValidKey); |
| } |
| |
| TEST(UtilsTest, GetLogStoreKeyFailureTest) { |
| auto vpd = std::make_shared<vpd::Vpd>(std::make_unique<vpd::FakeVpd>()); |
| ASSERT_TRUE( |
| vpd->WriteValues(vpd::VpdRw, {{"minios_log_store_key", "short_key"}})); |
| const auto log_store_key = GetLogStoreKey(vpd); |
| |
| EXPECT_FALSE(log_store_key.has_value()); |
| } |
| |
| TEST(UtilsTest, LogStoreKeyValidTest) { |
| const auto& kShortKey = brillo::SecureBlob{"short"}; |
| const auto& kLongKey = |
| brillo::SecureBlob{"thisisa32bytestring1234567890abc_____"}; |
| const auto& kEmptyKey = brillo::SecureBlob{""}; |
| |
| EXPECT_TRUE(IsLogStoreKeyValid(kValidKey)); |
| EXPECT_FALSE(IsLogStoreKeyValid(kShortKey)); |
| EXPECT_FALSE(IsLogStoreKeyValid(kLongKey)); |
| EXPECT_FALSE(IsLogStoreKeyValid(kEmptyKey)); |
| } |
| |
| TEST(UtilsTest, SaveLogKeyTest) { |
| const auto kValidHexKey = |
| brillo::SecureBlobToSecureHex(kValidKey).to_string(); |
| auto vpd = std::make_shared<vpd::Vpd>(std::make_unique<vpd::FakeVpd>()); |
| |
| EXPECT_TRUE(SaveLogStoreKey(vpd, kValidKey)); |
| EXPECT_THAT(vpd->GetValue(vpd::VpdRw, "minios_log_store_key"), |
| Optional(kValidHexKey)); |
| } |
| |
| TEST(UtilsTest, ClearLogStoreKeyTest) { |
| // Zero string of hex key size. |
| const std::string kExpectedNullKey(kLogStoreKeySizeBytes * 2, '0'); |
| auto vpd = std::make_shared<vpd::Vpd>(std::make_unique<vpd::FakeVpd>()); |
| EXPECT_TRUE(ClearLogStoreKey(vpd)); |
| EXPECT_THAT(vpd->GetValue(vpd::VpdRw, "minios_log_store_key"), |
| Optional(kExpectedNullKey)); |
| } |
| |
| TEST(UtilsTest, EncryptDecryptTest) { |
| const auto& encrypted_archive = EncryptLogArchive(kTestData, kValidKey); |
| EXPECT_TRUE(encrypted_archive.has_value()); |
| const auto& archive = DecryptLogArchive(encrypted_archive.value(), kValidKey); |
| const auto& empty_decrypt_contents = |
| DecryptLogArchive(EncryptedLogFile{}, kValidKey); |
| |
| EXPECT_TRUE(archive.has_value()); |
| EXPECT_EQ(kTestData, archive); |
| EXPECT_EQ(empty_decrypt_contents, std::nullopt); |
| } |
| |
| TEST(UtilsTest, ReadFileToSecureBlobTest) { |
| base::ScopedTempDir tmp_dir_; |
| ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir()); |
| const auto file_path = tmp_dir_.GetPath().Append("file"); |
| |
| ASSERT_TRUE(base::WriteFile(file_path, kTestData)); |
| |
| const auto& file_contents = ReadFileToSecureBlob(file_path); |
| EXPECT_TRUE(file_contents.has_value()); |
| EXPECT_EQ(file_contents.value(), kTestData); |
| } |
| |
| TEST(UtilsTest, WriteSecureBlobToFileTest) { |
| base::ScopedTempDir tmp_dir_; |
| ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir()); |
| const auto file_path = tmp_dir_.GetPath().Append("file"); |
| EXPECT_TRUE(WriteSecureBlobToFile(file_path, kTestData)); |
| |
| const auto& file_contents = ReadFileToSecureBlob(file_path); |
| EXPECT_TRUE(file_contents.has_value()); |
| EXPECT_EQ(file_contents.value(), kTestData); |
| } |
| |
| TEST(UtilsTest, GetPartitionSizeTest) { |
| const auto mock_cgpt_util = std::make_shared<MockCgptUtil>(); |
| EXPECT_CALL(*mock_cgpt_util, GetSize(1)).WillOnce(Return(10)); |
| EXPECT_CALL(*mock_cgpt_util, GetSize(2)).WillOnce(Return(std::nullopt)); |
| EXPECT_THAT(GetPartitionSize(1, mock_cgpt_util), Optional(10 * kBlockSize)); |
| EXPECT_EQ(GetPartitionSize(2, mock_cgpt_util), std::nullopt); |
| } |
| |
| TEST(UtilsTest, GetMiniOsPriorityPartitionTest) { |
| const auto stub_crossystem = std::make_shared<crossystem::Crossystem>( |
| std::make_unique<crossystem::fake::CrossystemFake>()); |
| |
| EXPECT_EQ(GetMiniOsPriorityPartition(stub_crossystem), std::nullopt); |
| |
| stub_crossystem->VbSetSystemPropertyString( |
| crossystem::Crossystem::kMiniosPriorityProperty, "A"); |
| EXPECT_THAT(GetMiniOsPriorityPartition(stub_crossystem), Optional(9)); |
| |
| stub_crossystem->VbSetSystemPropertyString( |
| crossystem::Crossystem::kMiniosPriorityProperty, "B"); |
| EXPECT_THAT(GetMiniOsPriorityPartition(stub_crossystem), Optional(10)); |
| |
| stub_crossystem->VbSetSystemPropertyString( |
| crossystem::Crossystem::kMiniosPriorityProperty, "C"); |
| EXPECT_EQ(GetMiniOsPriorityPartition(stub_crossystem), std::nullopt); |
| } |
| |
| TEST(UtilsTest, ExtractArchiveTest) { |
| auto mock_process_manager = std::make_shared<MockProcessManager>(); |
| base::FilePath archive_path; |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| ASSERT_TRUE( |
| base::CreateTemporaryFileInDir(temp_dir.GetPath(), &archive_path)); |
| std::vector<std::string> expected_cmd = {"/bin/tar", "-xzf", |
| archive_path.value(), "-C", |
| archive_path.DirName().value()}; |
| |
| EXPECT_CALL(*mock_process_manager, RunCommand(expected_cmd, _)) |
| .WillOnce(Return(0)); |
| EXPECT_TRUE(ExtractArchive(mock_process_manager, archive_path, |
| base::FilePath{archive_path}.DirName(), {})); |
| } |
| |
| TEST(UtilsTest, ExtractArchiveStripTest) { |
| auto mock_process_manager = std::make_shared<MockProcessManager>(); |
| base::FilePath archive_path; |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| ASSERT_TRUE( |
| base::CreateTemporaryFileInDir(temp_dir.GetPath(), &archive_path)); |
| |
| std::vector<std::string> expected_cmd = {"/bin/tar", |
| "-xzf", |
| archive_path.value(), |
| "-C", |
| temp_dir.GetPath().value(), |
| "--strip-components=2"}; |
| |
| EXPECT_CALL(*mock_process_manager, RunCommand(expected_cmd, _)) |
| .WillOnce(Return(0)); |
| EXPECT_TRUE(ExtractArchive(mock_process_manager, archive_path, |
| temp_dir.GetPath(), {"--strip-components=2"})); |
| } |
| |
| } // namespace minios |