#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)