// This #include is actually a test too. We have to duplicate the // definitions of the RENAME_ constants because <linux/fs.h> also contains // pollution such as BLOCK_SIZE which conflicts with lots of user code. // Important to check that we have matching definitions. // There's no _MAX to test that we have all the constants, sadly. #include <linux/fs.h>
if (is_fmemopen) { // fmemopen appends a trailing NUL byte, which probably shouldn't show up as an // extra empty line, but does on every C library I tested...
ASSERT_EQ(line, fgets(line, sizeof(line), fp));
ASSERT_STREQ("", line);
}
// Make sure there isn't anything else in the file.
ASSERT_EQ(nullptr, fgets(line, sizeof(line), fp)) << "junk at end of file: " << line;
}
#define EXPECT_SNPRINTF_N(expected, n, fmt, ...) \
{ \ char buf[BUFSIZ]; \ int w = snprintf(buf, sizeof(buf), fmt __VA_OPT__(, ) __VA_ARGS__); \
EXPECT_EQ(n, w); \
EXPECT_STREQ(expected, buf); \
}
TEST(STDIO_TEST, flockfile_18208568_stderr) {
flockfile(stderr); // Check that we're using a _recursive_ mutex for flockfile() by calling // something that will take the lock.
ASSERT_EQ(0, feof(stderr));
funlockfile(stderr);
}
TEST(STDIO_TEST, flockfile_18208568_ftrylock) { // Same test but for ftrylockfile().
ASSERT_EQ(0, ftrylockfile(stderr));
ASSERT_EQ(0, feof(stderr));
funlockfile(stderr);
}
TEST(STDIO_TEST, flockfile_18208568_regular) { // We never had a bug for streams other than stdin/stdout/stderr, but test anyway.
FILE* fp = fopen("/dev/null", "w");
ASSERT_TRUE(fp != nullptr);
flockfile(fp); // Check that we're using a _recursive_ mutex for flockfile() by calling // something that will take the lock.
ASSERT_EQ(0, feof(fp));
funlockfile(fp);
fclose(fp);
}
TEST(STDIO_TEST, ftrylockfile) {
FILE* fp = fopen("/dev/null", "w"); // If we lock it on this thread...
ASSERT_EQ(0, ftrylockfile(fp));
std::thread([=] { // ...we can't lock it on another thread.
ASSERT_EQ(EBUSY, ftrylockfile(fp));
}).join();
constchar* expected[] = { "This ", " ", "is ", "a ", "test" }; for (size_t i = 0; i < 5; ++i) {
ASSERT_FALSE(feof(fp));
ASSERT_EQ(getdelim(&word_read, &allocated_length, ' ', fp), static_cast<int>(strlen(expected[i])));
ASSERT_GE(allocated_length, strlen(expected[i]));
ASSERT_STREQ(expected[i], word_read);
} // The last read should have set the end-of-file indicator for the stream.
ASSERT_TRUE(feof(fp));
clearerr(fp);
// getdelim returns -1 but doesn't set errno if we're already at EOF. // It should set the end-of-file indicator for the stream, though.
ASSERT_ERRNO_FAILURE(0, -1, getdelim(&word_read, &allocated_length, ' ', fp));
ASSERT_TRUE(feof(fp));
std::vector<std::string> lines; for (size_t i = 0; i < 5; ++i) {
lines.push_back(android::base::StringPrintf("This is test line %zu for fgetln()\n", i));
}
for (size_t i = 0; i < lines.size(); ++i) { int rc = fprintf(fp, "%s", lines[i].c_str());
ASSERT_EQ(rc, static_cast<int>(lines[i].size()));
}
rewind(fp);
size_t i = 0; char* line = nullptr;
size_t line_length; while ((line = fgetln(fp, &line_length)) != nullptr) {
ASSERT_EQ(line_length, lines[i].size()); // "lines" aren't necessarily NUL-terminated, so we need to use memcmp().
ASSERT_EQ(0, memcmp(lines[i].c_str(), line, line_length));
++i;
}
ASSERT_EQ(i, lines.size());
// The last read should have set the end-of-file indicator for the stream.
ASSERT_TRUE(feof(fp));
clearerr(fp);
// fgetln() returns nullptr but doesn't set errno if we're already at EOF. // It should set the end-of-file indicator for the stream, though.
ASSERT_ERRNO_FAILURE(0, nullptr, fgetln(fp, &line_length));
ASSERT_TRUE(feof(fp));
fclose(fp); #else
GTEST_SKIP() << "no fgetln() in glibc"; #endif
}
std::vector<std::string> lines; for (size_t i = 0; i < 5; ++i) {
lines.push_back(android::base::StringPrintf("This is test line %zu for getline()\n", i));
}
for (size_t i = 0; i < lines.size(); ++i) { int rc = fprintf(fp, "%s", lines[i].c_str());
ASSERT_EQ(rc, static_cast<int>(lines[i].size()));
}
// The last read should have set the end-of-file indicator for the stream.
ASSERT_TRUE(feof(fp));
clearerr(fp);
// getline returns -1 but doesn't set errno if we're already at EOF. // It should set the end-of-file indicator for the stream, though.
ASSERT_ERRNO_FAILURE(0, -1, getline(&line_read, &allocated_length, fp));
ASSERT_TRUE(feof(fp));
// The first argument can't be NULL.
ASSERT_ERRNO_FAILURE(EINVAL, -1, getline(nullptr, &buffer_length, fp));
// The second argument can't be NULL.
ASSERT_ERRNO_FAILURE(EINVAL, -1, getline(&buffer, nullptr, fp));
fclose(fp); #pragma clang diagnostic pop
}
TEST(STDIO_TEST, printf_ssize_t) { // http://b/8253769
ASSERT_EQ(sizeof(ssize_t), sizeof(longint));
ASSERT_EQ(sizeof(ssize_t), sizeof(size_t)); // For our 32-bit ABI, we had a ssize_t definition that confuses GCC into saying: // error: format '%zd' expects argument of type 'signed size_t', // but argument 4 has type 'ssize_t {aka long int}' [-Werror=format]
ssize_t v = 1;
EXPECT_SNPRINTF("1", "%zd", v);
EXPECT_SWPRINTF(L"1", L"%zd", v);
}
staticvoid* snprintf_small_stack_fn(void*) { // Make life (realistically) hard for ourselves by allocating our own buffer for the result. char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "/proc/%d", getpid()); return nullptr;
}
TEST(STDIO_TEST, snprintf_small_stack) { // Is it safe to call snprintf on a thread with a small stack? // (The snprintf implementation puts some pretty large buffers on the stack.)
pthread_attr_t a;
ASSERT_EQ(0, pthread_attr_init(&a));
ASSERT_EQ(0, pthread_attr_setstacksize(&a, PTHREAD_STACK_MIN));
// Unbuffered case where the fprintf(3) itself fails.
ASSERT_NE(nullptr, fp = tmpfile());
setbuf(fp, nullptr);
ASSERT_EQ(4, fprintf(fp, "epic"));
ASSERT_NE(-1, dup2(fd_rdonly, fileno(fp)));
ASSERT_EQ(-1, fprintf(fp, "fail"));
ASSERT_EQ(0, fclose(fp));
// Buffered case where we won't notice until the fclose(3). // It's likely this is what was actually seen in http://b/7229520, // and that expecting fprintf to fail is setting yourself up for // disappointment. Remember to check fclose(3)'s return value, kids!
ASSERT_NE(nullptr, fp = tmpfile());
ASSERT_EQ(4, fprintf(fp, "epic"));
ASSERT_NE(-1, dup2(fd_rdonly, fileno(fp)));
ASSERT_EQ(4, fprintf(fp, "fail"));
ASSERT_EQ(-1, fclose(fp));
}
TEST(STDIO_TEST, popen_return_value_0) {
FILE* fp = popen("true", "r");
ASSERT_TRUE(fp != nullptr); int status = pclose(fp);
EXPECT_TRUE(WIFEXITED(status));
EXPECT_EQ(0, WEXITSTATUS(status));
}
TEST(STDIO_TEST, popen_return_value_1) {
FILE* fp = popen("false", "r");
ASSERT_TRUE(fp != nullptr); int status = pclose(fp);
EXPECT_TRUE(WIFEXITED(status));
EXPECT_EQ(1, WEXITSTATUS(status));
}
TEST(STDIO_TEST, popen_return_value_signal) { // Use a realtime signal to avoid creating a tombstone when running.
std::string cmd = android::base::StringPrintf("kill -%d $$", SIGRTMIN);
FILE* fp = popen(cmd.c_str(), "r");
ASSERT_TRUE(fp != nullptr); int status = pclose(fp);
EXPECT_TRUE(WIFSIGNALED(status));
EXPECT_EQ(SIGRTMIN, WTERMSIG(status));
}
TEST(STDIO_TEST, sscanf_ccl) { // `abc` is just those characters.
CheckScanf(sscanf, "abcd", "%[abc]", 1, "abc"); // `a-c` is the range 'a' .. 'c'.
CheckScanf(sscanf, "abcd", "%[a-c]", 1, "abc");
CheckScanf(sscanf, "-d", "%[a-c]", 0, "");
CheckScanf(sscanf, "ac-bAd", "%[a--c]", 1, "ac-bA"); // `a-c-e` is equivalent to `a-e`.
CheckScanf(sscanf, "abcdefg", "%[a-c-e]", 1, "abcde"); // `e-a` is equivalent to `ae-` (because 'e' > 'a').
CheckScanf(sscanf, "-a-e-b", "%[e-a]", 1, "-a-e-"); // An initial '^' negates the set.
CheckScanf(sscanf, "abcde", "%[^d]", 1, "abc");
CheckScanf(sscanf, "abcdefgh", "%[^c-d]", 1, "ab");
CheckScanf(sscanf, "hgfedcba", "%[^c-d]", 1, "hgfe"); // The first character may be ']' or '-' without being special.
CheckScanf(sscanf, "[[]]x", "%[][]", 1, "[[]]");
CheckScanf(sscanf, "-a-x", "%[-a]", 1, "-a-"); // The last character may be '-' without being special.
CheckScanf(sscanf, "-a-x", "%[a-]", 1, "-a-"); // X--Y is [X--] + Y, not [X--] + [--Y] (a bug in my initial implementation).
CheckScanf(sscanf, "+,-/.", "%[+--/]", 1, "+,-/");
}
TEST(STDIO_TEST, swscanf_ccl) { // `abc` is just those characters.
CheckScanf(swscanf, L"abcd", L"%[abc]", 1, "abc"); // `a-c` is the range 'a' .. 'c'.
CheckScanf(swscanf, L"abcd", L"%[a-c]", 1, "abc");
CheckScanf(swscanf, L"-d", L"%[a-c]", 0, "");
CheckScanf(swscanf, L"ac-bAd", L"%[a--c]", 1, "ac-bA"); // `a-c-e` is equivalent to `a-e`.
CheckScanf(swscanf, L"abcdefg", L"%[a-c-e]", 1, "abcde"); // `e-a` is equivalent to `ae-` (because 'e' > 'a').
CheckScanf(swscanf, L"-a-e-b", L"%[e-a]", 1, "-a-e-"); // An initial '^' negates the set.
CheckScanf(swscanf, L"abcde", L"%[^d]", 1, "abc");
CheckScanf(swscanf, L"abcdefgh", L"%[^c-d]", 1, "ab");
CheckScanf(swscanf, L"hgfedcba", L"%[^c-d]", 1, "hgfe"); // The first character may be ']' or '-' without being special.
CheckScanf(swscanf, L"[[]]x", L"%[][]", 1, "[[]]");
CheckScanf(swscanf, L"-a-x", L"%[-a]", 1, "-a-"); // The last character may be '-' without being special.
CheckScanf(swscanf, L"-a-x", L"%[a-]", 1, "-a-"); // X--Y is [X--] + Y, not [X--] + [--Y] (a bug in my initial implementation).
CheckScanf(swscanf, L"+,-/.", L"%[+--/]", 1, "+,-/");
}
TEST(STDIO_TEST, sscanf_mlc) { // This is so useless that clang doesn't even believe it exists... #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wformat-invalid-specifier" #pragma clang diagnostic ignored "-Wformat-extra-args"
TEST(STDIO_TEST, scanf_invalid_UTF8) { #if0// TODO: more tests invented during code review; no regressions, so fix later. char buf[BUFSIZ]; wchar_t wbuf[BUFSIZ];
TEST(STDIO_TEST, scanf_wscanf_wide_character_class) { #if0// TODO: more tests invented during code review; no regressions, so fix later. wchar_t buf[BUFSIZ];
// A wide character shouldn't match an ASCII-only class for scanf or wscanf.
memset(buf, 0, sizeof(buf));
EXPECT_EQ(1, sscanf("xĀyz", "%l[xy]", buf));
EXPECT_EQ(L"x"s, std::wstring(buf));
memset(buf, 0, sizeof(buf));
EXPECT_EQ(1, swscanf(L"xĀyz", L"%l[xy]", buf));
EXPECT_EQ(L"x"s, std::wstring(buf));
// Even if scanf has wide characters in a class, they won't match... // TODO: is that a bug?
memset(buf, 0, sizeof(buf));
EXPECT_EQ(1, sscanf("xĀyz", "%l[xĀy]", buf));
EXPECT_EQ(L"x"s, std::wstring(buf)); // ...unless you use wscanf.
memset(buf, 0, sizeof(buf));
EXPECT_EQ(1, swscanf(L"xĀyz", L"%l[xĀy]", buf));
EXPECT_EQ(L"xĀy"s, std::wstring(buf));
// We already determined that non-ASCII characters are ignored in scanf classes.
memset(buf, 0, sizeof(buf));
EXPECT_EQ(1, sscanf("x" "\xc4\x80"// Matches a byte from each wide char in the class. "\xc6\x82"// Neither byte is in the class. "yz", "%l[xy""\xc5\x80""\xc4\x81""]", buf));
EXPECT_EQ(L"x", std::wstring(buf)); // bionic and glibc both behave badly for wscanf, so let's call it right for now...
memset(buf, 0, sizeof(buf));
EXPECT_EQ(1, swscanf(L"x"
L"\xc4\x80"
L"\xc6\x82"
L"yz",
L"%l[xy" L"\xc5\x80" L"\xc4\x81" L"]", buf)); // Note that this isn't L"xĀ" --- although the *bytes* matched, they're // not put back together as a wide character.
EXPECT_EQ(L"x" L"\xc4" L"\x80", std::wstring(buf)); #endif
}
TEST(STDIO_TEST, cantwrite_EBADF) { // If we open a file read-only...
FILE* fp = fopen("/proc/version", "r");
// ...all attempts to write to that file should return failure.
// They should also set errno to EBADF. This isn't POSIX, but it's traditional.
// Tests that we can only have a consistent and correct fpos_t when using // f*pos functions (i.e. fpos doesn't get inside a multi byte character).
TEST(STDIO_TEST, consistent_fpos_t) {
ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
uselocale(LC_GLOBAL_LOCALE);
#ifdefined(__BIONIC__) // Bionic's fpos_t is just an alias for off_t. This is inherited from OpenBSD // upstream. Glibc differs by storing the mbstate_t inside its fpos_t. In // Bionic (and upstream OpenBSD) the mbstate_t is stored inside the FILE // structure.
ASSERT_EQ(0, static_cast<off_t>(pos1));
ASSERT_EQ(1, static_cast<off_t>(pos2));
ASSERT_EQ(3, static_cast<off_t>(pos3));
ASSERT_EQ(6, static_cast<off_t>(pos4));
ASSERT_EQ(10, static_cast<off_t>(pos5)); #endif
// Exercise back and forth movements of the position.
ASSERT_EQ(0, fsetpos(fp, &pos2));
ASSERT_EQ(mb_two_bytes, static_cast<wchar_t>(fgetwc(fp)));
ASSERT_EQ(0, fsetpos(fp, &pos1));
ASSERT_EQ(mb_one_bytes, static_cast<wchar_t>(fgetwc(fp)));
ASSERT_EQ(0, fsetpos(fp, &pos4));
ASSERT_EQ(mb_four_bytes, static_cast<wchar_t>(fgetwc(fp)));
ASSERT_EQ(0, fsetpos(fp, &pos3));
ASSERT_EQ(mb_three_bytes, static_cast<wchar_t>(fgetwc(fp)));
ASSERT_EQ(0, fsetpos(fp, &pos5));
ASSERT_EQ(WEOF, fgetwc(fp));
fclose(fp);
}
// Exercise the interaction between fpos and seek.
TEST(STDIO_TEST, fpos_t_and_seek) {
ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
uselocale(LC_GLOBAL_LOCALE);
// In glibc-2.16 fseek doesn't work properly in wide mode // (https://sourceware.org/bugzilla/show_bug.cgi?id=14543). One workaround is // to close and re-open the file. We do it in order to make the test pass // with all glibcs.
// Store a valid position.
fpos_t mb_two_bytes_pos;
ASSERT_EQ(0, fgetpos(fp, &mb_two_bytes_pos));
// Move inside mb_four_bytes with fseek. long offset_inside_mb = 6;
ASSERT_EQ(0, fseek(fp, offset_inside_mb, SEEK_SET));
// Store the "inside multi byte" position.
fpos_t pos_inside_mb;
ASSERT_EQ(0, fgetpos(fp, &pos_inside_mb)); #ifdefined(__BIONIC__)
ASSERT_EQ(offset_inside_mb, static_cast<off_t>(pos_inside_mb)); #endif
// Reading from within a byte should produce an error.
ASSERT_ERRNO_FAILURE(EILSEQ, WEOF, fgetwc(fp));
// Reverting to a valid position should work.
ASSERT_EQ(0, fsetpos(fp, &mb_two_bytes_pos));
ASSERT_EQ(mb_two_bytes, static_cast<wchar_t>(fgetwc(fp)));
// Moving withing a multi byte with fsetpos should work but reading should // produce an error.
ASSERT_EQ(0, fsetpos(fp, &pos_inside_mb));
ASSERT_ERRNO_FAILURE(EILSEQ, WEOF, fgetwc(fp));
// POSIX: "When a stream open for writing is flushed or closed, a null byte // shall be written at the current position or at the end of the buffer, // depending on the size of the contents."
memset(buf, 'x', sizeof(buf));
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "w")); // Even with nothing written (and not in truncate mode), we'll flush a NUL...
ASSERT_EQ(0, fflush(fp));
EXPECT_EQ("\0xxxxxxx"s, std::string(buf, buf + sizeof(buf))); // Now write and check that the NUL moves along with our writes...
ASSERT_NE(EOF, fputs("hello", fp));
ASSERT_EQ(0, fflush(fp));
EXPECT_EQ("hello\0xx"s, std::string(buf, buf + sizeof(buf)));
ASSERT_NE(EOF, fputs("wo", fp));
ASSERT_EQ(0, fflush(fp));
EXPECT_EQ("hellowo\0"s, std::string(buf, buf + sizeof(buf)));
ASSERT_EQ(0, fclose(fp));
// "If a stream open for update is flushed or closed and the last write has // advanced the current buffer size, a null byte shall be written at the end // of the buffer if it fits."
memset(buf, 'x', sizeof(buf));
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "r+")); // Nothing written yet, so no advance...
ASSERT_EQ(0, fflush(fp));
EXPECT_EQ("xxxxxxxx"s, std::string(buf, buf + sizeof(buf)));
ASSERT_NE(EOF, fputs("hello", fp));
ASSERT_EQ(0, fclose(fp));
}
// POSIX: "The stream shall also maintain the size of the current buffer // contents; use of fseek() or fseeko() on the stream with SEEK_END shall // seek relative to this size."
// "For modes r and r+ the size shall be set to the value given by the size // argument."
ASSERT_NE(nullptr, fp = fmemopen(buf, 16, "r"));
ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
EXPECT_EQ(16, ftell(fp));
EXPECT_EQ(16, ftello(fp));
ASSERT_EQ(0, fseeko(fp, 0, SEEK_END));
EXPECT_EQ(16, ftell(fp));
EXPECT_EQ(16, ftello(fp));
ASSERT_EQ(0, fclose(fp));
ASSERT_NE(nullptr, fp = fmemopen(buf, 16, "r+"));
ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
EXPECT_EQ(16, ftell(fp));
EXPECT_EQ(16, ftello(fp));
ASSERT_EQ(0, fseeko(fp, 0, SEEK_END));
EXPECT_EQ(16, ftell(fp));
EXPECT_EQ(16, ftello(fp));
ASSERT_EQ(0, fclose(fp));
// POSIX: "An attempt to seek ... to a negative position or to a position // larger than the buffer size given in the size argument shall fail." // (There's no mention of what errno should be set to, and glibc doesn't // set errno in any of these cases.)
EXPECT_EQ(-1, fseek(fp, -2, SEEK_SET));
EXPECT_EQ(-1, fseeko(fp, -2, SEEK_SET));
EXPECT_EQ(-1, fseek(fp, sizeof(buf) + 1, SEEK_SET));
EXPECT_EQ(-1, fseeko(fp, sizeof(buf) + 1, SEEK_SET));
}
TEST(STDIO_TEST, fmemopen_read_EOF) { // POSIX: "A read operation on the stream shall not advance the current // buffer position beyond the current buffer size." char buf[8];
memset(buf, 'x', sizeof(buf));
FILE* fp = fmemopen(buf, sizeof(buf), "r");
ASSERT_TRUE(fp != nullptr); char buf2[BUFSIZ];
ASSERT_EQ(8U, fread(buf2, 1, sizeof(buf2), fp)); // POSIX: "Reaching the buffer size in a read operation shall count as // end-of-file.
ASSERT_TRUE(feof(fp));
ASSERT_EQ(EOF, fgetc(fp));
ASSERT_EQ(0, fclose(fp));
}
TEST(STDIO_TEST, fmemopen_read_null_bytes) { // POSIX: "Null bytes in the buffer shall have no special meaning for reads." char buf[] = "h\0e\0l\0l\0o";
FILE* fp = fmemopen(buf, sizeof(buf), "r");
ASSERT_TRUE(fp != nullptr);
ASSERT_EQ('h', fgetc(fp));
ASSERT_EQ(0, fgetc(fp));
ASSERT_EQ('e', fgetc(fp));
ASSERT_EQ(0, fgetc(fp));
ASSERT_EQ('l', fgetc(fp));
ASSERT_EQ(0, fgetc(fp)); // POSIX: "The read operation shall start at the current buffer position of // the stream." char buf2[8];
memset(buf2, 'x', sizeof(buf2));
ASSERT_EQ(4U, fread(buf2, 1, sizeof(buf2), fp));
ASSERT_EQ('l', buf2[0]);
ASSERT_EQ(0, buf2[1]);
ASSERT_EQ('o', buf2[2]);
ASSERT_EQ(0, buf2[3]); for (size_t i = 4; i < sizeof(buf2); ++i) ASSERT_EQ('x', buf2[i]) << i;
ASSERT_TRUE(feof(fp));
ASSERT_EQ(0, fclose(fp));
}
// POSIX: "A write operation shall start either at the current position of // the stream (if mode has not specified 'a' as the first character)..."
memset(buf, 'x', sizeof(buf));
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "r+"));
setbuf(fp, nullptr); // Turn off buffering so we can see what's happening as it happens.
ASSERT_EQ(0, fseek(fp, 2, SEEK_SET));
ASSERT_EQ(' ', fputc(' ', fp));
EXPECT_EQ("xx xxxxx", std::string(buf, buf + sizeof(buf)));
ASSERT_EQ(0, fclose(fp));
// "...or at the current size of the stream (if mode had 'a' as the first // character)." (See the fmemopen_size test for what "size" means, but for // mode "a", it's the first NUL byte.)
memset(buf, 'x', sizeof(buf));
buf[3] = '\0';
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "a+"));
setbuf(fp, nullptr); // Turn off buffering so we can see what's happening as it happens.
ASSERT_EQ(' ', fputc(' ', fp));
EXPECT_EQ("xxx \0xxx"s, std::string(buf, buf + sizeof(buf)));
ASSERT_EQ(0, fclose(fp));
// "If the current position at the end of the write is larger than the // current buffer size, the current buffer size shall be set to the current // position." (See the fmemopen_size test for what "size" means, but to // query it we SEEK_END with offset 0, and then ftell.)
memset(buf, 'x', sizeof(buf));
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "w+"));
setbuf(fp, nullptr); // Turn off buffering so we can see what's happening as it happens.
ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
EXPECT_EQ(0, ftell(fp));
ASSERT_EQ(' ', fputc(' ', fp));
ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
EXPECT_EQ(1, ftell(fp));
ASSERT_NE(EOF, fputs("123", fp));
ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
EXPECT_EQ(4, ftell(fp));
EXPECT_EQ(" 123\0xxx"s, std::string(buf, buf + sizeof(buf)));
ASSERT_EQ(0, fclose(fp));
}
TEST(STDIO_TEST, fmemopen_write_EOF) { // POSIX: "A write operation on the stream shall not advance the current // buffer size beyond the size given in the size argument."
FILE* fp;
// Scalar writes...
ASSERT_NE(nullptr, fp = fmemopen(nullptr, 4, "w"));
setbuf(fp, nullptr); // Turn off buffering so we can see what's happening as it happens.
ASSERT_EQ('x', fputc('x', fp));
ASSERT_EQ('x', fputc('x', fp));
ASSERT_EQ('x', fputc('x', fp));
ASSERT_EQ(EOF, fputc('x', fp)); // Only 3 fit because of the implicit NUL.
ASSERT_EQ(0, fclose(fp));
// Vector writes...
ASSERT_NE(nullptr, fp = fmemopen(nullptr, 4, "w"));
setbuf(fp, nullptr); // Turn off buffering so we can see what's happening as it happens.
ASSERT_EQ(3U, fwrite("xxxx", 1, 4, fp));
ASSERT_EQ(0, fclose(fp));
}
TEST(STDIO_TEST, fmemopen_initial_position) { // POSIX: "The ... current position in the buffer ... shall be initially // set to either the beginning of the buffer (for r and w modes) ..." char buf[] = "hello\0world";
FILE* fp;
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "r"));
EXPECT_EQ(0L, ftell(fp));
EXPECT_EQ(0, fclose(fp));
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "w"));
EXPECT_EQ(0L, ftell(fp));
EXPECT_EQ(0, fclose(fp));
buf[0] = 'h'; // (Undo the effects of the above.)
// POSIX: "...or to the first null byte in the buffer (for a modes)."
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "a"));
EXPECT_EQ(5L, ftell(fp));
EXPECT_EQ(0, fclose(fp));
// POSIX: "If no null byte is found in append mode, the initial position // shall be set to one byte after the end of the buffer."
memset(buf, 'x', sizeof(buf));
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "a"));
EXPECT_EQ(static_cast<long>(sizeof(buf)), ftell(fp));
EXPECT_EQ(0, fclose(fp));
}
TEST(STDIO_TEST, fmemopen_initial_position_allocated) { // POSIX: "If buf is a null pointer, the initial position shall always be // set to the beginning of the buffer."
FILE* fp = fmemopen(nullptr, 128, "a+");
ASSERT_TRUE(fp != nullptr);
EXPECT_EQ(0L, ftell(fp));
EXPECT_EQ(0L, fseek(fp, 0, SEEK_SET));
EXPECT_EQ(0, fclose(fp));
}
TEST(STDIO_TEST, fmemopen_zero_length) { // POSIX says it's up to the implementation whether or not you can have a // zero-length buffer (but "A future version of this standard may require // support of zero-length buffer streams explicitly"). BSD and glibc < 2.22 // agreed that you couldn't, but glibc >= 2.22 allows it for consistency.
FILE* fp; char buf[16];
ASSERT_NE(nullptr, fp = fmemopen(buf, 0, "r+"));
ASSERT_EQ(EOF, fgetc(fp));
ASSERT_TRUE(feof(fp));
ASSERT_EQ(0, fclose(fp));
ASSERT_NE(nullptr, fp = fmemopen(nullptr, 0, "r+"));
ASSERT_EQ(EOF, fgetc(fp));
ASSERT_TRUE(feof(fp));
ASSERT_EQ(0, fclose(fp));
ASSERT_NE(nullptr, fp = fmemopen(buf, 0, "w+"));
setbuf(fp, nullptr); // Turn off buffering so we can see what's happening as it happens.
ASSERT_EQ(EOF, fputc('x', fp));
ASSERT_EQ(0, fclose(fp));
ASSERT_NE(nullptr, fp = fmemopen(nullptr, 0, "w+"));
setbuf(fp, nullptr); // Turn off buffering so we can see what's happening as it happens.
ASSERT_EQ(EOF, fputc('x', fp));
ASSERT_EQ(0, fclose(fp));
}
TEST(STDIO_TEST, fmemopen_write_only_allocated) { // POSIX says fmemopen "may fail if the mode argument does not include a '+'". // BSD fails, glibc doesn't. We side with the more lenient.
FILE* fp;
ASSERT_NE(nullptr, fp = fmemopen(nullptr, 16, "r"));
ASSERT_EQ(0, fclose(fp));
ASSERT_NE(nullptr, fp = fmemopen(nullptr, 16, "w"));
ASSERT_EQ(0, fclose(fp));
}
TEST(STDIO_TEST, fmemopen_append_after_seek) { // In BSD and glibc < 2.22, append mode didn't force writes to append if // there had been an intervening seek.
FILE* fp; char buf[] = "hello\0world";
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "a"));
setbuf(fp, nullptr); // Turn off buffering so we can see what's happening as it happens.
ASSERT_EQ(0, fseek(fp, 0, SEEK_SET));
ASSERT_NE(EOF, fputc('!', fp));
EXPECT_EQ("hello!\0orld\0"s, std::string(buf, buf + sizeof(buf)));
ASSERT_EQ(0, fclose(fp));
memcpy(buf, "hello\0world", sizeof(buf));
ASSERT_NE(nullptr, fp = fmemopen(buf, sizeof(buf), "a+"));
setbuf(fp, nullptr); // Turn off buffering so we can see what's happening as it happens.
ASSERT_EQ(0, fseek(fp, 0, SEEK_SET));
ASSERT_NE(EOF, fputc('!', fp));
EXPECT_EQ("hello!\0orld\0"s, std::string(buf, buf + sizeof(buf)));
ASSERT_EQ(0, fclose(fp));
}
// Invalid size.
ASSERT_ERRNO_FAILURE(EINVAL, nullptr, open_memstream(&p, nullptr)); #pragma clang diagnostic pop #else
GTEST_SKIP() << "glibc is broken"; #endif
}
TEST(STDIO_TEST, fdopen_add_CLOEXEC) { // This fd doesn't have O_CLOEXEC... int fd = open("/proc/version", O_RDONLY);
ASSERT_FALSE(CloseOnExec(fd)); // ...but the new one does.
FILE* fp = fdopen(fd, "re");
ASSERT_TRUE(CloseOnExec(fileno(fp)));
fclose(fp);
}
TEST(STDIO_TEST, fdopen_remove_CLOEXEC) { // This fd has O_CLOEXEC... int fd = open("/proc/version", O_RDONLY | O_CLOEXEC);
ASSERT_TRUE(CloseOnExec(fd)); // ...but the new one doesn't.
FILE* fp = fdopen(fd, "r");
ASSERT_TRUE(CloseOnExec(fileno(fp)));
fclose(fp);
}
TEST(STDIO_TEST, freopen_add_CLOEXEC) { // This FILE* doesn't have O_CLOEXEC...
FILE* fp = fopen("/proc/version", "r");
ASSERT_FALSE(CloseOnExec(fileno(fp))); // ...but the new one does.
fp = freopen("/proc/version", "re", fp);
ASSERT_TRUE(CloseOnExec(fileno(fp)));
fclose(fp);
}
TEST(STDIO_TEST, freopen_remove_CLOEXEC) { // This FILE* has O_CLOEXEC...
FILE* fp = fopen("/proc/version", "re");
ASSERT_TRUE(CloseOnExec(fileno(fp))); // ...but the new one doesn't.
fp = freopen("/proc/version", "r", fp);
ASSERT_FALSE(CloseOnExec(fileno(fp)));
fclose(fp);
}
TEST(STDIO_TEST, freopen_null_filename_add_CLOEXEC) { // This FILE* doesn't have O_CLOEXEC...
FILE* fp = fopen("/proc/version", "r");
ASSERT_FALSE(CloseOnExec(fileno(fp))); // ...but the new one does.
fp = freopen(nullptr, "re", fp);
ASSERT_TRUE(CloseOnExec(fileno(fp)));
fclose(fp);
}
TEST(STDIO_TEST, freopen_null_filename_remove_CLOEXEC) { // This FILE* has O_CLOEXEC...
FILE* fp = fopen("/proc/version", "re");
ASSERT_TRUE(CloseOnExec(fileno(fp))); // ...but the new one doesn't.
fp = freopen(nullptr, "r", fp);
ASSERT_FALSE(CloseOnExec(fileno(fp)));
fclose(fp);
}
// Try to read too much, but little enough that it still fits in the FILE's internal buffer. char buf1[4 * 4];
memset(buf1, 0, sizeof(buf1));
ASSERT_EQ(2U, fread(buf1, 4, 4, fp));
ASSERT_STREQ("0123456789", buf1);
ASSERT_TRUE(feof(fp));
rewind(fp);
// Try to read way too much so stdio tries to read more direct from the stream. char buf2[4 * 4096];
memset(buf2, 0, sizeof(buf2));
ASSERT_EQ(2U, fread(buf2, 4, 4096, fp));
ASSERT_STREQ("0123456789", buf2);
ASSERT_TRUE(feof(fp));
// We've flushed but not rewound, so there's nothing to read.
std::vector<char> buf(n, 0);
ASSERT_EQ(0U, fread(&buf[0], 1, buf.size(), fp));
ASSERT_TRUE(feof(fp));
// But hitting EOF doesn't prevent us from writing...
errno = 0;
ASSERT_EQ(1U, fwrite("2", 1, 1, fp)) << strerror(errno);
// And if we rewind, everything's there.
rewind(fp);
ASSERT_EQ(2U, fread(&buf[0], 1, buf.size(), fp));
ASSERT_EQ('1', buf[0]);
ASSERT_EQ('2', buf[1]);
cur_location = static_cast<size_t>(ftell(fp)); // Large read to force reading into the user supplied buffer and bypassing // the internal buffer.
ASSERT_EQ(8192U, fread(buffer, 1, 8192, fp));
ASSERT_EQ(memcmp(file_data+cur_location, buffer, 8192), 0);
// Small backwards seek to verify fseek does not reuse the internal buffer.
ASSERT_EQ(0, fseek(fp, -22, SEEK_CUR)) << strerror(errno);
cur_location = static_cast<size_t>(ftell(fp));
ASSERT_EQ(22U, fread(buffer, 1, 22, fp));
ASSERT_EQ(memcmp(file_data+cur_location, buffer, 22), 0);
// 'fr' is now at EOF.
ASSERT_EQ(0U, fread(buf, 1, 1, fr));
ASSERT_TRUE(feof(fr));
// Write some more...
fwrite("z", 1, 1, fw);
fflush(fw);
// ...and check that we can read it back. // (BSD thinks that once a stream has hit EOF, it must always return EOF. SysV disagrees.)
ASSERT_EQ(1U, fread(buf, 1, 1, fr));
ASSERT_STREQ("z", buf);
// But now we're done.
ASSERT_EQ(0U, fread(buf, 1, 1, fr));
fclose(fr);
fclose(fw);
}
TEST(STDIO_TEST, fclose_invalidates_fd) { // The typical error we're trying to help people catch involves accessing // memory after it's been freed. But we know that stdin/stdout/stderr are // special and don't get deallocated, so this test uses stdin.
ASSERT_EQ(0, fclose(stdin));
// Even though using a FILE* after close is undefined behavior, I've closed // this bug as "WAI" too many times. We shouldn't hand out stale fds, // especially because they might actually correspond to a real stream.
ASSERT_ERRNO_FAILURE(EBADF, -1, fileno(stdin));
}
// Neither Bionic nor glibc implement the overflow checking for SEEK_END. // (Aside: FreeBSD's libc is an example of a libc that checks both SEEK_CUR // and SEEK_END -- many C libraries check neither.)
ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
ASSERT_EQ(0x2'0000'0000, ftello64(fp));
fclose(fp);
}
TEST(STDIO_TEST, dev_std_files) { // POSIX only mentions /dev/stdout, but we should have all three (http://b/31824379). char path[PATH_MAX];
ssize_t length = readlink("/dev/stdin", path, sizeof(path));
ASSERT_LT(0, length);
ASSERT_EQ("/proc/self/fd/0", std::string(path, length));
TEST(STDIO_TEST, fread_with_locked_file) { // Reading an unbuffered/line-buffered file from one thread shouldn't block on // files locked on other threads, even if it flushes some line-buffered files.
FILE* fp1 = fopen("/dev/zero", "r");
ASSERT_TRUE(fp1 != nullptr);
flockfile(fp1);
// Historically the implementation used ints where it should have used size_t. const size_t too_big_for_an_int = 0x80000000ULL;
std::vector<char> buf(too_big_for_an_int);
// We test both fread() and fwrite() in the same function to avoid two tests // both allocating 2GiB of ram at the same time.
{
std::unique_ptr<FILE, decltype(&fclose)> fp{fopen("/dev/zero", "re"), fclose};
ASSERT_EQ(too_big_for_an_int, fread(&buf[0], 1, too_big_for_an_int, fp.get()));
}
{
std::unique_ptr<FILE, decltype(&fclose)> fp{fopen("/dev/null", "we"), fclose};
ASSERT_EQ(too_big_for_an_int, fwrite(&buf[0], 1, too_big_for_an_int, fp.get()));
} #else
GTEST_SKIP() << "32-bit can't allocate 2GiB"; #endif
}
TEST(STDIO_TEST, fflush_POSIX_2008) { // C23 still has fflush() on a read-only stream as undefined behavior. // POSIX 2008 has "For a stream open for reading with an underlying file description, // if the file is not already at EOF, and the file is one capable of seeking, // the file offset of the underlying open file description shall be set to the file position of the stream, // and any characters pushed back onto the stream by ungetc() or ungetwc() // that have not subsequently been read from the stream shall be discarded // (without further changing the file offset)". // It doesn't look like macOS or glibc actually implement the "discard the unget buffer" part, // but all the implementations seem to agree that fflush() should return success at least.
FILE* fp = fopen("/proc/version", "r");
ASSERT_TRUE(fp != nullptr);
ASSERT_EQ(0, fflush(fp));
fclose(fp);
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.48Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.