blob: dc6a1eec4e0be44512528687b4ab96c9cb030aec [file] [log] [blame]
// Copyright 2015 The Chromium 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 <stdint.h>
#include <memory>
#include <utility>
#include "base/macros.h"
#include "components/filesystem/public/interfaces/file_system.mojom.h"
#include "mojo/application/public/cpp/application_impl.h"
#include "mojo/application/public/cpp/application_test_base.h"
#include "mojo/util/capture_util.h"
#include "sql/mojo/mojo_vfs.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/sqlite/sqlite3.h"
namespace std {
// This deleter lets us be safe with sqlite3 objects, which aren't really the
// structs, but slabs of new uint8_t[size].
template <>
struct default_delete<sqlite3_file> {
inline void operator()(sqlite3_file* ptr) const {
// Why don't we call file->pMethods->xClose() here? Because it's not
// guaranteed to be valid. sqlite3_file "objects" can be in partially
// initialized states.
delete [] reinterpret_cast<uint8_t*>(ptr);
}
};
} // namespace std
namespace sql {
const char kFileName[] = "TestingDatabase.db";
class VFSTest : public mojo::test::ApplicationTestBase,
public filesystem::FileSystemClient {
public:
VFSTest() : binding_(this) {}
~VFSTest() override {}
sqlite3_vfs* vfs() {
return sqlite3_vfs_find(NULL);
}
scoped_ptr<sqlite3_file> MakeFile() {
return scoped_ptr<sqlite3_file>(reinterpret_cast<sqlite3_file*>(
new uint8_t[vfs()->szOsFile]));
}
void SetUp() override {
mojo::test::ApplicationTestBase::SetUp();
application_impl()->ConnectToService("mojo:filesystem", &files_);
filesystem::FileSystemClientPtr client;
binding_.Bind(GetProxy(&client));
filesystem::FileError error = filesystem::FILE_ERROR_FAILED;
filesystem::DirectoryPtr directory;
files_->OpenFileSystem("temp", GetProxy(&directory), std::move(client),
mojo::Capture(&error));
ASSERT_TRUE(files_.WaitForIncomingResponse());
ASSERT_EQ(filesystem::FILE_ERROR_OK, error);
vfs_.reset(new ScopedMojoFilesystemVFS(std::move(directory)));
}
void TearDown() override {
vfs_.reset();
mojo::test::ApplicationTestBase::TearDown();
}
void OnFileSystemShutdown() override {
}
private:
filesystem::FileSystemPtr files_;
scoped_ptr<ScopedMojoFilesystemVFS> vfs_;
mojo::Binding<filesystem::FileSystemClient> binding_;
DISALLOW_COPY_AND_ASSIGN(VFSTest);
};
TEST_F(VFSTest, TestInstalled) {
EXPECT_EQ("mojo", std::string(vfs()->zName));
}
TEST_F(VFSTest, ExclusiveOpen) {
// Opening a non-existent file exclusively should work.
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_CREATE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
// Opening it a second time exclusively shouldn't.
scoped_ptr<sqlite3_file> file2(MakeFile());
rc = vfs()->xOpen(vfs(), kFileName, file2.get(),
SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_CREATE,
&out_flags);
EXPECT_NE(SQLITE_OK, rc);
file->pMethods->xClose(file.get());
}
TEST_F(VFSTest, NonexclusiveOpen) {
// Opening a non-existent file should work.
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
// Opening it a second time should work.
scoped_ptr<sqlite3_file> file2(MakeFile());
rc = vfs()->xOpen(vfs(), kFileName, file2.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
file->pMethods->xClose(file.get());
file->pMethods->xClose(file2.get());
}
TEST_F(VFSTest, NullFilenameOpen) {
// Opening a file with a null filename should return a valid file object.
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(
vfs(), nullptr, file.get(),
SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
file->pMethods->xClose(file.get());
}
TEST_F(VFSTest, DeleteOnClose) {
{
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(
vfs(), kFileName, file.get(),
SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
file->pMethods->xClose(file.get());
}
// The file shouldn't exist now.
int result = 0;
vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result);
EXPECT_FALSE(result);
}
TEST_F(VFSTest, TestNonExistence) {
// We shouldn't have a file exist yet in a fresh directory.
int result = 0;
vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result);
EXPECT_FALSE(result);
}
TEST_F(VFSTest, TestExistence) {
{
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
file->pMethods->xClose(file.get());
}
int result = 0;
vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result);
EXPECT_TRUE(result);
}
TEST_F(VFSTest, TestDelete) {
{
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
file->pMethods->xClose(file.get());
}
int result = 0;
vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result);
EXPECT_TRUE(result);
vfs()->xDelete(vfs(), kFileName, 0);
vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result);
EXPECT_FALSE(result);
}
TEST_F(VFSTest, TestWriteAndRead) {
const char kBuffer[] = "One Two Three Four Five Six Seven";
const int kBufferSize = arraysize(kBuffer);
{
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
for (int i = 0; i < 10; ++i) {
rc = file->pMethods->xWrite(file.get(), kBuffer, kBufferSize,
i * kBufferSize);
EXPECT_EQ(SQLITE_OK, rc);
}
file->pMethods->xClose(file.get());
}
// Expect that the size of the file is 10 * arraysize(kBuffer);
{
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
sqlite_int64 size;
rc = file->pMethods->xFileSize(file.get(), &size);
EXPECT_EQ(SQLITE_OK, rc);
EXPECT_EQ(10 * kBufferSize, size);
file->pMethods->xClose(file.get());
}
// We should be able to read things back.
{
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
char data_buffer[kBufferSize];
memset(data_buffer, '8', kBufferSize);
for (int i = 0; i < 10; ++i) {
rc = file->pMethods->xRead(file.get(), data_buffer, kBufferSize,
i * kBufferSize);
EXPECT_EQ(SQLITE_OK, rc);
EXPECT_TRUE(strncmp(kBuffer, &data_buffer[0], kBufferSize) == 0);
}
file->pMethods->xClose(file.get());
}
}
TEST_F(VFSTest, PartialRead) {
const char kBuffer[] = "One Two Three Four Five Six Seven";
const int kBufferSize = arraysize(kBuffer);
// Write the data once.
{
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
rc = file->pMethods->xWrite(file.get(), kBuffer, kBufferSize, 0);
EXPECT_EQ(SQLITE_OK, rc);
file->pMethods->xClose(file.get());
}
// Now attempt to read kBufferSize + 5 from a file sized to kBufferSize.
{
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
const char kBufferWithFiveNulls[] =
"One Two Three Four Five Six Seven\0\0\0\0\0";
const int kBufferWithFiveNullsSize = arraysize(kBufferWithFiveNulls);
char data_buffer[kBufferWithFiveNullsSize];
memset(data_buffer, '8', kBufferWithFiveNullsSize);
rc = file->pMethods->xRead(file.get(), data_buffer,
kBufferWithFiveNullsSize, 0);
EXPECT_EQ(SQLITE_IOERR_SHORT_READ, rc);
EXPECT_TRUE(strncmp(kBufferWithFiveNulls, &data_buffer[0],
kBufferWithFiveNullsSize) == 0);
file->pMethods->xClose(file.get());
}
}
TEST_F(VFSTest, Truncate) {
const char kBuffer[] = "One Two Three Four Five Six Seven";
const int kBufferSize = arraysize(kBuffer);
const int kCharsToThree = 13;
scoped_ptr<sqlite3_file> file(MakeFile());
int out_flags;
int rc = vfs()->xOpen(vfs(), kFileName, file.get(),
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
&out_flags);
EXPECT_EQ(SQLITE_OK, rc);
rc = file->pMethods->xWrite(file.get(), kBuffer, kBufferSize, 0);
EXPECT_EQ(SQLITE_OK, rc);
sqlite_int64 size;
rc = file->pMethods->xFileSize(file.get(), &size);
EXPECT_EQ(SQLITE_OK, rc);
EXPECT_EQ(kBufferSize, size);
rc = file->pMethods->xTruncate(file.get(), kCharsToThree);
EXPECT_EQ(SQLITE_OK, rc);
rc = file->pMethods->xFileSize(file.get(), &size);
EXPECT_EQ(SQLITE_OK, rc);
EXPECT_EQ(kCharsToThree, size);
file->pMethods->xClose(file.get());
}
} // namespace sql