aboutsummaryrefslogtreecommitdiff
path: root/tests/stdio_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/stdio_test.cpp')
-rw-r--r--tests/stdio_test.cpp277
1 files changed, 253 insertions, 24 deletions
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 75abbd235..5736e1787 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -33,9 +33,10 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/silent_death_test.h>
+#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
-#include "BionicDeathTest.h"
#include "utils.h"
// This #include is actually a test too. We have to duplicate the
@@ -55,8 +56,8 @@
using namespace std::string_literals;
-class stdio_DeathTest : public BionicDeathTest {};
-class stdio_nofortify_DeathTest : public BionicDeathTest {};
+using stdio_DeathTest = SilentDeathTest;
+using stdio_nofortify_DeathTest = SilentDeathTest;
static void SetFileTo(const char* path, const char* content) {
FILE* fp;
@@ -294,6 +295,34 @@ TEST(STDIO_TEST, snprintf_a) {
EXPECT_STREQ("<0x1.3831e147ae148p+13>", buf);
}
+// http://b/152588929
+TEST(STDIO_TEST, snprintf_La) {
+#if defined(__LP64__)
+ char buf[BUFSIZ];
+ union {
+ uint64_t a[2];
+ long double v;
+ } u;
+
+ u.a[0] = UINT64_C(0x9b9b9b9b9b9b9b9b);
+ u.a[1] = UINT64_C(0xdfdfdfdfdfdfdfdf);
+ EXPECT_EQ(41, snprintf(buf, sizeof(buf), "<%La>", u.v));
+ EXPECT_STREQ("<-0x1.dfdfdfdfdfdf9b9b9b9b9b9b9b9bp+8160>", buf);
+
+ u.a[0] = UINT64_C(0xffffffffffffffff);
+ u.a[1] = UINT64_C(0x7ffeffffffffffff);
+ EXPECT_EQ(41, snprintf(buf, sizeof(buf), "<%La>", u.v));
+ EXPECT_STREQ("<0x1.ffffffffffffffffffffffffffffp+16383>", buf);
+
+ u.a[0] = UINT64_C(0x0000000000000000);
+ u.a[1] = UINT64_C(0x0000000000000000);
+ EXPECT_EQ(8, snprintf(buf, sizeof(buf), "<%La>", u.v));
+ EXPECT_STREQ("<0x0p+0>", buf);
+#else
+ GTEST_SKIP() << "no ld128";
+#endif
+}
+
TEST(STDIO_TEST, snprintf_lc) {
char buf[BUFSIZ];
wint_t wc = L'a';
@@ -332,7 +361,7 @@ TEST(STDIO_TEST, snprintf_S) { // Synonym for %ls.
EXPECT_STREQ("<hi>", buf);
}
-TEST(STDIO_TEST, snprintf_n) {
+TEST_F(STDIO_DEATHTEST, snprintf_n) {
#if defined(__BIONIC__)
// http://b/14492135 and http://b/31832608.
char buf[32];
@@ -343,6 +372,11 @@ TEST(STDIO_TEST, snprintf_n) {
#endif
}
+TEST(STDIO_TEST, snprintf_measure) {
+ char buf[16];
+ ASSERT_EQ(11, snprintf(buf, 0, "Hello %s", "world"));
+}
+
TEST(STDIO_TEST, snprintf_smoke) {
char buf[BUFSIZ];
@@ -1127,7 +1161,6 @@ TEST(STDIO_TEST, sscanf_mc) {
free(p1);
}
-
TEST(STDIO_TEST, sscanf_mlc) {
// This is so useless that clang doesn't even believe it exists...
#pragma clang diagnostic push
@@ -1161,7 +1194,6 @@ TEST(STDIO_TEST, sscanf_mlc) {
#pragma clang diagnostic pop
}
-
TEST(STDIO_TEST, sscanf_ms) {
CheckScanfM(sscanf, "hello", "%ms", 1, "hello");
CheckScanfM(sscanf, "hello", "%4ms", 1, "hell");
@@ -1924,34 +1956,64 @@ TEST(STDIO_TEST, open_memstream_EINVAL) {
#endif
}
-TEST(STDIO_TEST, fdopen_CLOEXEC) {
- int fd = open("/proc/version", O_RDONLY);
- ASSERT_TRUE(fd != -1);
-
+TEST(STDIO_TEST, fdopen_add_CLOEXEC) {
// This fd doesn't have O_CLOEXEC...
- AssertCloseOnExec(fd, false);
-
- FILE* fp = fdopen(fd, "re");
- ASSERT_TRUE(fp != nullptr);
-
+ int fd = open("/proc/version", O_RDONLY);
+ ASSERT_FALSE(CloseOnExec(fd));
// ...but the new one does.
- AssertCloseOnExec(fileno(fp), true);
+ 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_CLOEXEC) {
+TEST(STDIO_TEST, freopen_add_CLOEXEC) {
+ // This FILE* doesn't have O_CLOEXEC...
FILE* fp = fopen("/proc/version", "r");
- ASSERT_TRUE(fp != nullptr);
+ ASSERT_FALSE(CloseOnExec(fileno(fp)));
+ // ...but the new one does.
+ fp = freopen("/proc/version", "re", fp);
+ ASSERT_TRUE(CloseOnExec(fileno(fp)));
- // This FILE* doesn't have O_CLOEXEC...
- AssertCloseOnExec(fileno(fp), false);
+ fclose(fp);
+}
- fp = freopen("/proc/version", "re", 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.
- AssertCloseOnExec(fileno(fp), true);
+ 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);
}
@@ -2363,7 +2425,7 @@ TEST(STDIO_TEST, remove) {
ASSERT_EQ(ENOENT, errno);
}
-TEST(STDIO_DEATHTEST, snprintf_30445072_known_buffer_size) {
+TEST_F(STDIO_DEATHTEST, snprintf_30445072_known_buffer_size) {
char buf[16];
ASSERT_EXIT(snprintf(buf, atol("-1"), "hello"),
testing::KilledBySignal(SIGABRT),
@@ -2375,7 +2437,7 @@ TEST(STDIO_DEATHTEST, snprintf_30445072_known_buffer_size) {
);
}
-TEST(STDIO_DEATHTEST, snprintf_30445072_unknown_buffer_size) {
+TEST_F(STDIO_DEATHTEST, snprintf_30445072_unknown_buffer_size) {
std::string buf = "world";
ASSERT_EXIT(snprintf(&buf[0], atol("-1"), "hello"),
testing::KilledBySignal(SIGABRT),
@@ -2505,6 +2567,16 @@ TEST(STDIO_TEST, puts) {
eth.Run([&]() { exit(puts("a b c")); }, 0, "a b c\n");
}
+TEST(STDIO_TEST, putchar) {
+ ExecTestHelper eth;
+ eth.Run([&]() { exit(putchar('A')); }, 65, "A");
+}
+
+TEST(STDIO_TEST, putchar_unlocked) {
+ ExecTestHelper eth;
+ eth.Run([&]() { exit(putchar('B')); }, 66, "B");
+}
+
TEST(STDIO_TEST, unlocked) {
TemporaryFile tf;
@@ -2705,3 +2777,160 @@ TEST(STDIO_TEST, renameat2_flags) {
ASSERT_NE(0, RENAME_WHITEOUT);
#endif
}
+
+TEST(STDIO_TEST, fdopen_failures) {
+ FILE* fp;
+ int fd = open("/proc/version", O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ // Nonsense mode.
+ errno = 0;
+ fp = fdopen(fd, "nonsense");
+ ASSERT_TRUE(fp == nullptr);
+ ASSERT_EQ(EINVAL, errno);
+
+ // Mode that isn't a subset of the fd's actual mode.
+ errno = 0;
+ fp = fdopen(fd, "w");
+ ASSERT_TRUE(fp == nullptr);
+ ASSERT_EQ(EINVAL, errno);
+
+ // Can't set append on the underlying fd.
+ errno = 0;
+ fp = fdopen(fd, "a");
+ ASSERT_TRUE(fp == nullptr);
+ ASSERT_EQ(EINVAL, errno);
+
+ // Bad fd.
+ errno = 0;
+ fp = fdopen(-1, "re");
+ ASSERT_TRUE(fp == nullptr);
+ ASSERT_EQ(EBADF, errno);
+
+ close(fd);
+}
+
+TEST(STDIO_TEST, fmemopen_invalid_mode) {
+ errno = 0;
+ FILE* fp = fmemopen(nullptr, 16, "nonsense");
+ ASSERT_TRUE(fp == nullptr);
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(STDIO_TEST, fopen_invalid_mode) {
+ errno = 0;
+ FILE* fp = fopen("/proc/version", "nonsense");
+ ASSERT_TRUE(fp == nullptr);
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(STDIO_TEST, freopen_invalid_mode) {
+ FILE* fp = fopen("/proc/version", "re");
+ ASSERT_TRUE(fp != nullptr);
+
+ errno = 0;
+ fp = freopen("/proc/version", "nonsense", fp);
+ ASSERT_TRUE(fp == nullptr);
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(STDIO_TEST, asprintf_smoke) {
+ char* p = nullptr;
+ ASSERT_EQ(11, asprintf(&p, "hello %s", "world"));
+ ASSERT_STREQ("hello world", p);
+ free(p);
+}
+
+TEST(STDIO_TEST, fopen_ENOENT) {
+ errno = 0;
+ FILE* fp = fopen("/proc/does-not-exist", "re");
+ ASSERT_TRUE(fp == nullptr);
+ ASSERT_EQ(ENOENT, errno);
+}
+
+static void tempnam_test(bool has_TMPDIR, const char* dir, const char* prefix, const char* re) {
+ if (has_TMPDIR) {
+ setenv("TMPDIR", "/my/tmp/dir", 1);
+ } else {
+ unsetenv("TMPDIR");
+ }
+ char* s1 = tempnam(dir, prefix);
+ char* s2 = tempnam(dir, prefix);
+ ASSERT_MATCH(s1, re);
+ ASSERT_MATCH(s2, re);
+ ASSERT_STRNE(s1, s2);
+ free(s1);
+ free(s2);
+}
+
+TEST(STDIO_TEST, tempnam__system_directory_system_prefix_with_TMPDIR) {
+ tempnam_test(true, nullptr, nullptr, "^/my/tmp/dir/.*");
+}
+
+TEST(STDIO_TEST, tempnam__system_directory_system_prefix_without_TMPDIR) {
+ tempnam_test(false, nullptr, nullptr, "^/data/local/tmp/.*");
+}
+
+TEST(STDIO_TEST, tempnam__system_directory_user_prefix_with_TMPDIR) {
+ tempnam_test(true, nullptr, "prefix", "^/my/tmp/dir/prefix.*");
+}
+
+TEST(STDIO_TEST, tempnam__system_directory_user_prefix_without_TMPDIR) {
+ tempnam_test(false, nullptr, "prefix", "^/data/local/tmp/prefix.*");
+}
+
+TEST(STDIO_TEST, tempnam__user_directory_system_prefix_with_TMPDIR) {
+ tempnam_test(true, "/a/b/c", nullptr, "^/my/tmp/dir/.*");
+}
+
+TEST(STDIO_TEST, tempnam__user_directory_system_prefix_without_TMPDIR) {
+ tempnam_test(false, "/a/b/c", nullptr, "^/a/b/c/.*");
+}
+
+TEST(STDIO_TEST, tempnam__user_directory_user_prefix_with_TMPDIR) {
+ tempnam_test(true, "/a/b/c", "prefix", "^/my/tmp/dir/prefix.*");
+}
+
+TEST(STDIO_TEST, tempnam__user_directory_user_prefix_without_TMPDIR) {
+ tempnam_test(false, "/a/b/c", "prefix", "^/a/b/c/prefix.*");
+}
+
+static void tmpnam_test(char* s) {
+ char s1[L_tmpnam], s2[L_tmpnam];
+
+ strcpy(s1, tmpnam(s));
+ strcpy(s2, tmpnam(s));
+ ASSERT_MATCH(s1, "/tmp/.*");
+ ASSERT_MATCH(s2, "/tmp/.*");
+ ASSERT_STRNE(s1, s2);
+}
+
+TEST(STDIO_TEST, tmpnam) {
+ tmpnam_test(nullptr);
+}
+
+TEST(STDIO_TEST, tmpnam_buf) {
+ char buf[L_tmpnam];
+ tmpnam_test(buf);
+}
+
+TEST(STDIO_TEST, freopen_null_filename_mode) {
+ TemporaryFile tf;
+ FILE* fp = fopen(tf.path, "r");
+ ASSERT_TRUE(fp != nullptr);
+
+ // "r" = O_RDONLY
+ char buf[1];
+ ASSERT_EQ(0, read(fileno(fp), buf, 1));
+ ASSERT_EQ(-1, write(fileno(fp), "hello", 1));
+ // "r+" = O_RDWR
+ fp = freopen(nullptr, "r+", fp);
+ ASSERT_EQ(0, read(fileno(fp), buf, 1));
+ ASSERT_EQ(1, write(fileno(fp), "hello", 1));
+ // "w" = O_WRONLY
+ fp = freopen(nullptr, "w", fp);
+ ASSERT_EQ(-1, read(fileno(fp), buf, 1));
+ ASSERT_EQ(1, write(fileno(fp), "hello", 1));
+
+ fclose(fp);
+}