summaryrefslogtreecommitdiffstats
path: root/tests/stdio_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/stdio_test.cpp')
-rw-r--r--tests/stdio_test.cpp327
1 files changed, 307 insertions, 20 deletions
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index bba744a..62677cd 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -27,6 +27,8 @@
#include <wchar.h>
#include <locale.h>
+#include <vector>
+
#include "TemporaryFile.h"
TEST(stdio, flockfile_18208568_stderr) {
@@ -77,7 +79,7 @@ TEST(stdio, dprintf) {
int rc = dprintf(tf.fd, "hello\n");
ASSERT_EQ(rc, 6);
- lseek(tf.fd, SEEK_SET, 0);
+ lseek(tf.fd, 0, SEEK_SET);
FILE* tfile = fdopen(tf.fd, "r");
ASSERT_TRUE(tfile != NULL);
@@ -107,7 +109,7 @@ TEST(stdio, getdelim) {
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(word_read, 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));
@@ -149,6 +151,15 @@ TEST(stdio, getdelim_invalid) {
fclose(fp);
}
+TEST(stdio, getdelim_directory) {
+ FILE* fp = fopen("/proc", "r");
+ ASSERT_TRUE(fp != NULL);
+ char* word_read;
+ size_t allocated_length;
+ ASSERT_EQ(-1, getdelim(&word_read, &allocated_length, ' ', fp));
+ fclose(fp);
+}
+
TEST(stdio, getline) {
FILE* fp = tmpfile();
ASSERT_TRUE(fp != NULL);
@@ -171,7 +182,7 @@ TEST(stdio, getline) {
while ((read_char_count = getline(&line_read, &allocated_length, fp)) != -1) {
ASSERT_EQ(read_char_count, static_cast<int>(strlen(line_written)));
ASSERT_GE(allocated_length, strlen(line_written));
- ASSERT_STREQ(line_read, line_written);
+ ASSERT_STREQ(line_written, line_read);
++read_line_count;
}
ASSERT_EQ(read_line_count, line_count);
@@ -364,22 +375,52 @@ TEST(stdio, snprintf_smoke) {
EXPECT_STREQ("print_me_twice print_me_twice", buf);
}
-TEST(stdio, snprintf_f_special) {
- char buf[BUFSIZ];
- snprintf(buf, sizeof(buf), "%f", nanf(""));
- EXPECT_STRCASEEQ("NaN", buf);
-
- snprintf(buf, sizeof(buf), "%f", HUGE_VALF);
- EXPECT_STRCASEEQ("Inf", buf);
-}
-
-TEST(stdio, snprintf_g_special) {
- char buf[BUFSIZ];
- snprintf(buf, sizeof(buf), "%g", nan(""));
- EXPECT_STRCASEEQ("NaN", buf);
-
- snprintf(buf, sizeof(buf), "%g", HUGE_VAL);
- EXPECT_STRCASEEQ("Inf", buf);
+template <typename T>
+void CheckInfNan(int snprintf_fn(T*, size_t, const T*, ...),
+ const T* fmt, const T* fmt_plus,
+ const T* minus_inf, const T* inf_, const T* plus_inf,
+ const T* minus_nan, const T* nan_, const T* plus_nan) {
+ T buf[BUFSIZ];
+
+ snprintf_fn(buf, sizeof(buf), fmt, nan(""));
+ EXPECT_STREQ(nan_, buf) << fmt;
+ snprintf_fn(buf, sizeof(buf), fmt, -nan(""));
+ EXPECT_STREQ(minus_nan, buf) << fmt;
+ snprintf_fn(buf, sizeof(buf), fmt_plus, nan(""));
+ EXPECT_STREQ(plus_nan, buf) << fmt_plus;
+ snprintf_fn(buf, sizeof(buf), fmt_plus, -nan(""));
+ EXPECT_STREQ(minus_nan, buf) << fmt_plus;
+
+ snprintf_fn(buf, sizeof(buf), fmt, HUGE_VAL);
+ EXPECT_STREQ(inf_, buf) << fmt;
+ snprintf_fn(buf, sizeof(buf), fmt, -HUGE_VAL);
+ EXPECT_STREQ(minus_inf, buf) << fmt;
+ snprintf_fn(buf, sizeof(buf), fmt_plus, HUGE_VAL);
+ EXPECT_STREQ(plus_inf, buf) << fmt_plus;
+ snprintf_fn(buf, sizeof(buf), fmt_plus, -HUGE_VAL);
+ EXPECT_STREQ(minus_inf, buf) << fmt_plus;
+}
+
+TEST(stdio, snprintf_inf_nan) {
+ CheckInfNan(snprintf, "%a", "%+a", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
+ CheckInfNan(snprintf, "%A", "%+A", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
+ CheckInfNan(snprintf, "%e", "%+e", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
+ CheckInfNan(snprintf, "%E", "%+E", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
+ CheckInfNan(snprintf, "%f", "%+f", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
+ CheckInfNan(snprintf, "%F", "%+F", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
+ CheckInfNan(snprintf, "%g", "%+g", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
+ CheckInfNan(snprintf, "%G", "%+G", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
+}
+
+TEST(stdio, wsprintf_inf_nan) {
+ CheckInfNan(swprintf, L"%a", L"%+a", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
+ CheckInfNan(swprintf, L"%A", L"%+A", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
+ CheckInfNan(swprintf, L"%e", L"%+e", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
+ CheckInfNan(swprintf, L"%E", L"%+E", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
+ CheckInfNan(swprintf, L"%f", L"%+f", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
+ CheckInfNan(swprintf, L"%F", L"%+F", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
+ CheckInfNan(swprintf, L"%g", L"%+g", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
+ CheckInfNan(swprintf, L"%G", L"%+G", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
}
TEST(stdio, snprintf_d_INT_MAX) {
@@ -439,8 +480,22 @@ TEST(stdio, snprintf_e) {
TEST(stdio, snprintf_negative_zero_5084292) {
char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "%e", -0.0);
+ EXPECT_STREQ("-0.000000e+00", buf);
+ snprintf(buf, sizeof(buf), "%E", -0.0);
+ EXPECT_STREQ("-0.000000E+00", buf);
snprintf(buf, sizeof(buf), "%f", -0.0);
EXPECT_STREQ("-0.000000", buf);
+ snprintf(buf, sizeof(buf), "%F", -0.0);
+ EXPECT_STREQ("-0.000000", buf);
+ snprintf(buf, sizeof(buf), "%g", -0.0);
+ EXPECT_STREQ("-0", buf);
+ snprintf(buf, sizeof(buf), "%G", -0.0);
+ EXPECT_STREQ("-0", buf);
+ snprintf(buf, sizeof(buf), "%a", -0.0);
+ EXPECT_STREQ("-0x0p+0", buf);
+ snprintf(buf, sizeof(buf), "%A", -0.0);
+ EXPECT_STREQ("-0X0P+0", buf);
}
TEST(stdio, snprintf_utf8_15439554) {
@@ -695,6 +750,125 @@ TEST(stdio, fpos_t_and_seek) {
fclose(fp);
}
+TEST(stdio, fmemopen) {
+ char buf[16];
+ memset(buf, 0, sizeof(buf));
+ FILE* fp = fmemopen(buf, sizeof(buf), "r+");
+ ASSERT_EQ('<', fputc('<', fp));
+ ASSERT_NE(EOF, fputs("abc>\n", fp));
+ fflush(fp);
+
+ ASSERT_STREQ("<abc>\n", buf);
+
+ rewind(fp);
+
+ char line[16];
+ char* s = fgets(line, sizeof(line), fp);
+ ASSERT_TRUE(s != NULL);
+ ASSERT_STREQ("<abc>\n", s);
+
+ fclose(fp);
+}
+
+TEST(stdio, fmemopen_NULL) {
+ FILE* fp = fmemopen(nullptr, 128, "r+");
+ ASSERT_NE(EOF, fputs("xyz\n", fp));
+
+ rewind(fp);
+
+ char line[16];
+ char* s = fgets(line, sizeof(line), fp);
+ ASSERT_TRUE(s != NULL);
+ ASSERT_STREQ("xyz\n", s);
+
+ fclose(fp);
+}
+
+TEST(stdio, fmemopen_EINVAL) {
+ char buf[16];
+
+ // Invalid size.
+ errno = 0;
+ ASSERT_EQ(nullptr, fmemopen(buf, 0, "r+"));
+ ASSERT_EQ(EINVAL, errno);
+
+ // No '+' with NULL buffer.
+ errno = 0;
+ ASSERT_EQ(nullptr, fmemopen(nullptr, 0, "r"));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(stdio, open_memstream) {
+ char* p = nullptr;
+ size_t size = 0;
+ FILE* fp = open_memstream(&p, &size);
+ ASSERT_NE(EOF, fputs("hello, world!", fp));
+ fclose(fp);
+
+ ASSERT_STREQ("hello, world!", p);
+ ASSERT_EQ(strlen("hello, world!"), size);
+ free(p);
+}
+
+TEST(stdio, open_memstream_EINVAL) {
+#if defined(__BIONIC__)
+ char* p;
+ size_t size;
+
+ // Invalid buffer.
+ errno = 0;
+ ASSERT_EQ(nullptr, open_memstream(nullptr, &size));
+ ASSERT_EQ(EINVAL, errno);
+
+ // Invalid size.
+ errno = 0;
+ ASSERT_EQ(nullptr, open_memstream(&p, nullptr));
+ ASSERT_EQ(EINVAL, errno);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(stdio, fdopen_CLOEXEC) {
+ int fd = open("/proc/version", O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ // This fd doesn't have O_CLOEXEC...
+ int flags = fcntl(fd, F_GETFD);
+ ASSERT_TRUE(flags != -1);
+ ASSERT_EQ(0, flags & FD_CLOEXEC);
+
+ FILE* fp = fdopen(fd, "re");
+ ASSERT_TRUE(fp != NULL);
+
+ // ...but the new one does.
+ flags = fcntl(fileno(fp), F_GETFD);
+ ASSERT_TRUE(flags != -1);
+ ASSERT_EQ(FD_CLOEXEC, flags & FD_CLOEXEC);
+
+ fclose(fp);
+ close(fd);
+}
+
+TEST(stdio, freopen_CLOEXEC) {
+ FILE* fp = fopen("/proc/version", "r");
+ ASSERT_TRUE(fp != NULL);
+
+ // This FILE* doesn't have O_CLOEXEC...
+ int flags = fcntl(fileno(fp), F_GETFD);
+ ASSERT_TRUE(flags != -1);
+ ASSERT_EQ(0, flags & FD_CLOEXEC);
+
+ fp = freopen("/proc/version", "re", fp);
+
+ // ...but the new one does.
+ flags = fcntl(fileno(fp), F_GETFD);
+ ASSERT_TRUE(flags != -1);
+ ASSERT_EQ(FD_CLOEXEC, flags & FD_CLOEXEC);
+
+ fclose(fp);
+}
+
// https://code.google.com/p/android/issues/detail?id=81155
// http://b/18556607
TEST(stdio, fread_unbuffered_pathological_performance) {
@@ -709,7 +883,7 @@ TEST(stdio, fread_unbuffered_pathological_performance) {
time_t t0 = time(NULL);
for (size_t i = 0; i < 1024; ++i) {
- fread(buf, 64*1024, 1, fp);
+ ASSERT_EQ(1U, fread(buf, 64*1024, 1, fp));
}
time_t t1 = time(NULL);
@@ -725,3 +899,116 @@ TEST(stdio, fread_unbuffered_pathological_performance) {
ASSERT_EQ('\xff', buf[i]);
}
}
+
+TEST(stdio, fread_EOF) {
+ std::string digits("0123456789");
+ FILE* fp = fmemopen(&digits[0], digits.size(), "r");
+
+ // 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));
+
+ fclose(fp);
+}
+
+static void test_fread_from_write_only_stream(size_t n) {
+ FILE* fp = fopen("/dev/null", "w");
+ std::vector<char> buf(n, 0);
+ errno = 0;
+ ASSERT_EQ(0U, fread(&buf[0], n, 1, fp));
+ ASSERT_EQ(EBADF, errno);
+ ASSERT_TRUE(ferror(fp));
+ ASSERT_FALSE(feof(fp));
+ fclose(fp);
+}
+
+TEST(stdio, fread_from_write_only_stream_slow_path) {
+ test_fread_from_write_only_stream(1);
+}
+
+TEST(stdio, fread_from_write_only_stream_fast_path) {
+ test_fread_from_write_only_stream(64*1024);
+}
+
+static void test_fwrite_after_fread(size_t n) {
+ TemporaryFile tf;
+
+ FILE* fp = fdopen(tf.fd, "w+");
+ ASSERT_EQ(1U, fwrite("1", 1, 1, fp));
+ fflush(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)) << 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]);
+
+ fclose(fp);
+}
+
+TEST(stdio, fwrite_after_fread_slow_path) {
+ test_fwrite_after_fread(16);
+}
+
+TEST(stdio, fwrite_after_fread_fast_path) {
+ test_fwrite_after_fread(64*1024);
+}
+
+// http://b/19172514
+TEST(stdio, fread_after_fseek) {
+ TemporaryFile tf;
+
+ FILE* fp = fopen(tf.filename, "w+");
+ ASSERT_TRUE(fp != nullptr);
+
+ char file_data[12288];
+ for (size_t i = 0; i < 12288; i++) {
+ file_data[i] = i;
+ }
+ ASSERT_EQ(12288U, fwrite(file_data, 1, 12288, fp));
+ fclose(fp);
+
+ fp = fopen(tf.filename, "r");
+ ASSERT_TRUE(fp != nullptr);
+
+ char buffer[8192];
+ size_t cur_location = 0;
+ // Small read to populate internal buffer.
+ ASSERT_EQ(100U, fread(buffer, 1, 100, fp));
+ ASSERT_EQ(memcmp(file_data, buffer, 100), 0);
+
+ 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));
+ 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);
+
+ fclose(fp);
+}