#include "file.h"
#include "fs.h"
#include "tempfile.h"
#include <library/cpp/testing/unittest/registar.h>
#include <util/stream/file.h>
#include <util/generic/yexception.h>
class TFileTest: public TTestBase {
UNIT_TEST_SUITE(TFileTest);
UNIT_TEST(TestOpen);
UNIT_TEST(TestOpenSync);
UNIT_TEST(TestRW);
UNIT_TEST(TestReWrite);
UNIT_TEST(TestAppend);
UNIT_TEST(TestLinkTo);
UNIT_TEST(TestResize);
UNIT_TEST(TestLocale);
UNIT_TEST(TestFlush);
UNIT_TEST(TestFlushSpecialFile);
UNIT_TEST(TestRawRead);
UNIT_TEST(TestRead);
UNIT_TEST(TestRawPread);
UNIT_TEST(TestPread);
UNIT_TEST(TestCache);
UNIT_TEST_SUITE_END();
public:
void TestOpen();
void TestOpenSync();
void TestRW();
void TestLocale();
void TestFlush();
void TestFlushSpecialFile();
void TestRawRead();
void TestRead();
void TestRawPread();
void TestPread();
void TestCache();
inline void TestLinkTo() {
TTempFile tmp1("tmp1");
TTempFile tmp2("tmp2");
{
TFile f1(tmp1.Name(), OpenAlways | WrOnly);
TFile f2(tmp2.Name(), OpenAlways | WrOnly);
f1.LinkTo(f2);
f1.Write("12345", 5);
f2.Write("67890", 5);
}
UNIT_ASSERT_EQUAL(TUnbufferedFileInput(tmp2.Name()).ReadAll(), "1234567890");
}
inline void TestAppend() {
TTempFile tmp("tmp");
{
TFile f(tmp.Name(), OpenAlways | WrOnly);
f.Write("12345678", 8);
}
{
TFile f(tmp.Name(), OpenAlways | WrOnly | ForAppend);
f.Write("67", 2);
f.Write("89", 2);
}
UNIT_ASSERT_EQUAL(TUnbufferedFileInput(tmp.Name()).ReadAll(), "123456786789");
}
inline void TestReWrite() {
TTempFile tmp("tmp");
{
TFile f(tmp.Name(), OpenAlways | WrOnly);
f.Write("12345678", 8);
}
{
TFile f(tmp.Name(), OpenAlways | WrOnly);
f.Write("6789", 4);
}
UNIT_ASSERT_EQUAL(TUnbufferedFileInput(tmp.Name()).ReadAll(), "67895678");
}
inline void TestResize() {
TTempFile tmp("tmp");
{
TFile file(tmp.Name(), OpenAlways | WrOnly);
file.Write("1234567", 7);
file.Seek(3, sSet);
file.Resize(5);
UNIT_ASSERT_EQUAL(file.GetLength(), 5);
UNIT_ASSERT_EQUAL(file.GetPosition(), 3);
file.Resize(12);
UNIT_ASSERT_EQUAL(file.GetLength(), 12);
UNIT_ASSERT_EQUAL(file.GetPosition(), 3);
}
const TString data = TUnbufferedFileInput(tmp.Name()).ReadAll();
UNIT_ASSERT_EQUAL(data.length(), 12);
UNIT_ASSERT(data.StartsWith("12345"));
}
};
UNIT_TEST_SUITE_REGISTRATION(TFileTest);
void TFileTest::TestOpen() {
TString res;
TFile f1;
try {
TFile f2("f1.txt", OpenExisting);
} catch (const yexception& e) {
res = e.what();
}
UNIT_ASSERT(!res.empty());
res.remove();
try {
TFile f2("f1.txt", OpenAlways);
f1 = f2;
} catch (const yexception& e) {
res = e.what();
}
UNIT_ASSERT(res.empty());
UNIT_ASSERT(f1.IsOpen());
UNIT_ASSERT_VALUES_EQUAL(f1.GetName(), "f1.txt");
UNIT_ASSERT_VALUES_EQUAL(f1.GetLength(), 0);
try {
TFile f2("f1.txt", CreateNew);
} catch (const yexception& e) {
res = e.what();
}
UNIT_ASSERT(!res.empty());
res.remove();
f1.Close();
UNIT_ASSERT(unlink("f1.txt") == 0);
}
void TFileTest::TestOpenSync() {
TFile f1("f1.txt", CreateNew | Sync);
UNIT_ASSERT(f1.IsOpen());
f1.Close();
UNIT_ASSERT(!f1.IsOpen());
UNIT_ASSERT(unlink("f1.txt") == 0);
}
void TFileTest::TestRW() {
TFile f1("f1.txt", CreateNew);
UNIT_ASSERT(f1.IsOpen());
UNIT_ASSERT_VALUES_EQUAL(f1.GetName(), "f1.txt");
UNIT_ASSERT_VALUES_EQUAL(f1.GetLength(), 0);
ui32 d[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
f1.Write(&d, sizeof(ui32) * 10);
UNIT_ASSERT_VALUES_EQUAL(f1.GetLength(), 40);
UNIT_ASSERT_VALUES_EQUAL(f1.GetPosition(), 40);
UNIT_ASSERT_VALUES_EQUAL(f1.Seek(12, sSet), 12);
f1.Flush();
ui32 v;
f1.Load(&v, sizeof(v));
UNIT_ASSERT_VALUES_EQUAL(v, 3u);
UNIT_ASSERT_VALUES_EQUAL(f1.GetPosition(), 16);
TFile f2 = f1;
UNIT_ASSERT(f2.IsOpen());
UNIT_ASSERT_VALUES_EQUAL(f2.GetName(), "f1.txt");
UNIT_ASSERT_VALUES_EQUAL(f2.GetPosition(), 16);
UNIT_ASSERT_VALUES_EQUAL(f2.GetLength(), 40);
f2.Write(&v, sizeof(v));
UNIT_ASSERT_VALUES_EQUAL(f1.GetPosition(), 20);
UNIT_ASSERT_VALUES_EQUAL(f1.Seek(-4, sCur), 16);
v = 0;
f1.Load(&v, sizeof(v));
UNIT_ASSERT_VALUES_EQUAL(v, 3u);
f1.Close();
UNIT_ASSERT(!f1.IsOpen());
UNIT_ASSERT(!f2.IsOpen());
UNIT_ASSERT(unlink("f1.txt") == 0);
}
#ifdef _unix_
#include <locale.h>
#endif
void TFileTest::TestLocale() {
#ifdef _unix_
const char* loc = setlocale(LC_CTYPE, nullptr);
setlocale(LC_CTYPE, "ru_RU.UTF-8");
#endif
TFile f("Имя.txt", CreateNew);
UNIT_ASSERT(f.IsOpen());
UNIT_ASSERT_VALUES_EQUAL(f.GetName(), "Имя.txt");
UNIT_ASSERT_VALUES_EQUAL(f.GetLength(), 0);
f.Close();
UNIT_ASSERT(NFs::Remove("Имя.txt"));
#ifdef _unix_
setlocale(LC_CTYPE, loc);
#endif
}
void TFileTest::TestFlush() {
TTempFile tmp("tmp");
{
TFile f(tmp.Name(), OpenAlways | WrOnly);
f.Flush();
f.FlushData();
f.Close();
UNIT_ASSERT_EXCEPTION(f.Flush(), TFileError);
UNIT_ASSERT_EXCEPTION(f.FlushData(), TFileError);
}
}
void TFileTest::TestFlushSpecialFile() {
#ifdef _unix_
TFile devNull("/dev/null", WrOnly);
devNull.FlushData();
devNull.Flush();
devNull.Close();
#endif
}
void TFileTest::TestRawRead() {
TTempFile tmp("tmp");
{
TFile file(tmp.Name(), OpenAlways | WrOnly);
file.Write("1234567", 7);
file.Flush();
file.Close();
}
{
TFile file(tmp.Name(), OpenExisting | RdOnly);
char buf[7];
i32 reallyRead = file.RawRead(buf, 7);
Y_ENSURE(0 <= reallyRead && reallyRead <= 7);
Y_ENSURE(TStringBuf(buf, reallyRead) == TStringBuf("1234567").Head(reallyRead));
}
}
void TFileTest::TestRead() {
TTempFile tmp("tmp");
{
TFile file(tmp.Name(), OpenAlways | WrOnly);
file.Write("1234567", 7);
file.Flush();
file.Close();
}
{
TFile file(tmp.Name(), OpenExisting | RdOnly);
char buf[7];
Y_ENSURE(file.Read(buf, 7) == 7);
Y_ENSURE(TStringBuf(buf, 7) == "1234567");
memset(buf, 0, sizeof(buf));
file.Seek(0, sSet);
Y_ENSURE(file.Read(buf, 123) == 7);
Y_ENSURE(TStringBuf(buf, 7) == "1234567");
}
}
void TFileTest::TestRawPread() {
TTempFile tmp("tmp");
{
TFile file(tmp.Name(), OpenAlways | WrOnly);
file.Write("1234567", 7);
file.Flush();
file.Close();
}
{
TFile file(tmp.Name(), OpenExisting | RdOnly);
char buf[7];
i32 reallyRead = file.RawPread(buf, 3, 1);
Y_ENSURE(0 <= reallyRead && reallyRead <= 3);
Y_ENSURE(TStringBuf(buf, reallyRead) == TStringBuf("234").Head(reallyRead));
memset(buf, 0, sizeof(buf));
reallyRead = file.RawPread(buf, 2, 5);
Y_ENSURE(0 <= reallyRead && reallyRead <= 2);
Y_ENSURE(TStringBuf(buf, reallyRead) == TStringBuf("67").Head(reallyRead));
}
}
void TFileTest::TestPread() {
TTempFile tmp("tmp");
{
TFile file(tmp.Name(), OpenAlways | WrOnly);
file.Write("1234567", 7);
file.Flush();
file.Close();
}
{
TFile file(tmp.Name(), OpenExisting | RdOnly);
char buf[7];
Y_ENSURE(file.Pread(buf, 3, 1) == 3);
Y_ENSURE(TStringBuf(buf, 3) == "234");
memset(buf, 0, sizeof(buf));
Y_ENSURE(file.Pread(buf, 2, 5) == 2);
Y_ENSURE(TStringBuf(buf, 2) == "67");
}
}
#ifdef _linux_
#include <sys/statfs.h>
#endif
#ifndef TMPFS_MAGIC
#define TMPFS_MAGIC 0x01021994
#endif
void TFileTest::TestCache(){
#ifdef _linux_
{// create file in /tmp, current dir could be tmpfs which does not support fadvise
TFile file(MakeTempName("/tmp"), OpenAlways | Transient | RdWr | NoReadAhead);
struct statfs fs;
if (!fstatfs(file.GetHandle(), &fs) && fs.f_type == TMPFS_MAGIC) {
return;
}
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(), 0);
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(0, 0), 0);
file.Resize(7);
file.PrefetchCache();
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(), 7);
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(3, 2), 2);
file.FlushCache();
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(), 7);
file.EvictCache();
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(), 0);
file.PrefetchCache();
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(), 7);
file.Resize(12345);
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(), 4096);
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(4096, 0), 0);
file.PrefetchCache();
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(), 12345);
file.FlushCache();
file.EvictCache();
UNIT_ASSERT_LE(file.CountCache(), 0);
file.Resize(33333333);
file.PrefetchCache(11111111, 11111111);
UNIT_ASSERT_GE(file.CountCache(), 11111111);
UNIT_ASSERT_LE(file.CountCache(0, 11111111), 1111111);
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(11111111, 11111111), 11111111);
UNIT_ASSERT_LE(file.CountCache(22222222, 11111111), 1111111);
file.FlushCache(11111111, 11111111);
UNIT_ASSERT_GE(file.CountCache(), 11111111);
// first and last incomplete pages could stay in cache
file.EvictCache(11111111, 11111111);
UNIT_ASSERT_LT(file.CountCache(11111111, 11111111), 4096 * 2);
file.EvictCache();
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(), 0);
}
#else
{TFile file(MakeTempName(), OpenAlways | Transient | RdWr);
file.Resize(12345);
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(), -1);
file.PrefetchCache();
file.FlushCache();
file.EvictCache();
UNIT_ASSERT_VALUES_EQUAL(file.CountCache(0, 12345), -1);
}
#endif
}
Y_UNIT_TEST_SUITE(TTestFileHandle) {
Y_UNIT_TEST(MoveAssignment) {
TTempFile tmp("tmp");
{
TFileHandle file1(tmp.Name(), OpenAlways | WrOnly);
file1.Write("1", 1);
TFileHandle file2;
file2 = std::move(file1);
Y_ENSURE(!file1.IsOpen());
Y_ENSURE(file2.IsOpen());
file2.Write("2", 1);
}
{
TFileHandle file(tmp.Name(), OpenExisting | RdOnly);
char buf[2];
Y_ENSURE(file.Read(buf, 2) == 2);
Y_ENSURE(TStringBuf(buf, 2) == "12");
}
}
} // Y_UNIT_TEST_SUITE(TTestFileHandle)
Y_UNIT_TEST_SUITE(TTestDecodeOpenMode) {
Y_UNIT_TEST(It) {
UNIT_ASSERT_VALUES_EQUAL("0", DecodeOpenMode(0));
UNIT_ASSERT_VALUES_EQUAL("RdOnly", DecodeOpenMode(RdOnly));
UNIT_ASSERT_VALUES_EQUAL("RdWr", DecodeOpenMode(RdWr));
UNIT_ASSERT_VALUES_EQUAL("WrOnly|ForAppend", DecodeOpenMode(WrOnly | ForAppend));
UNIT_ASSERT_VALUES_EQUAL("RdWr|CreateAlways|CreateNew|ForAppend|Transient|CloseOnExec|Temp|Sync|Direct|DirectAligned|Seq|NoReuse|NoReadAhead|AX|AR|AW|AWOther|0xF8888000", DecodeOpenMode(0xFFFFFFFF));
}
} // Y_UNIT_TEST_SUITE(TTestDecodeOpenMode)