aboutsummaryrefslogtreecommitdiffstats
path: root/util/system/direct_io_ut.cpp
blob: 839c3de7ca4b49e67cee995e3329691ea8853fc5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <library/cpp/testing/unittest/registar.h>

#include <util/generic/yexception.h>
#include <util/system/fs.h>
#include <util/system/tempfile.h>
#include <util/random/random.h>

#include "direct_io.h"

static const char* FileName_("./test.file");

Y_UNIT_TEST_SUITE(TDirectIoTestSuite) {
    Y_UNIT_TEST(TestDirectFile) {
        TDirectIOBufferedFile file(FileName_, RdWr | Direct | Seq | CreateAlways, 1 << 15);
        TVector<ui64> data((1 << 15) + 1);
        TVector<ui64> readResult(data.size());
        for (auto& i : data) {
            i = RandomNumber<ui64>();
        }
        for (size_t writePos = 0; writePos < data.size();) {
            size_t writeCount = Min<size_t>(1 + RandomNumber<size_t>(1 << 10), data.ysize() - writePos);
            file.Write(&data[writePos], sizeof(ui64) * writeCount);
            writePos += writeCount;
            size_t readPos = RandomNumber(writePos);
            size_t readCount = RandomNumber(writePos - readPos);
            UNIT_ASSERT_VALUES_EQUAL(
                file.Pread(&readResult[0], readCount * sizeof(ui64), readPos * sizeof(ui64)),
                readCount * sizeof(ui64));
            for (size_t i = 0; i < readCount; ++i) {
                UNIT_ASSERT_VALUES_EQUAL(readResult[i], data[i + readPos]);
            }
        }
        file.Finish();
        TDirectIOBufferedFile fileNew(FileName_, RdOnly | Direct | Seq | OpenAlways, 1 << 15);
        for (int i = 0; i < 1000; ++i) {
            size_t readPos = RandomNumber(data.size());
            size_t readCount = RandomNumber(data.size() - readPos);
            UNIT_ASSERT_VALUES_EQUAL(
                fileNew.Pread(&readResult[0], readCount * sizeof(ui64), readPos * sizeof(ui64)),
                readCount * sizeof(ui64));
            for (size_t j = 0; j < readCount; ++j) {
                UNIT_ASSERT_VALUES_EQUAL(readResult[j], data[j + readPos]);
            }
        }
        size_t readCount = data.size();
        UNIT_ASSERT_VALUES_EQUAL(
            fileNew.Pread(&readResult[0], readCount * sizeof(ui64), 0),
            readCount * sizeof(ui64));
        for (size_t i = 0; i < readCount; ++i) {
            UNIT_ASSERT_VALUES_EQUAL(readResult[i], data[i]);
        }
        NFs::Remove(FileName_);
    }

    void TestHugeFile(size_t size) {
        TTempFile tmpFile("test.file");

        {
            TDirectIOBufferedFile directIOFile(tmpFile.Name(), WrOnly | CreateAlways | Direct);
            TVector<ui8> data(size, 'x');
            directIOFile.Write(&data[0], data.size());
        }

        {
            TDirectIOBufferedFile directIOFile(tmpFile.Name(), RdOnly | Direct);
            TVector<ui8> data(size + 1, 'y');

            const size_t readResult = directIOFile.Read(&data[0], data.size());

            UNIT_ASSERT_VALUES_EQUAL(readResult, size);

            UNIT_ASSERT_VALUES_EQUAL(data[0], 'x');
            UNIT_ASSERT_VALUES_EQUAL(data[size / 2], 'x');
            UNIT_ASSERT_VALUES_EQUAL(data[size - 1], 'x');
            UNIT_ASSERT_VALUES_EQUAL(data[size], 'y');
        }
    }

    Y_UNIT_TEST(TestHugeFile1) {
        if constexpr (sizeof(size_t) > 4) {
            TestHugeFile(5 * 1024 * 1024 * 1024ULL);
        }
    }
    Y_UNIT_TEST(TestHugeFile2) {
        if constexpr (sizeof(size_t) > 4) {
            TestHugeFile(5 * 1024 * 1024 * 1024ULL + 1111);
        }
    }
}

Y_UNIT_TEST_SUITE(TDirectIoErrorHandling) {
    Y_UNIT_TEST(Constructor) {
        // A non-existent file should not be opened for reading
        UNIT_ASSERT_EXCEPTION(TDirectIOBufferedFile(FileName_, RdOnly, 1 << 15), TFileError);
    }

    Y_UNIT_TEST(WritingReadOnlyFileBufferFlushed) {
        // Note the absence of Direct
        TDirectIOBufferedFile file(FileName_, RdOnly | OpenAlways, 1);
        TString buffer = "Hello";
        UNIT_ASSERT_EXCEPTION(file.Write(buffer.data(), buffer.size()), TFileError);
        NFs::Remove(FileName_);
    }

    Y_UNIT_TEST(WritingReadOnlyFileAllInBuffer) {
        TDirectIOBufferedFile file(FileName_, RdOnly | Direct | Seq | OpenAlways, 1 << 15);
        TString buffer = "Hello";

        // Doesn't throw because of buffering.
        file.Write(buffer.data(), buffer.size());

        UNIT_ASSERT_EXCEPTION(file.Finish(), TFileError);
        NFs::Remove(FileName_);
    }
}