119a6dd33SSiva Chandra Reddy //===-- Unittests for the fopencookie function ----------------------------===// 219a6dd33SSiva Chandra Reddy // 319a6dd33SSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 419a6dd33SSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information. 519a6dd33SSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 619a6dd33SSiva Chandra Reddy // 719a6dd33SSiva Chandra Reddy //===----------------------------------------------------------------------===// 819a6dd33SSiva Chandra Reddy 99db0037bSSiva Chandra Reddy #include "src/stdio/clearerr.h" 1019a6dd33SSiva Chandra Reddy #include "src/stdio/fclose.h" 119db0037bSSiva Chandra Reddy #include "src/stdio/feof.h" 129db0037bSSiva Chandra Reddy #include "src/stdio/ferror.h" 1319a6dd33SSiva Chandra Reddy #include "src/stdio/fflush.h" 1419a6dd33SSiva Chandra Reddy #include "src/stdio/fopencookie.h" 1519a6dd33SSiva Chandra Reddy #include "src/stdio/fread.h" 1619a6dd33SSiva Chandra Reddy #include "src/stdio/fseek.h" 1719a6dd33SSiva Chandra Reddy #include "src/stdio/fwrite.h" 18af1315c2SSiva Chandra Reddy #include "test/UnitTest/MemoryMatcher.h" 19af1315c2SSiva Chandra Reddy #include "test/UnitTest/Test.h" 2019a6dd33SSiva Chandra Reddy 21c63112a9Slntue #include "hdr/stdio_macros.h" 22*33bdb53dSJob Henandez Lara #include "hdr/types/size_t.h" 2304a9c625SMichael Jones #include "src/errno/libc_errno.h" 2419a6dd33SSiva Chandra Reddy 25b6bc9d72SGuillaume Chatelet using MemoryView = LIBC_NAMESPACE::testing::MemoryView; 2619a6dd33SSiva Chandra Reddy 2719a6dd33SSiva Chandra Reddy struct StringStream { 2819a6dd33SSiva Chandra Reddy char *buf; 2919a6dd33SSiva Chandra Reddy size_t bufsize; // Size of buf 3019a6dd33SSiva Chandra Reddy size_t endpos; // 1 more than current fill size 3119a6dd33SSiva Chandra Reddy size_t offset; // Current read/write location 3219a6dd33SSiva Chandra Reddy }; 3319a6dd33SSiva Chandra Reddy 3419a6dd33SSiva Chandra Reddy ssize_t write_ss(void *cookie, const char *buf, size_t size) { 3519a6dd33SSiva Chandra Reddy auto *ss = reinterpret_cast<StringStream *>(cookie); 3619a6dd33SSiva Chandra Reddy if (ss->offset + size > ss->bufsize) 3719a6dd33SSiva Chandra Reddy ss->buf = 3819a6dd33SSiva Chandra Reddy reinterpret_cast<char *>(realloc(ss->buf, (ss->offset + size) * 2)); 3919a6dd33SSiva Chandra Reddy for (size_t i = 0; i < size; ++i, ss->offset += 1) 4019a6dd33SSiva Chandra Reddy ss->buf[ss->offset] = buf[i]; 4119a6dd33SSiva Chandra Reddy if (ss->offset > ss->endpos) 4219a6dd33SSiva Chandra Reddy ss->endpos = ss->offset; 4319a6dd33SSiva Chandra Reddy return size; 4419a6dd33SSiva Chandra Reddy } 4519a6dd33SSiva Chandra Reddy 4619a6dd33SSiva Chandra Reddy ssize_t read_ss(void *cookie, char *buf, size_t size) { 4719a6dd33SSiva Chandra Reddy auto *ss = reinterpret_cast<StringStream *>(cookie); 4819a6dd33SSiva Chandra Reddy ssize_t copysize = size; 4919a6dd33SSiva Chandra Reddy if (ss->offset + size > ss->endpos) { 5019a6dd33SSiva Chandra Reddy // You cannot copy more than what you have available. 5119a6dd33SSiva Chandra Reddy copysize = ss->endpos - ss->offset; 5219a6dd33SSiva Chandra Reddy if (copysize < 0) 5319a6dd33SSiva Chandra Reddy copysize = 0; // A seek could have moved offset past the endpos 5419a6dd33SSiva Chandra Reddy } 5519a6dd33SSiva Chandra Reddy for (size_t i = 0; i < size_t(copysize); ++i, ++ss->offset) 5619a6dd33SSiva Chandra Reddy buf[i] = ss->buf[ss->offset]; 5719a6dd33SSiva Chandra Reddy return copysize; 5819a6dd33SSiva Chandra Reddy } 5919a6dd33SSiva Chandra Reddy 6019a6dd33SSiva Chandra Reddy int seek_ss(void *cookie, off64_t *offset, int whence) { 6119a6dd33SSiva Chandra Reddy auto *ss = reinterpret_cast<StringStream *>(cookie); 6219a6dd33SSiva Chandra Reddy off64_t new_offset; 6319a6dd33SSiva Chandra Reddy if (whence == SEEK_SET) { 6419a6dd33SSiva Chandra Reddy new_offset = *offset; 6519a6dd33SSiva Chandra Reddy } else if (whence == SEEK_CUR) { 6619a6dd33SSiva Chandra Reddy new_offset = *offset + ss->offset; 6719a6dd33SSiva Chandra Reddy } else if (whence == SEEK_END) { 6819a6dd33SSiva Chandra Reddy new_offset = *offset + ss->endpos; 6919a6dd33SSiva Chandra Reddy } else { 703eb1e6d8Smichaelrj-google LIBC_NAMESPACE::libc_errno = EINVAL; 7119a6dd33SSiva Chandra Reddy return -1; 7219a6dd33SSiva Chandra Reddy } 7319a6dd33SSiva Chandra Reddy if (new_offset < 0 || size_t(new_offset) > ss->bufsize) 7419a6dd33SSiva Chandra Reddy return -1; 7519a6dd33SSiva Chandra Reddy ss->offset = new_offset; 7619a6dd33SSiva Chandra Reddy *offset = new_offset; 7719a6dd33SSiva Chandra Reddy return 0; 7819a6dd33SSiva Chandra Reddy } 7919a6dd33SSiva Chandra Reddy 8019a6dd33SSiva Chandra Reddy int close_ss(void *cookie) { 8119a6dd33SSiva Chandra Reddy auto *ss = reinterpret_cast<StringStream *>(cookie); 8219a6dd33SSiva Chandra Reddy free(ss->buf); 8319a6dd33SSiva Chandra Reddy ss->buf = nullptr; 8419a6dd33SSiva Chandra Reddy ss->bufsize = ss->endpos = ss->offset = 0; 8519a6dd33SSiva Chandra Reddy return 0; 8619a6dd33SSiva Chandra Reddy } 8719a6dd33SSiva Chandra Reddy 8819a6dd33SSiva Chandra Reddy constexpr cookie_io_functions_t STRING_STREAM_FUNCS = {&read_ss, &write_ss, 8919a6dd33SSiva Chandra Reddy &seek_ss, &close_ss}; 9019a6dd33SSiva Chandra Reddy 9119a6dd33SSiva Chandra Reddy TEST(LlvmLibcFOpenCookie, ReadOnlyCookieTest) { 9219a6dd33SSiva Chandra Reddy constexpr char CONTENT[] = "Hello,readonly!"; 9319a6dd33SSiva Chandra Reddy auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream))); 9419a6dd33SSiva Chandra Reddy ss->buf = reinterpret_cast<char *>(malloc(sizeof(CONTENT))); 9519a6dd33SSiva Chandra Reddy ss->bufsize = sizeof(CONTENT); 9619a6dd33SSiva Chandra Reddy ss->offset = 0; 9719a6dd33SSiva Chandra Reddy ss->endpos = ss->bufsize; 9819a6dd33SSiva Chandra Reddy for (size_t i = 0; i < sizeof(CONTENT); ++i) 9919a6dd33SSiva Chandra Reddy ss->buf[i] = CONTENT[i]; 10019a6dd33SSiva Chandra Reddy 101b6bc9d72SGuillaume Chatelet ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "r", STRING_STREAM_FUNCS); 10219a6dd33SSiva Chandra Reddy ASSERT_TRUE(f != nullptr); 10319a6dd33SSiva Chandra Reddy char read_data[sizeof(CONTENT)]; 10419a6dd33SSiva Chandra Reddy ASSERT_EQ(sizeof(CONTENT), 105b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::fread(read_data, 1, sizeof(CONTENT), f)); 10619a6dd33SSiva Chandra Reddy ASSERT_STREQ(read_data, CONTENT); 10719a6dd33SSiva Chandra Reddy 1089db0037bSSiva Chandra Reddy // Reading another time should trigger eof. 1099db0037bSSiva Chandra Reddy ASSERT_NE(sizeof(CONTENT), 110b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::fread(read_data, 1, sizeof(CONTENT), f)); 111b6bc9d72SGuillaume Chatelet ASSERT_NE(LIBC_NAMESPACE::feof(f), 0); 1129db0037bSSiva Chandra Reddy 113b6bc9d72SGuillaume Chatelet ASSERT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET)); 11419a6dd33SSiva Chandra Reddy // Should be an error to write. 115b6bc9d72SGuillaume Chatelet ASSERT_EQ(size_t(0), LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), f)); 116b6bc9d72SGuillaume Chatelet ASSERT_NE(LIBC_NAMESPACE::ferror(f), 0); 11773874f7aSGuillaume Chatelet ASSERT_ERRNO_FAILURE(); 1183eb1e6d8Smichaelrj-google LIBC_NAMESPACE::libc_errno = 0; 11919a6dd33SSiva Chandra Reddy 120b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::clearerr(f); 121b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::ferror(f), 0); 1229db0037bSSiva Chandra Reddy 123b6bc9d72SGuillaume Chatelet ASSERT_EQ(0, LIBC_NAMESPACE::fclose(f)); 12419a6dd33SSiva Chandra Reddy free(ss); 12519a6dd33SSiva Chandra Reddy } 12619a6dd33SSiva Chandra Reddy 12719a6dd33SSiva Chandra Reddy TEST(LlvmLibcFOpenCookie, WriteOnlyCookieTest) { 12819a6dd33SSiva Chandra Reddy size_t INIT_BUFSIZE = 32; 12919a6dd33SSiva Chandra Reddy auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream))); 13019a6dd33SSiva Chandra Reddy ss->buf = reinterpret_cast<char *>(malloc(INIT_BUFSIZE)); 13119a6dd33SSiva Chandra Reddy ss->bufsize = INIT_BUFSIZE; 13219a6dd33SSiva Chandra Reddy ss->offset = 0; 13319a6dd33SSiva Chandra Reddy ss->endpos = 0; 13419a6dd33SSiva Chandra Reddy 135b6bc9d72SGuillaume Chatelet ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "w", STRING_STREAM_FUNCS); 13619a6dd33SSiva Chandra Reddy ASSERT_TRUE(f != nullptr); 13719a6dd33SSiva Chandra Reddy 13819a6dd33SSiva Chandra Reddy constexpr char WRITE_DATA[] = "Hello,writeonly!"; 13919a6dd33SSiva Chandra Reddy ASSERT_EQ(sizeof(WRITE_DATA), 140b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::fwrite(WRITE_DATA, 1, sizeof(WRITE_DATA), f)); 14119a6dd33SSiva Chandra Reddy // Flushing will ensure the data to be written to the string stream. 142b6bc9d72SGuillaume Chatelet ASSERT_EQ(0, LIBC_NAMESPACE::fflush(f)); 14319a6dd33SSiva Chandra Reddy ASSERT_STREQ(WRITE_DATA, ss->buf); 14419a6dd33SSiva Chandra Reddy 145b6bc9d72SGuillaume Chatelet ASSERT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET)); 14619a6dd33SSiva Chandra Reddy char read_data[sizeof(WRITE_DATA)]; 14719a6dd33SSiva Chandra Reddy // Should be an error to read. 148b6bc9d72SGuillaume Chatelet ASSERT_EQ(size_t(0), 149b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::fread(read_data, 1, sizeof(WRITE_DATA), f)); 150b6bc9d72SGuillaume Chatelet ASSERT_NE(LIBC_NAMESPACE::ferror(f), 0); 15173874f7aSGuillaume Chatelet ASSERT_ERRNO_EQ(EBADF); 1523eb1e6d8Smichaelrj-google LIBC_NAMESPACE::libc_errno = 0; 15319a6dd33SSiva Chandra Reddy 154b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::clearerr(f); 155b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::ferror(f), 0); 1569db0037bSSiva Chandra Reddy 157b6bc9d72SGuillaume Chatelet ASSERT_EQ(0, LIBC_NAMESPACE::fclose(f)); 15819a6dd33SSiva Chandra Reddy free(ss); 15919a6dd33SSiva Chandra Reddy } 16019a6dd33SSiva Chandra Reddy 16119a6dd33SSiva Chandra Reddy TEST(LlvmLibcFOpenCookie, AppendOnlyCookieTest) { 16219a6dd33SSiva Chandra Reddy constexpr char INITIAL_CONTENT[] = "1234567890987654321"; 16319a6dd33SSiva Chandra Reddy constexpr char WRITE_DATA[] = "append"; 16419a6dd33SSiva Chandra Reddy auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream))); 16519a6dd33SSiva Chandra Reddy ss->buf = reinterpret_cast<char *>(malloc(sizeof(INITIAL_CONTENT))); 16619a6dd33SSiva Chandra Reddy ss->bufsize = sizeof(INITIAL_CONTENT); 16719a6dd33SSiva Chandra Reddy ss->offset = ss->bufsize; // We want to open the file in append mode. 16819a6dd33SSiva Chandra Reddy ss->endpos = ss->bufsize; 16919a6dd33SSiva Chandra Reddy for (size_t i = 0; i < sizeof(INITIAL_CONTENT); ++i) 17019a6dd33SSiva Chandra Reddy ss->buf[i] = INITIAL_CONTENT[i]; 17119a6dd33SSiva Chandra Reddy 172b6bc9d72SGuillaume Chatelet ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "a", STRING_STREAM_FUNCS); 17319a6dd33SSiva Chandra Reddy ASSERT_TRUE(f != nullptr); 17419a6dd33SSiva Chandra Reddy 17519a6dd33SSiva Chandra Reddy constexpr size_t READ_SIZE = 5; 17619a6dd33SSiva Chandra Reddy char read_data[READ_SIZE]; 17719a6dd33SSiva Chandra Reddy // This is not a readable file. 178b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::fread(read_data, 1, READ_SIZE, f), size_t(0)); 179b6bc9d72SGuillaume Chatelet ASSERT_NE(LIBC_NAMESPACE::ferror(f), 0); 18073874f7aSGuillaume Chatelet ASSERT_ERRNO_FAILURE(); 1813eb1e6d8Smichaelrj-google LIBC_NAMESPACE::libc_errno = 0; 18219a6dd33SSiva Chandra Reddy 183b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::clearerr(f); 184b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::ferror(f), 0); 1859db0037bSSiva Chandra Reddy 186b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::fwrite(WRITE_DATA, 1, sizeof(WRITE_DATA), f), 18719a6dd33SSiva Chandra Reddy sizeof(WRITE_DATA)); 188b6bc9d72SGuillaume Chatelet EXPECT_EQ(LIBC_NAMESPACE::fflush(f), 0); 18919a6dd33SSiva Chandra Reddy EXPECT_EQ(ss->endpos, sizeof(WRITE_DATA) + sizeof(INITIAL_CONTENT)); 19019a6dd33SSiva Chandra Reddy 191b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::fclose(f), 0); 19219a6dd33SSiva Chandra Reddy free(ss); 19319a6dd33SSiva Chandra Reddy } 19419a6dd33SSiva Chandra Reddy 19519a6dd33SSiva Chandra Reddy TEST(LlvmLibcFOpenCookie, ReadUpdateCookieTest) { 19619a6dd33SSiva Chandra Reddy const char INITIAL_CONTENT[] = "1234567890987654321"; 19719a6dd33SSiva Chandra Reddy auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream))); 19819a6dd33SSiva Chandra Reddy ss->buf = reinterpret_cast<char *>(malloc(sizeof(INITIAL_CONTENT))); 19919a6dd33SSiva Chandra Reddy ss->bufsize = sizeof(INITIAL_CONTENT); 20019a6dd33SSiva Chandra Reddy ss->offset = 0; 20119a6dd33SSiva Chandra Reddy ss->endpos = ss->bufsize; 20219a6dd33SSiva Chandra Reddy for (size_t i = 0; i < sizeof(INITIAL_CONTENT); ++i) 20319a6dd33SSiva Chandra Reddy ss->buf[i] = INITIAL_CONTENT[i]; 20419a6dd33SSiva Chandra Reddy 205b6bc9d72SGuillaume Chatelet ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "r+", STRING_STREAM_FUNCS); 20619a6dd33SSiva Chandra Reddy ASSERT_TRUE(f != nullptr); 20719a6dd33SSiva Chandra Reddy 20819a6dd33SSiva Chandra Reddy constexpr size_t READ_SIZE = sizeof(INITIAL_CONTENT) / 2; 20919a6dd33SSiva Chandra Reddy char read_data[READ_SIZE]; 210b6bc9d72SGuillaume Chatelet ASSERT_EQ(READ_SIZE, LIBC_NAMESPACE::fread(read_data, 1, READ_SIZE, f)); 21119a6dd33SSiva Chandra Reddy 21219a6dd33SSiva Chandra Reddy MemoryView src1(INITIAL_CONTENT, READ_SIZE), dst1(read_data, READ_SIZE); 21319a6dd33SSiva Chandra Reddy EXPECT_MEM_EQ(src1, dst1); 21419a6dd33SSiva Chandra Reddy 215b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::fseek(f, 0, SEEK_SET), 0); 21619a6dd33SSiva Chandra Reddy constexpr char WRITE_DATA[] = "hello, file"; 21719a6dd33SSiva Chandra Reddy ASSERT_EQ(sizeof(WRITE_DATA), 218b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::fwrite(WRITE_DATA, 1, sizeof(WRITE_DATA), f)); 219b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::fflush(f), 0); 22019a6dd33SSiva Chandra Reddy EXPECT_STREQ(ss->buf, WRITE_DATA); 22119a6dd33SSiva Chandra Reddy 222b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::fclose(f), 0); 22319a6dd33SSiva Chandra Reddy free(ss); 22419a6dd33SSiva Chandra Reddy } 22519a6dd33SSiva Chandra Reddy 22619a6dd33SSiva Chandra Reddy TEST(LlvmLibcFOpenCookie, WriteUpdateCookieTest) { 22719a6dd33SSiva Chandra Reddy constexpr char WRITE_DATA[] = "hello, file"; 22819a6dd33SSiva Chandra Reddy auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream))); 22919a6dd33SSiva Chandra Reddy ss->buf = reinterpret_cast<char *>(malloc(sizeof(WRITE_DATA))); 23019a6dd33SSiva Chandra Reddy ss->bufsize = sizeof(WRITE_DATA); 23119a6dd33SSiva Chandra Reddy ss->offset = 0; 23219a6dd33SSiva Chandra Reddy ss->endpos = 0; 23319a6dd33SSiva Chandra Reddy 234b6bc9d72SGuillaume Chatelet ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "w+", STRING_STREAM_FUNCS); 23519a6dd33SSiva Chandra Reddy ASSERT_TRUE(f != nullptr); 23619a6dd33SSiva Chandra Reddy 23719a6dd33SSiva Chandra Reddy ASSERT_EQ(sizeof(WRITE_DATA), 238b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::fwrite(WRITE_DATA, 1, sizeof(WRITE_DATA), f)); 23919a6dd33SSiva Chandra Reddy 240b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::fseek(f, 0, SEEK_SET), 0); 24119a6dd33SSiva Chandra Reddy 24219a6dd33SSiva Chandra Reddy char read_data[sizeof(WRITE_DATA)]; 243b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::fread(read_data, 1, sizeof(read_data), f), 24419a6dd33SSiva Chandra Reddy sizeof(read_data)); 24519a6dd33SSiva Chandra Reddy EXPECT_STREQ(read_data, WRITE_DATA); 24619a6dd33SSiva Chandra Reddy 247b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::fclose(f), 0); 24819a6dd33SSiva Chandra Reddy free(ss); 24919a6dd33SSiva Chandra Reddy } 250