#include "util/file/file_io.h"
#include <stdio.h>
#include <iterator>
#include <limits>
#include <type_traits>
#include "base/atomicops.h"
#include "base/files/file_path.h"
#include "build/build_config.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/file.h"
#include "test/scoped_temp_dir.h"
#include "util/misc/implicit_cast.h"
#include "util/thread/thread.h"
namespace crashpad {
namespace test {
namespace {
_;
InSequence;
Return;
class MockReadExactly : public internal::ReadExactlyInternal { … };
TEST(FileIO, ReadExactly_Zero) { … }
TEST(FileIO, ReadExactly_SingleSmallSuccess) { … }
TEST(FileIO, ReadExactly_SingleSmallSuccessCanLog) { … }
TEST(FileIO, ReadExactly_SingleSmallFailure) { … }
TEST(FileIO, ReadExactly_SingleSmallFailureCanLog) { … }
TEST(FileIO, ReadExactly_DoubleSmallSuccess) { … }
TEST(FileIO, ReadExactly_DoubleSmallShort) { … }
TEST(FileIO, ReadExactly_DoubleSmallShortCanLog) { … }
TEST(FileIO, ReadExactly_Medium) { … }
TEST(FileIO, ReadExactly_LargeSuccess) { … }
TEST(FileIO, ReadExactly_LargeShort) { … }
TEST(FileIO, ReadExactly_LargeFailure) { … }
TEST(FileIO, ReadExactly_TripleMax) { … }
class MockWriteAll : public internal::WriteAllInternal { … };
TEST(FileIO, WriteAll_Zero) { … }
TEST(FileIO, WriteAll_SingleSmallSuccess) { … }
TEST(FileIO, WriteAll_SingleSmallFailure) { … }
TEST(FileIO, WriteAll_DoubleSmall) { … }
TEST(FileIO, WriteAll_Medium) { … }
TEST(FileIO, WriteAll_LargeSuccess) { … }
TEST(FileIO, WriteAll_LargeFailure) { … }
TEST(FileIO, WriteAll_TripleMax) { … }
void TestOpenFileForWrite(FileHandle (*opener)(const base::FilePath&,
FileWriteMode,
FilePermissions)) { … }
TEST(FileIO, OpenFileForWrite) { … }
TEST(FileIO, OpenFileForReadAndWrite) { … }
TEST(FileIO, LoggingOpenFileForWrite) { … }
TEST(FileIO, LoggingOpenFileForReadAndWrite) { … }
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
TEST(FileIO, LoggingOpenMemoryFileForReadAndWrite) { … }
#endif
enum class ReadOrWrite : bool { … };
void FileShareModeTest(ReadOrWrite first, ReadOrWrite second) { … }
TEST(FileIO, FileShareMode_Read_Read) { … }
TEST(FileIO, FileShareMode_Read_Write) { … }
TEST(FileIO, FileShareMode_Write_Read) { … }
TEST(FileIO, FileShareMode_Write_Write) { … }
#if CRASHPAD_FLOCK_ALWAYS_SUPPORTED
TEST(FileIO, MultipleSharedLocks) {
ScopedTempDir temp_dir;
base::FilePath shared_file =
temp_dir.path().Append(FILE_PATH_LITERAL("file_to_lock"));
{
ScopedFileHandle create(
LoggingOpenFileForWrite(shared_file,
FileWriteMode::kCreateOrFail,
FilePermissions::kOwnerOnly));
}
auto handle1 = ScopedFileHandle(LoggingOpenFileForRead(shared_file));
ASSERT_NE(handle1, kInvalidFileHandle);
EXPECT_EQ(
LoggingLockFile(
handle1.get(), FileLocking::kShared, FileLockingBlocking::kBlocking),
FileLockingResult::kSuccess);
auto handle2 = ScopedFileHandle(LoggingOpenFileForRead(shared_file));
ASSERT_NE(handle1, kInvalidFileHandle);
EXPECT_EQ(
LoggingLockFile(
handle2.get(), FileLocking::kShared, FileLockingBlocking::kBlocking),
FileLockingResult::kSuccess);
EXPECT_TRUE(LoggingUnlockFile(handle1.get()));
EXPECT_TRUE(LoggingUnlockFile(handle2.get()));
}
class LockingTestThread : public Thread {
public:
LockingTestThread()
: file_(), lock_type_(), iterations_(), actual_iterations_() {}
LockingTestThread(const LockingTestThread&) = delete;
LockingTestThread& operator=(const LockingTestThread&) = delete;
void Init(FileHandle file,
FileLocking lock_type,
int iterations,
base::subtle::Atomic32* actual_iterations) {
ASSERT_NE(file, kInvalidFileHandle);
file_ = ScopedFileHandle(file);
lock_type_ = lock_type;
iterations_ = iterations;
actual_iterations_ = actual_iterations;
}
private:
void ThreadMain() override {
for (int i = 0; i < iterations_; ++i) {
EXPECT_EQ(LoggingLockFile(
file_.get(), lock_type_, FileLockingBlocking::kBlocking),
FileLockingResult::kSuccess);
base::subtle::NoBarrier_AtomicIncrement(actual_iterations_, 1);
EXPECT_TRUE(LoggingUnlockFile(file_.get()));
}
}
ScopedFileHandle file_;
FileLocking lock_type_;
int iterations_;
base::subtle::Atomic32* actual_iterations_;
};
void LockingTest(FileLocking main_lock, FileLocking other_locks) {
ScopedTempDir temp_dir;
base::FilePath shared_file =
temp_dir.path().Append(FILE_PATH_LITERAL("file_to_lock"));
{
ScopedFileHandle create(
LoggingOpenFileForWrite(shared_file,
FileWriteMode::kCreateOrFail,
FilePermissions::kOwnerOnly));
}
auto initial = ScopedFileHandle(
(main_lock == FileLocking::kShared)
? LoggingOpenFileForRead(shared_file)
: LoggingOpenFileForWrite(shared_file,
FileWriteMode::kReuseOrCreate,
FilePermissions::kOwnerOnly));
ASSERT_NE(initial, kInvalidFileHandle);
EXPECT_EQ(
LoggingLockFile(initial.get(), main_lock, FileLockingBlocking::kBlocking),
FileLockingResult::kSuccess);
base::subtle::Atomic32 actual_iterations = 0;
LockingTestThread threads[20];
int expected_iterations = 0;
for (size_t index = 0; index < std::size(threads); ++index) {
int iterations_for_this_thread = static_cast<int>(index * 10);
threads[index].Init(
(other_locks == FileLocking::kShared)
? LoggingOpenFileForRead(shared_file)
: LoggingOpenFileForWrite(shared_file,
FileWriteMode::kReuseOrCreate,
FilePermissions::kOwnerOnly),
other_locks,
iterations_for_this_thread,
&actual_iterations);
expected_iterations += iterations_for_this_thread;
ASSERT_NO_FATAL_FAILURE(threads[index].Start());
}
base::subtle::Atomic32 result =
base::subtle::NoBarrier_Load(&actual_iterations);
EXPECT_EQ(result, 0);
ASSERT_TRUE(LoggingUnlockFile(initial.get()));
for (auto& t : threads)
t.Join();
result = base::subtle::NoBarrier_Load(&actual_iterations);
EXPECT_EQ(result, expected_iterations);
}
TEST(FileIO, ExclusiveVsExclusives) {
LockingTest(FileLocking::kExclusive, FileLocking::kExclusive);
}
TEST(FileIO, ExclusiveVsShareds) {
LockingTest(FileLocking::kExclusive, FileLocking::kShared);
}
TEST(FileIO, SharedVsExclusives) {
LockingTest(FileLocking::kShared, FileLocking::kExclusive);
}
TEST(FileIO, ExclusiveVsExclusivesNonBlocking) {
ScopedTempDir temp_dir;
base::FilePath exclusive_file =
temp_dir.path().Append(FILE_PATH_LITERAL("file_to_lock"));
{
ScopedFileHandle create(
LoggingOpenFileForWrite(exclusive_file,
FileWriteMode::kCreateOrFail,
FilePermissions::kOwnerOnly));
}
auto handle1 = ScopedFileHandle(LoggingOpenFileForRead(exclusive_file));
ASSERT_NE(handle1, kInvalidFileHandle);
EXPECT_EQ(LoggingLockFile(handle1.get(),
FileLocking::kExclusive,
FileLockingBlocking::kBlocking),
FileLockingResult::kSuccess);
auto handle2 = ScopedFileHandle(LoggingOpenFileForRead(exclusive_file));
ASSERT_NE(handle2, kInvalidFileHandle);
EXPECT_EQ(LoggingLockFile(handle2.get(),
FileLocking::kExclusive,
FileLockingBlocking::kNonBlocking),
FileLockingResult::kWouldBlock);
EXPECT_TRUE(LoggingUnlockFile(handle1.get()));
EXPECT_EQ(LoggingLockFile(handle2.get(),
FileLocking::kExclusive,
FileLockingBlocking::kNonBlocking),
FileLockingResult::kSuccess);
EXPECT_TRUE(LoggingUnlockFile(handle2.get()));
}
#endif
TEST(FileIO, FileSizeByHandle) { … }
FileHandle FileHandleForFILE(FILE* file) { … }
TEST(FileIO, StdioFileHandle) { … }
}
}
}