blob: cb91abcc6b5f4e7a98ea1d0ba04230ace7b12769 [file] [log] [blame]
// Copyright 2020 The Chromium OS 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 "smbfs/recursive_delete_operation.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <list>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/callback_helpers.h>
#include <base/test/task_environment.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "smbfs/samba_interface_impl.h"
#include "smbfs/smb_credential.h"
#include "smbfs/smb_filesystem.h"
namespace smbfs {
namespace {
using ::testing::_;
using ::testing::Return;
constexpr char kSharePath[] = "smb://server/share";
constexpr char kDirectoryToDelete[] = "/the/directory";
constexpr char kFileToDelete[] = "/the/directory/file";
class MockSmbFilesystemDelegate : public SmbFilesystem::Delegate {
public:
MOCK_METHOD(void,
RequestCredentials,
(RequestCredentialsCallback),
(override));
};
class MockSambaInterface : public SambaInterfaceImpl {
public:
MOCK_METHOD(int, UnlinkFile, (const std::string&), (override));
MOCK_METHOD(int, RemoveDirectory, (const std::string&), (override));
MOCK_METHOD(int, CloseDirectory, (SMBCFILE*), (override));
MOCK_METHOD(int, OpenDirectory, (const std::string&, SMBCFILE**), (override));
MOCK_METHOD(int,
ReadDirectory,
(SMBCFILE*, const struct libsmb_file_info**, struct stat*),
(override));
};
class MockSmbFilesystem : public SmbFilesystem {
public:
MockSmbFilesystem()
: SmbFilesystem(&mock_delegate_, kSharePath),
mock_samba_impl_(new MockSambaInterface()) {
const std::vector<uint8_t> empty_ip_address;
SetResolvedAddress(empty_ip_address);
SetSambaInterface(std::unique_ptr<SambaInterface>(mock_samba_impl_));
}
MockSambaInterface* samba_impl() { return mock_samba_impl_; }
private:
MockSmbFilesystemDelegate mock_delegate_;
MockSambaInterface* mock_samba_impl_;
};
class TestRecursiveDeleteOperation : public RecursiveDeleteOperation {
public:
explicit TestRecursiveDeleteOperation(CompletionCallback callback)
: RecursiveDeleteOperation(nullptr,
kSharePath,
base::FilePath(kDirectoryToDelete),
std::move(callback)) {
SetSambaInterface(fs_.samba_impl());
}
MockSmbFilesystem& fs() { return fs_; }
private:
MockSmbFilesystem fs_;
};
} // namespace
class RecursiveDeleteOperationTest : public testing::Test {
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY,
base::test::TaskEnvironment::MainThreadType::IO};
};
TEST_F(RecursiveDeleteOperationTest, DeleteFile) {
TestRecursiveDeleteOperation delete_operation((base::DoNothing()));
base::FilePath file_path(kFileToDelete);
std::string share_path = std::string(kSharePath) + file_path.value();
EXPECT_CALL(*(delete_operation.fs().samba_impl()), UnlinkFile(_))
.WillOnce([&](const std::string& share_file_path) -> int {
EXPECT_EQ(share_path, share_file_path);
return 0;
});
delete_operation.DeleteFile(file_path);
}
TEST_F(RecursiveDeleteOperationTest, DeleteDirectory) {
TestRecursiveDeleteOperation delete_operation(
base::BindOnce([](mojom::DeleteRecursivelyError error) {}));
base::FilePath dir_path(kDirectoryToDelete);
std::string share_path = std::string(kSharePath) + dir_path.value();
EXPECT_CALL(*(delete_operation.fs().samba_impl()), RemoveDirectory(_))
.WillOnce([&](const std::string& share_dir_path) -> int {
EXPECT_EQ(share_path, share_dir_path);
return 0;
});
delete_operation.DeleteDirectory(dir_path);
}
TEST_F(RecursiveDeleteOperationTest, CloseDirectory) {
TestRecursiveDeleteOperation delete_operation(
base::BindOnce([](mojom::DeleteRecursivelyError error) {}));
EXPECT_CALL(*(delete_operation.fs().samba_impl()), CloseDirectory(_))
.WillOnce([&](SMBCFILE* dir) -> int { return 0; });
delete_operation.CloseDirectory(nullptr);
}
TEST_F(RecursiveDeleteOperationTest, GetDirectoryListing) {
TestRecursiveDeleteOperation delete_operation(
base::BindOnce([](mojom::DeleteRecursivelyError error) {}));
EXPECT_CALL(*(delete_operation.fs().samba_impl()), OpenDirectory(_, _))
.Times(1)
.WillOnce(Return(0));
EXPECT_CALL(*(delete_operation.fs().samba_impl()), CloseDirectory(_))
.Times(1)
.WillOnce(Return(0));
// Mock out the listing of a directory with two entries: a file and a
// subdirectory.
struct MockEntry {
std::string entry_name;
struct libsmb_file_info entry_info;
struct stat entry_stat;
};
int entry_count = 0;
std::vector<MockEntry> mock_entries = {{"file_name", {0}, {0}},
{"dir_name", {0}, {0}}};
mock_entries[1].entry_stat.st_mode |= S_IFDIR;
EXPECT_CALL(*(delete_operation.fs().samba_impl()), ReadDirectory(_, _, _))
.Times(mock_entries.size() + 1)
.WillRepeatedly([&](SMBCFILE* dir,
const struct libsmb_file_info** file_info,
struct stat* file_stat) -> int {
if (entry_count < mock_entries.size()) {
*file_info = &(mock_entries[entry_count].entry_info);
struct libsmb_file_info** inner_file_info =
const_cast<struct libsmb_file_info**>(file_info);
(*inner_file_info)->name =
const_cast<char*>(mock_entries[entry_count].entry_name.c_str());
*file_stat = mock_entries[entry_count].entry_stat;
} else {
*file_info = nullptr;
}
entry_count++;
return 0;
});
base::FilePath dir_path(kDirectoryToDelete);
std::list<struct RecursiveDeleteOperation::Entry> entries;
bool success = delete_operation.GetDirectoryListing(dir_path, &entries);
EXPECT_TRUE(success);
EXPECT_EQ(mock_entries.size(), entries.size());
RecursiveDeleteOperation::Entry entry = entries.front();
EXPECT_EQ(entry.path, dir_path.Append(mock_entries[0].entry_name));
EXPECT_FALSE(entry.is_directory);
entries.pop_front();
entry = entries.front();
EXPECT_EQ(entry.path.value(),
dir_path.Append(mock_entries[1].entry_name).value());
EXPECT_TRUE(entry.is_directory);
}
} // namespace smbfs