249 lines · cpp
1//===-- Unittests for the fopencookie function ----------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "src/stdio/clearerr.h"10#include "src/stdio/fclose.h"11#include "src/stdio/feof.h"12#include "src/stdio/ferror.h"13#include "src/stdio/fflush.h"14#include "src/stdio/fopencookie.h"15#include "src/stdio/fread.h"16#include "src/stdio/fseek.h"17#include "src/stdio/fwrite.h"18#include "test/UnitTest/ErrnoCheckingTest.h"19#include "test/UnitTest/MemoryMatcher.h"20#include "test/UnitTest/Test.h"21 22#include "hdr/stdio_macros.h"23#include "hdr/types/size_t.h"24#include "src/__support/libc_errno.h"25 26using LlvmLibcFOpenCookieTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;27using MemoryView = LIBC_NAMESPACE::testing::MemoryView;28 29struct StringStream {30 char *buf;31 size_t bufsize; // Size of buf32 size_t endpos; // 1 more than current fill size33 size_t offset; // Current read/write location34};35 36ssize_t write_ss(void *cookie, const char *buf, size_t size) {37 auto *ss = reinterpret_cast<StringStream *>(cookie);38 if (ss->offset + size > ss->bufsize)39 ss->buf =40 reinterpret_cast<char *>(realloc(ss->buf, (ss->offset + size) * 2));41 for (size_t i = 0; i < size; ++i, ss->offset += 1)42 ss->buf[ss->offset] = buf[i];43 if (ss->offset > ss->endpos)44 ss->endpos = ss->offset;45 return size;46}47 48ssize_t read_ss(void *cookie, char *buf, size_t size) {49 auto *ss = reinterpret_cast<StringStream *>(cookie);50 ssize_t copysize = size;51 if (ss->offset + size > ss->endpos) {52 // You cannot copy more than what you have available.53 copysize = ss->endpos - ss->offset;54 if (copysize < 0)55 copysize = 0; // A seek could have moved offset past the endpos56 }57 for (size_t i = 0; i < size_t(copysize); ++i, ++ss->offset)58 buf[i] = ss->buf[ss->offset];59 return copysize;60}61 62int seek_ss(void *cookie, off64_t *offset, int whence) {63 auto *ss = reinterpret_cast<StringStream *>(cookie);64 off64_t new_offset;65 if (whence == SEEK_SET) {66 new_offset = *offset;67 } else if (whence == SEEK_CUR) {68 new_offset = *offset + ss->offset;69 } else if (whence == SEEK_END) {70 new_offset = *offset + ss->endpos;71 } else {72 libc_errno = EINVAL;73 return -1;74 }75 if (new_offset < 0 || size_t(new_offset) > ss->bufsize)76 return -1;77 ss->offset = new_offset;78 *offset = new_offset;79 return 0;80}81 82int close_ss(void *cookie) {83 auto *ss = reinterpret_cast<StringStream *>(cookie);84 free(ss->buf);85 ss->buf = nullptr;86 ss->bufsize = ss->endpos = ss->offset = 0;87 return 0;88}89 90constexpr cookie_io_functions_t STRING_STREAM_FUNCS = {&read_ss, &write_ss,91 &seek_ss, &close_ss};92 93TEST_F(LlvmLibcFOpenCookieTest, ReadOnlyCookieTest) {94 constexpr char CONTENT[] = "Hello,readonly!";95 auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream)));96 ss->buf = reinterpret_cast<char *>(malloc(sizeof(CONTENT)));97 ss->bufsize = sizeof(CONTENT);98 ss->offset = 0;99 ss->endpos = ss->bufsize;100 for (size_t i = 0; i < sizeof(CONTENT); ++i)101 ss->buf[i] = CONTENT[i];102 103 ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "r", STRING_STREAM_FUNCS);104 ASSERT_TRUE(f != nullptr);105 char read_data[sizeof(CONTENT)];106 ASSERT_EQ(sizeof(CONTENT),107 LIBC_NAMESPACE::fread(read_data, 1, sizeof(CONTENT), f));108 ASSERT_STREQ(read_data, CONTENT);109 110 // Reading another time should trigger eof.111 ASSERT_NE(sizeof(CONTENT),112 LIBC_NAMESPACE::fread(read_data, 1, sizeof(CONTENT), f));113 ASSERT_NE(LIBC_NAMESPACE::feof(f), 0);114 115 ASSERT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET));116 // Should be an error to write.117 ASSERT_EQ(size_t(0), LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), f));118 ASSERT_NE(LIBC_NAMESPACE::ferror(f), 0);119 ASSERT_ERRNO_FAILURE();120 121 LIBC_NAMESPACE::clearerr(f);122 ASSERT_EQ(LIBC_NAMESPACE::ferror(f), 0);123 124 ASSERT_EQ(0, LIBC_NAMESPACE::fclose(f));125 free(ss);126}127 128TEST_F(LlvmLibcFOpenCookieTest, WriteOnlyCookieTest) {129 size_t INIT_BUFSIZE = 32;130 auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream)));131 ss->buf = reinterpret_cast<char *>(malloc(INIT_BUFSIZE));132 ss->bufsize = INIT_BUFSIZE;133 ss->offset = 0;134 ss->endpos = 0;135 136 ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "w", STRING_STREAM_FUNCS);137 ASSERT_TRUE(f != nullptr);138 139 constexpr char WRITE_DATA[] = "Hello,writeonly!";140 ASSERT_EQ(sizeof(WRITE_DATA),141 LIBC_NAMESPACE::fwrite(WRITE_DATA, 1, sizeof(WRITE_DATA), f));142 // Flushing will ensure the data to be written to the string stream.143 ASSERT_EQ(0, LIBC_NAMESPACE::fflush(f));144 ASSERT_STREQ(WRITE_DATA, ss->buf);145 146 ASSERT_EQ(0, LIBC_NAMESPACE::fseek(f, 0, SEEK_SET));147 char read_data[sizeof(WRITE_DATA)];148 // Should be an error to read.149 ASSERT_EQ(size_t(0),150 LIBC_NAMESPACE::fread(read_data, 1, sizeof(WRITE_DATA), f));151 ASSERT_NE(LIBC_NAMESPACE::ferror(f), 0);152 ASSERT_ERRNO_EQ(EBADF);153 154 LIBC_NAMESPACE::clearerr(f);155 ASSERT_EQ(LIBC_NAMESPACE::ferror(f), 0);156 157 ASSERT_EQ(0, LIBC_NAMESPACE::fclose(f));158 free(ss);159}160 161TEST_F(LlvmLibcFOpenCookieTest, AppendOnlyCookieTest) {162 constexpr char INITIAL_CONTENT[] = "1234567890987654321";163 constexpr char WRITE_DATA[] = "append";164 auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream)));165 ss->buf = reinterpret_cast<char *>(malloc(sizeof(INITIAL_CONTENT)));166 ss->bufsize = sizeof(INITIAL_CONTENT);167 ss->offset = ss->bufsize; // We want to open the file in append mode.168 ss->endpos = ss->bufsize;169 for (size_t i = 0; i < sizeof(INITIAL_CONTENT); ++i)170 ss->buf[i] = INITIAL_CONTENT[i];171 172 ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "a", STRING_STREAM_FUNCS);173 ASSERT_TRUE(f != nullptr);174 175 constexpr size_t READ_SIZE = 5;176 char read_data[READ_SIZE];177 // This is not a readable file.178 ASSERT_EQ(LIBC_NAMESPACE::fread(read_data, 1, READ_SIZE, f), size_t(0));179 ASSERT_NE(LIBC_NAMESPACE::ferror(f), 0);180 ASSERT_ERRNO_FAILURE();181 182 LIBC_NAMESPACE::clearerr(f);183 ASSERT_EQ(LIBC_NAMESPACE::ferror(f), 0);184 185 ASSERT_EQ(LIBC_NAMESPACE::fwrite(WRITE_DATA, 1, sizeof(WRITE_DATA), f),186 sizeof(WRITE_DATA));187 EXPECT_EQ(LIBC_NAMESPACE::fflush(f), 0);188 EXPECT_EQ(ss->endpos, sizeof(WRITE_DATA) + sizeof(INITIAL_CONTENT));189 190 ASSERT_EQ(LIBC_NAMESPACE::fclose(f), 0);191 free(ss);192}193 194TEST_F(LlvmLibcFOpenCookieTest, ReadUpdateCookieTest) {195 const char INITIAL_CONTENT[] = "1234567890987654321";196 auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream)));197 ss->buf = reinterpret_cast<char *>(malloc(sizeof(INITIAL_CONTENT)));198 ss->bufsize = sizeof(INITIAL_CONTENT);199 ss->offset = 0;200 ss->endpos = ss->bufsize;201 for (size_t i = 0; i < sizeof(INITIAL_CONTENT); ++i)202 ss->buf[i] = INITIAL_CONTENT[i];203 204 ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "r+", STRING_STREAM_FUNCS);205 ASSERT_TRUE(f != nullptr);206 207 constexpr size_t READ_SIZE = sizeof(INITIAL_CONTENT) / 2;208 char read_data[READ_SIZE];209 ASSERT_EQ(READ_SIZE, LIBC_NAMESPACE::fread(read_data, 1, READ_SIZE, f));210 211 MemoryView src1(INITIAL_CONTENT, READ_SIZE), dst1(read_data, READ_SIZE);212 EXPECT_MEM_EQ(src1, dst1);213 214 ASSERT_EQ(LIBC_NAMESPACE::fseek(f, 0, SEEK_SET), 0);215 constexpr char WRITE_DATA[] = "hello, file";216 ASSERT_EQ(sizeof(WRITE_DATA),217 LIBC_NAMESPACE::fwrite(WRITE_DATA, 1, sizeof(WRITE_DATA), f));218 ASSERT_EQ(LIBC_NAMESPACE::fflush(f), 0);219 EXPECT_STREQ(ss->buf, WRITE_DATA);220 221 ASSERT_EQ(LIBC_NAMESPACE::fclose(f), 0);222 free(ss);223}224 225TEST_F(LlvmLibcFOpenCookieTest, WriteUpdateCookieTest) {226 constexpr char WRITE_DATA[] = "hello, file";227 auto *ss = reinterpret_cast<StringStream *>(malloc(sizeof(StringStream)));228 ss->buf = reinterpret_cast<char *>(malloc(sizeof(WRITE_DATA)));229 ss->bufsize = sizeof(WRITE_DATA);230 ss->offset = 0;231 ss->endpos = 0;232 233 ::FILE *f = LIBC_NAMESPACE::fopencookie(ss, "w+", STRING_STREAM_FUNCS);234 ASSERT_TRUE(f != nullptr);235 236 ASSERT_EQ(sizeof(WRITE_DATA),237 LIBC_NAMESPACE::fwrite(WRITE_DATA, 1, sizeof(WRITE_DATA), f));238 239 ASSERT_EQ(LIBC_NAMESPACE::fseek(f, 0, SEEK_SET), 0);240 241 char read_data[sizeof(WRITE_DATA)];242 ASSERT_EQ(LIBC_NAMESPACE::fread(read_data, 1, sizeof(read_data), f),243 sizeof(read_data));244 EXPECT_STREQ(read_data, WRITE_DATA);245 246 ASSERT_EQ(LIBC_NAMESPACE::fclose(f), 0);247 free(ss);248}249