/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" TEST(stdio_ext, __fbufsize) { FILE* fp = fopen("/proc/version", "r"); // Initially, there's no buffer in case the first thing you do is disable buffering. ASSERT_EQ(0U, __fbufsize(fp)); // A read forces a buffer to be created. char buf[128]; fgets(buf, sizeof(buf), fp); ASSERT_EQ(1024U, __fbufsize(fp)); ASSERT_EQ(0, setvbuf(fp, buf, _IOFBF, 1)); ASSERT_EQ(1U, __fbufsize(fp)); ASSERT_EQ(0, setvbuf(fp, buf, _IOFBF, 8)); ASSERT_EQ(8U, __fbufsize(fp)); fclose(fp); } TEST(stdio_ext, __flbf) { FILE* fp = fopen("/proc/version", "r"); ASSERT_FALSE(__flbf(fp)); char buf[128]; ASSERT_EQ(0, setvbuf(fp, buf, _IOLBF, sizeof(buf))); ASSERT_TRUE(__flbf(fp)); fclose(fp); } TEST(stdio_ext, __fpending) { FILE* fp = fopen("/dev/null", "w"); ASSERT_EQ(0U, __fpending(fp)); ASSERT_EQ('x', fputc('x', fp)); ASSERT_EQ(1U, __fpending(fp)); ASSERT_EQ('y', fputc('y', fp)); ASSERT_EQ(2U, __fpending(fp)); fflush(fp); ASSERT_EQ(0U, __fpending(fp)); fclose(fp); } TEST(stdio_ext, __freadahead) { #if defined(__GLIBC__) GTEST_SKIP() << "glibc doesn't have __freadahead"; #else FILE* fp = tmpfile(); ASSERT_NE(EOF, fputs("hello", fp)); rewind(fp); ASSERT_EQ('h', fgetc(fp)); ASSERT_EQ(4u, __freadahead(fp)); ASSERT_EQ('H', ungetc('H', fp)); ASSERT_EQ(5u, __freadahead(fp)); fclose(fp); #endif } TEST(stdio_ext, __fpurge) { FILE* fp = tmpfile(); ASSERT_EQ('a', fputc('a', fp)); ASSERT_EQ(1U, __fpending(fp)); __fpurge(fp); ASSERT_EQ(0U, __fpending(fp)); ASSERT_EQ('b', fputc('b', fp)); ASSERT_EQ('\n', fputc('\n', fp)); ASSERT_EQ(2U, __fpending(fp)); rewind(fp); char buf[16]; char* s = fgets(buf, sizeof(buf), fp); ASSERT_TRUE(s != nullptr); ASSERT_STREQ("b\n", s); fclose(fp); } TEST(stdio_ext, _flushlbf) { FILE* fp = fopen("/dev/null", "w"); char buf[128]; ASSERT_EQ(0, setvbuf(fp, buf, _IOLBF, sizeof(buf))); ASSERT_EQ('a', fputc('a', fp)); ASSERT_EQ(1U, __fpending(fp)); _flushlbf(); ASSERT_EQ(0U, __fpending(fp)); fclose(fp); } TEST(stdio_ext, __freadable__fwritable) { FILE* fp; // Read-only. fp = fopen("/dev/null", "r"); ASSERT_TRUE(__freadable(fp)); ASSERT_FALSE(__fwritable(fp)); fclose(fp); // Write-only. fp = fopen("/dev/null", "w"); ASSERT_FALSE(__freadable(fp)); ASSERT_TRUE(__fwritable(fp)); fclose(fp); // Append (aka write-only). fp = fopen("/dev/null", "a"); ASSERT_FALSE(__freadable(fp)); ASSERT_TRUE(__fwritable(fp)); fclose(fp); // The three read-write modes. for (auto read_write_mode : {"r+", "w+", "a+"}) { fp = fopen("/dev/null", read_write_mode); ASSERT_TRUE(__freadable(fp)); ASSERT_TRUE(__fwritable(fp)); fclose(fp); } } TEST(stdio_ext, __freading__fwriting) { FILE* fp; // Append (aka write-only). Never reading. Always writing. fp = fopen("/dev/zero", "a"); ASSERT_FALSE(__freading(fp)); // Not reading initially. ASSERT_TRUE(__fwriting(fp)); // Writing initially. ASSERT_TRUE(fputc('x', fp) != EOF); ASSERT_FALSE(__freading(fp)); // Not reading after write. ASSERT_TRUE(__fwriting(fp)); // Still writing after write. fclose(fp); // Write-only. Never reading. Always writing. fp = fopen("/dev/zero", "w"); ASSERT_FALSE(__freading(fp)); // Not reading initially. ASSERT_TRUE(__fwriting(fp)); // Writing initially. ASSERT_TRUE(fputc('x', fp) != EOF); ASSERT_FALSE(__freading(fp)); // Not reading after write. ASSERT_TRUE(__fwriting(fp)); // Still writing after write. fclose(fp); // Read-only. Always reading. Never writing. fp = fopen("/dev/zero", "r"); ASSERT_TRUE(__freading(fp)); // Reading initially. ASSERT_FALSE(__fwriting(fp)); // Not writing initially. ASSERT_TRUE(fgetc(fp) == 0); ASSERT_TRUE(__freading(fp)); // Still reading after read. ASSERT_FALSE(__fwriting(fp)); // Still not writing after read. fclose(fp); // The three read-write modes. for (auto read_write_mode : {"r+", "w+", "a+"}) { fp = fopen("/dev/zero", read_write_mode); ASSERT_FALSE(__freading(fp)); // Not reading initially. ASSERT_FALSE(__fwriting(fp)); // Not writing initially. ASSERT_TRUE(fgetc(fp) == 0); ASSERT_TRUE(__freading(fp)); // Reading after read. ASSERT_FALSE(__fwriting(fp)); // Not writing after read. ASSERT_TRUE(fputc('x', fp) != EOF); ASSERT_FALSE(__freading(fp)); // Not reading after write. ASSERT_TRUE(__fwriting(fp)); // Writing after write. fclose(fp); } } TEST(stdio_ext, __fseterr) { #if defined(__GLIBC__) GTEST_SKIP() << "glibc doesn't have __fseterr, but gnulib will use it"; #else FILE* fp = fopen("/dev/null", "w"); ASSERT_FALSE(ferror(fp)); __fseterr(fp); ASSERT_TRUE(ferror(fp)); clearerr(fp); ASSERT_FALSE(ferror(fp)); fclose(fp); #endif } TEST(stdio_ext, __fsetlocking) { FILE* fp = fopen("/proc/version", "r"); ASSERT_EQ(FSETLOCKING_INTERNAL, __fsetlocking(fp, FSETLOCKING_QUERY)); ASSERT_EQ(FSETLOCKING_INTERNAL, __fsetlocking(fp, FSETLOCKING_BYCALLER)); ASSERT_EQ(FSETLOCKING_BYCALLER, __fsetlocking(fp, FSETLOCKING_QUERY)); ASSERT_EQ(FSETLOCKING_BYCALLER, __fsetlocking(fp, FSETLOCKING_INTERNAL)); ASSERT_EQ(FSETLOCKING_INTERNAL, __fsetlocking(fp, FSETLOCKING_QUERY)); fclose(fp); } static void LockingByCallerHelper(std::atomic* pid) { *pid = gettid(); flockfile(stdout); funlockfile(stdout); } TEST(stdio_ext, __fsetlocking_BYCALLER) { // Check if users can use flockfile/funlockfile to protect stdio operations. int old_state = __fsetlocking(stdout, FSETLOCKING_BYCALLER); flockfile(stdout); pthread_t thread; std::atomic pid(0); ASSERT_EQ(0, pthread_create(&thread, nullptr, reinterpret_cast(LockingByCallerHelper), &pid)); WaitUntilThreadSleep(pid); funlockfile(stdout); ASSERT_EQ(0, pthread_join(thread, nullptr)); __fsetlocking(stdout, old_state); }