1//===-- Unittests for file operations like fopen, flcose etc --------------===//
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 "src/stdio/clearerr.h"
10#include "src/stdio/fclose.h"
11#include "src/stdio/feof.h"
12#include "src/stdio/ferror.h"
13#include "src/stdio/fflush.h"
14#include "src/stdio/fileno.h"
15#include "src/stdio/fopen.h"
16#include "src/stdio/fputs.h"
17#include "src/stdio/fread.h"
18#include "src/stdio/fseek.h"
19#include "src/stdio/fwrite.h"
20#include "test/UnitTest/ErrnoCheckingTest.h"
21#include "test/UnitTest/ErrnoSetterMatcher.h"
22#include "test/UnitTest/Test.h"
23
24#include "hdr/stdio_macros.h"
25
26using LlvmLibcFILETest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
27using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::EQ;
28using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::NE;
29using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::returns;
30
31TEST_F(LlvmLibcFILETest, SimpleFileOperations) {
32 constexpr char FILENAME[] = "testdata/simple_operations.test";
33 ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
34 ASSERT_FALSE(file == nullptr);
35 ASSERT_GE(LIBC_NAMESPACE::fileno(file), 0);
36 constexpr char CONTENT[] = "1234567890987654321";
37 ASSERT_EQ(sizeof(CONTENT) - 1,
38 LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT) - 1, file));
39
40 // This is not a readable file.
41 char read_data[sizeof(CONTENT)];
42 ASSERT_THAT(LIBC_NAMESPACE::fread(read_data, 1, sizeof(CONTENT), file),
43 returns(EQ(size_t(0))).with_errno(NE(0)));
44 ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
45
46 LIBC_NAMESPACE::clearerr(file);
47 ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
48
49 ASSERT_EQ(0, LIBC_NAMESPACE::fclose(file));
50
51 file = LIBC_NAMESPACE::fopen(FILENAME, "r");
52 ASSERT_FALSE(file == nullptr);
53
54 constexpr size_t READ_SIZE = 5;
55 char data[READ_SIZE];
56 data[READ_SIZE - 1] = '\0';
57 ASSERT_EQ(LIBC_NAMESPACE::fread(data, 1, READ_SIZE - 1, file), READ_SIZE - 1);
58 ASSERT_STREQ(data, "1234");
59 ASSERT_EQ(LIBC_NAMESPACE::fseek(file, 5, SEEK_CUR), 0);
60 ASSERT_EQ(LIBC_NAMESPACE::fread(data, 1, READ_SIZE - 1, file), READ_SIZE - 1);
61 ASSERT_STREQ(data, "0987");
62 ASSERT_EQ(LIBC_NAMESPACE::fseek(file, -5, SEEK_CUR), 0);
63 ASSERT_EQ(LIBC_NAMESPACE::fread(data, 1, READ_SIZE - 1, file), READ_SIZE - 1);
64 ASSERT_STREQ(data, "9098");
65
66 // Reading another time should trigger eof.
67 ASSERT_NE(sizeof(CONTENT),
68 LIBC_NAMESPACE::fread(read_data, 1, sizeof(CONTENT), file));
69 ASSERT_NE(LIBC_NAMESPACE::feof(file), 0);
70
71 // Should be an error to write.
72 ASSERT_THAT(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), file),
73 returns(EQ(size_t(0))).with_errno(NE(0)));
74 ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
75
76 LIBC_NAMESPACE::clearerr(file);
77
78 // Should be an error to puts.
79 ASSERT_THAT(LIBC_NAMESPACE::fputs(CONTENT, file),
80 returns(EQ(EOF)).with_errno(NE(0)));
81 ASSERT_NE(LIBC_NAMESPACE::ferror(file), 0);
82
83 LIBC_NAMESPACE::clearerr(file);
84 ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
85
86 ASSERT_THAT(LIBC_NAMESPACE::fwrite("nothing", 1, 1, file),
87 returns(EQ(size_t(0))).with_errno(NE(0)));
88
89 ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
90
91 // Now try puts.
92 file = LIBC_NAMESPACE::fopen(FILENAME, "w");
93 ASSERT_FALSE(file == nullptr);
94 // fputs returns a negative value on error (EOF) or any non-negative value on
95 // success. This assert checks that the return value is non-negative.
96 ASSERT_GE(LIBC_NAMESPACE::fputs(CONTENT, file), 0);
97
98 LIBC_NAMESPACE::clearerr(file);
99 ASSERT_EQ(LIBC_NAMESPACE::ferror(file), 0);
100
101 // This is not a readable file.
102 ASSERT_THAT(LIBC_NAMESPACE::fread(data, 1, 1, file),
103 returns(EQ(0)).with_errno(NE(0)));
104
105 ASSERT_EQ(0, LIBC_NAMESPACE::fclose(file));
106
107 file = LIBC_NAMESPACE::fopen(FILENAME, "r");
108 ASSERT_FALSE(file == nullptr);
109
110 ASSERT_EQ(LIBC_NAMESPACE::fread(read_data, 1, sizeof(CONTENT) - 1, file),
111 sizeof(CONTENT) - 1);
112 read_data[sizeof(CONTENT) - 1] = '\0';
113 ASSERT_STREQ(read_data, CONTENT);
114 ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
115
116 // Check that the other functions correctly set libc_errno.
117
118 // ASSERT_NE(LIBC_NAMESPACE::fseek(file, 0, SEEK_SET), 0);
119 // ASSERT_ERRNO_FAILURE();
120
121 // ASSERT_NE(LIBC_NAMESPACE::fclose(file), 0);
122 // ASSERT_ERRNO_FAILURE();
123
124 // ASSERT_EQ(LIBC_NAMESPACE::fopen("INVALID FILE NAME", "r"),
125 // static_cast<FILE *>(nullptr));
126 // ASSERT_ERRNO_FAILURE();
127}
128
129TEST_F(LlvmLibcFILETest, FFlush) {
130 constexpr char FILENAME[] = "testdata/fflush.test";
131 ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w+");
132 ASSERT_FALSE(file == nullptr);
133 constexpr char CONTENT[] = "1234567890987654321";
134 ASSERT_EQ(sizeof(CONTENT),
135 LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), file));
136
137 // Flushing at this point should write the data to disk. So, we should be
138 // able to read it back.
139 ASSERT_EQ(0, LIBC_NAMESPACE::fflush(file));
140
141 char data[sizeof(CONTENT)];
142 ASSERT_EQ(LIBC_NAMESPACE::fseek(file, 0, SEEK_SET), 0);
143 ASSERT_EQ(LIBC_NAMESPACE::fread(data, 1, sizeof(CONTENT), file),
144 sizeof(CONTENT));
145 ASSERT_STREQ(data, CONTENT);
146
147 ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
148}
149
150TEST_F(LlvmLibcFILETest, FOpenFWriteSizeGreaterThanOne) {
151 using MyStruct = struct {
152 char c;
153 unsigned long long i;
154 };
155 constexpr MyStruct WRITE_DATA[] = {{.c: 'a', .i: 1}, {.c: 'b', .i: 2}, {.c: 'c', .i: 3}};
156 constexpr size_t WRITE_NMEMB = sizeof(WRITE_DATA) / sizeof(MyStruct);
157 constexpr char FILENAME[] = "testdata/fread_fwrite.test";
158
159 FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
160 ASSERT_FALSE(file == nullptr);
161 ASSERT_EQ(size_t(0), LIBC_NAMESPACE::fwrite(WRITE_DATA, 0, 1, file));
162 ASSERT_THAT(
163 LIBC_NAMESPACE::fwrite(WRITE_DATA, sizeof(MyStruct), WRITE_NMEMB, file),
164 returns(EQ(WRITE_NMEMB)).with_errno(EQ(0)));
165 ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
166
167 file = LIBC_NAMESPACE::fopen(FILENAME, "r");
168 ASSERT_FALSE(file == nullptr);
169 MyStruct read_data[WRITE_NMEMB];
170 ASSERT_EQ(size_t(0), LIBC_NAMESPACE::fread(read_data, 0, 1, file));
171 ASSERT_THAT(
172 LIBC_NAMESPACE::fread(read_data, sizeof(MyStruct), WRITE_NMEMB, file),
173 returns(EQ(WRITE_NMEMB)).with_errno(EQ(0)));
174 // Trying to read more should fetch nothing.
175 ASSERT_THAT(
176 LIBC_NAMESPACE::fread(read_data, sizeof(MyStruct), WRITE_NMEMB, file),
177 returns(EQ(0)).with_errno(EQ(0)));
178 EXPECT_NE(LIBC_NAMESPACE::feof(file), 0);
179 EXPECT_EQ(LIBC_NAMESPACE::ferror(file), 0);
180 ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
181 // Verify that the data which was read is correct.
182 for (size_t i = 0; i < WRITE_NMEMB; ++i) {
183 ASSERT_EQ(read_data[i].c, WRITE_DATA[i].c);
184 ASSERT_EQ(read_data[i].i, WRITE_DATA[i].i);
185 }
186}
187

source code of libc/test/src/stdio/fileop_test.cpp