blob: faaa15b4afa848a7e1d5270903107a7420ed3fa1 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "file_utils.h"
#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
namespace {
// TODO: Move that to a test support library or reuse an existing one.
// Convenience class to ensure that a FILE* handle is fclose()-ed
// on scope exit. Usage is pretty simple:
//
// FILE* f = fopen(....);
// if (!f)
// return ....; // error
//
// ScopedFileGuard guard(f); // ensures |f| is closed on scope exit.
//
class ScopedFileGuard {
public:
ScopedFileGuard(FILE * file) : file_(file)
{
}
~ScopedFileGuard()
{
if (file_)
{
fclose(file_);
}
}
private:
FILE * file_ = NULL;
};
// Convenience class for a temporary directory.
// Ensures that the directory is removed on scope exit.
class ScopedTempDir {
public:
ScopedTempDir()
{
const char * tmp_dir = getenv("TMPDIR");
if (!tmp_dir)
tmp_dir = "/tmp";
snprintf(temp_root_, sizeof(temp_root_), "%s/XXXXXX", tmp_dir);
EXPECT_TRUE(mkdtemp(temp_root_));
}
// NOTE: Destructor only removes the directory, not any files in it.
// Use ScopedTempFile() to ensure temporary files are destroyed at the
// end of tests.
~ScopedTempDir()
{
rmdir(temp_root_);
}
// Return temporary root directory.
const char *
root() const
{
return temp_root_;
}
private:
char temp_root_[256];
};
// Convenience class for a temporary file that is removed on scoped exit.
class ScopedTempFile {
public:
// Constructor, creates file path, but doesn't touch the file system.
// |name| is a file name (cannot include sub-directory).
// |root| is the root ScopedTempDir.
ScopedTempFile(const char * name, const ScopedTempDir & root)
{
snprintf(file_path_, sizeof(file_path_), "%s/%s", root.root(), name);
}
// Destructor removes the file, if any.
~ScopedTempFile()
{
(void)unlink(file_path_);
}
// Write |data_size| bytes from |data| into the file.
bool
WriteData(const void * data, size_t data_size)
{
FILE * f = fopen(file_path_, "wb");
if (!f)
return false;
ScopedFileGuard guard(f);
return fwrite(data, 1, data_size, f) == data_size;
}
// Read the file. On success return true and sets |*out|, false/errno
// otherwise.
bool
ReadData(std::string * out)
{
FILE * f = fopen(file_path_, "rb");
if (!f)
return false;
ScopedFileGuard guard(f);
if (fseek(f, 0, SEEK_END) != 0)
return false;
long signed_size = ftell(f);
if (signed_size < 0)
return false;
if (fseek(f, 0, SEEK_SET) != 0)
return false;
auto size = static_cast<size_t>(signed_size);
out->resize(size);
return fread(&out[0], 1, size, f) == size;
}
const char *
path() const
{
return file_path_;
}
private:
char file_path_[256];
};
//
//
//
TEST(common, file_read_WithInvalidPath)
{
int dummy = 10;
void * data = &dummy; // Pseudo-random value.
size_t size = 42;
EXPECT_FALSE(file_read("/this/path/does/not/exist", &data, &size));
EXPECT_EQ(NULL, data);
EXPECT_EQ(0U, size);
}
TEST(common, file_read_EmptyFile)
{
ScopedTempDir tmp_dir;
ScopedTempFile empty("empty.txt", tmp_dir);
empty.WriteData("", 0);
int dummy = 10;
void * data = &dummy;
size_t size = 42;
EXPECT_TRUE(file_read(empty.path(), &data, &size));
EXPECT_EQ(NULL, data);
EXPECT_EQ(0U, size);
}
TEST(common, file_read_RegularFile)
{
ScopedTempDir tmp_dir;
ScopedTempFile file("example.txt", tmp_dir);
std::string text = "Hello World!";
ASSERT_TRUE(file.WriteData(text.c_str(), text.size()));
void * data;
size_t size;
EXPECT_TRUE(file_read(file.path(), &data, &size));
ASSERT_TRUE(data);
ASSERT_EQ(text.size(), size);
ASSERT_TRUE(!memcmp(data, text.c_str(), size));
free(data);
}
TEST(common, file_write_WithInvalidPath)
{
char kData[] = "Hello World!";
EXPECT_FALSE(file_write("/this/path/does/not/exist", kData, sizeof(kData)));
}
TEST(common, file_write_EmptyFile)
{
ScopedTempDir tmp_dir;
ScopedTempFile empty("empty.txt", tmp_dir);
EXPECT_TRUE(file_write(empty.path(), "", 0));
std::string out("dummy");
ASSERT_TRUE(empty.ReadData(&out));
EXPECT_EQ("", out);
}
TEST(common, file_write_RegularFile)
{
ScopedTempDir tmp_dir;
ScopedTempFile file("example.txt", tmp_dir);
const std::string text = "Hello World!";
EXPECT_TRUE(file_write(file.path(), text.c_str(), text.size()));
std::string data;
ASSERT_TRUE(file.ReadData(&data));
EXPECT_EQ(text, data);
}
} // namespace