1 | //===- llvm/unittest/Support/FileOutputBuffer.cpp - unit tests ------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/Support/FileOutputBuffer.h" |
10 | #include "llvm/Support/Errc.h" |
11 | #include "llvm/Support/ErrorHandling.h" |
12 | #include "llvm/Support/FileSystem.h" |
13 | #include "llvm/Support/MemoryBuffer.h" |
14 | #include "llvm/Support/Path.h" |
15 | #include "llvm/Support/raw_ostream.h" |
16 | #include "gtest/gtest.h" |
17 | |
18 | using namespace llvm; |
19 | using namespace llvm::sys; |
20 | |
21 | #define ASSERT_NO_ERROR(x) \ |
22 | if (std::error_code ASSERT_NO_ERROR_ec = x) { \ |
23 | SmallString<128> MessageStorage; \ |
24 | raw_svector_ostream Message(MessageStorage); \ |
25 | Message << #x ": did not return errc::success.\n" \ |
26 | << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \ |
27 | << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \ |
28 | GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \ |
29 | } else { \ |
30 | } |
31 | |
32 | namespace { |
33 | TEST(FileOutputBuffer, Test) { |
34 | // Create unique temporary directory for these tests |
35 | SmallString<128> TestDirectory; |
36 | { |
37 | ASSERT_NO_ERROR( |
38 | fs::createUniqueDirectory("FileOutputBuffer-test" , TestDirectory)); |
39 | } |
40 | |
41 | // TEST 1: Verify commit case. |
42 | SmallString<128> File1(TestDirectory); |
43 | File1.append(RHS: "/file1" ); |
44 | { |
45 | Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = |
46 | FileOutputBuffer::create(FilePath: File1, Size: 8192); |
47 | ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError())); |
48 | std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr; |
49 | // Start buffer with special header. |
50 | memcpy(dest: Buffer->getBufferStart(), src: "AABBCCDDEEFFGGHHIIJJ" , n: 20); |
51 | // Write to end of buffer to verify it is writable. |
52 | memcpy(dest: Buffer->getBufferEnd() - 20, src: "AABBCCDDEEFFGGHHIIJJ" , n: 20); |
53 | // Commit buffer. |
54 | ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit())); |
55 | } |
56 | |
57 | // Verify file is correct size. |
58 | uint64_t File1Size; |
59 | ASSERT_NO_ERROR(fs::file_size(Twine(File1), File1Size)); |
60 | ASSERT_EQ(File1Size, 8192ULL); |
61 | ASSERT_NO_ERROR(fs::remove(File1.str())); |
62 | |
63 | // TEST 2: Verify abort case. |
64 | SmallString<128> File2(TestDirectory); |
65 | File2.append(RHS: "/file2" ); |
66 | { |
67 | Expected<std::unique_ptr<FileOutputBuffer>> Buffer2OrErr = |
68 | FileOutputBuffer::create(FilePath: File2, Size: 8192); |
69 | ASSERT_NO_ERROR(errorToErrorCode(Buffer2OrErr.takeError())); |
70 | std::unique_ptr<FileOutputBuffer> &Buffer2 = *Buffer2OrErr; |
71 | // Fill buffer with special header. |
72 | memcpy(dest: Buffer2->getBufferStart(), src: "AABBCCDDEEFFGGHHIIJJ" , n: 20); |
73 | // Do *not* commit buffer. |
74 | } |
75 | // Verify file does not exist (because buffer not committed). |
76 | ASSERT_EQ(fs::access(Twine(File2), fs::AccessMode::Exist), |
77 | errc::no_such_file_or_directory); |
78 | ASSERT_NO_ERROR(fs::remove(File2.str())); |
79 | |
80 | // TEST 3: Verify sizing down case. |
81 | SmallString<128> File3(TestDirectory); |
82 | File3.append(RHS: "/file3" ); |
83 | { |
84 | Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = |
85 | FileOutputBuffer::create(FilePath: File3, Size: 8192000); |
86 | ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError())); |
87 | std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr; |
88 | // Start buffer with special header. |
89 | memcpy(dest: Buffer->getBufferStart(), src: "AABBCCDDEEFFGGHHIIJJ" , n: 20); |
90 | // Write to end of buffer to verify it is writable. |
91 | memcpy(dest: Buffer->getBufferEnd() - 20, src: "AABBCCDDEEFFGGHHIIJJ" , n: 20); |
92 | ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit())); |
93 | } |
94 | |
95 | // Verify file is correct size. |
96 | uint64_t File3Size; |
97 | ASSERT_NO_ERROR(fs::file_size(Twine(File3), File3Size)); |
98 | ASSERT_EQ(File3Size, 8192000ULL); |
99 | ASSERT_NO_ERROR(fs::remove(File3.str())); |
100 | |
101 | // TEST 4: Verify file can be made executable. |
102 | SmallString<128> File4(TestDirectory); |
103 | File4.append(RHS: "/file4" ); |
104 | { |
105 | Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = |
106 | FileOutputBuffer::create(FilePath: File4, Size: 8192, Flags: FileOutputBuffer::F_executable); |
107 | ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError())); |
108 | std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr; |
109 | // Start buffer with special header. |
110 | memcpy(dest: Buffer->getBufferStart(), src: "AABBCCDDEEFFGGHHIIJJ" , n: 20); |
111 | // Commit buffer. |
112 | ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit())); |
113 | } |
114 | // Verify file exists and is executable. |
115 | fs::file_status Status; |
116 | ASSERT_NO_ERROR(fs::status(Twine(File4), Status)); |
117 | bool IsExecutable = (Status.permissions() & fs::owner_exe); |
118 | EXPECT_TRUE(IsExecutable); |
119 | ASSERT_NO_ERROR(fs::remove(File4.str())); |
120 | |
121 | // TEST 5: In-memory buffer works as expected. |
122 | SmallString<128> File5(TestDirectory); |
123 | File5.append(RHS: "/file5" ); |
124 | { |
125 | Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = |
126 | FileOutputBuffer::create(FilePath: File5, Size: 8000, Flags: FileOutputBuffer::F_no_mmap); |
127 | ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError())); |
128 | std::unique_ptr<FileOutputBuffer> &Buffer = *BufferOrErr; |
129 | // Start buffer with special header. |
130 | memcpy(dest: Buffer->getBufferStart(), src: "AABBCCDDEEFFGGHHIIJJ" , n: 20); |
131 | ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit())); |
132 | // Write to end of buffer to verify it is writable. |
133 | memcpy(dest: Buffer->getBufferEnd() - 20, src: "AABBCCDDEEFFGGHHIIJJ" , n: 20); |
134 | // Commit buffer. |
135 | ASSERT_NO_ERROR(errorToErrorCode(Buffer->commit())); |
136 | } |
137 | |
138 | // Verify file is correct size. |
139 | uint64_t File5Size; |
140 | ASSERT_NO_ERROR(fs::file_size(Twine(File5), File5Size)); |
141 | ASSERT_EQ(File5Size, 8000ULL); |
142 | ASSERT_NO_ERROR(fs::remove(File5.str())); |
143 | |
144 | // TEST 6: Create an empty file. |
145 | SmallString<128> File6(TestDirectory); |
146 | File6.append(RHS: "/file6" ); |
147 | { |
148 | Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = |
149 | FileOutputBuffer::create(FilePath: File6, Size: 0); |
150 | ASSERT_NO_ERROR(errorToErrorCode(BufferOrErr.takeError())); |
151 | ASSERT_NO_ERROR(errorToErrorCode((*BufferOrErr)->commit())); |
152 | } |
153 | uint64_t File6Size; |
154 | ASSERT_NO_ERROR(fs::file_size(Twine(File6), File6Size)); |
155 | ASSERT_EQ(File6Size, 0ULL); |
156 | ASSERT_NO_ERROR(fs::remove(File6.str())); |
157 | |
158 | // Clean up. |
159 | ASSERT_NO_ERROR(fs::remove(TestDirectory.str())); |
160 | } |
161 | } // anonymous namespace |
162 | |