Loading...
tests/fflush.c Libc-1725.40.4 /dev/null
--- Libc/Libc-1725.40.4/tests/fflush.c
+++ /dev/null
@@ -1,458 +0,0 @@
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <darwintest.h>
-#include <darwintest_utils.h>
-
-#include "libc_hooks_helper.h"
-
-static char tmpfile_template[] = "/tmp/libc_test_fflushXXXXX";
-#define BUFSZ 128
-static char wrbuf[BUFSZ] = "";
-static const size_t filesz = BUFSZ * 120;
-
-static void
-cleanup_tmp_file(void)
-{
-	(void)unlink(tmpfile_template);
-}
-
-static const char *
-assert_empty_tmp_file(void)
-{
-	T_SETUPBEGIN;
-
-	int tmpfd = mkstemp(tmpfile_template);
-	T_ASSERT_POSIX_SUCCESS(tmpfd, "created tmp file at %s", tmpfile_template);
-	T_ATEND(cleanup_tmp_file);
-	close(tmpfd);
-
-	T_SETUPEND;
-
-	return tmpfile_template;
-}
-
-static const char *
-assert_full_tmp_file(void)
-{
-	T_SETUPBEGIN;
-
-	int tmpfd = mkstemp(tmpfile_template);
-	T_ASSERT_POSIX_SUCCESS(tmpfd, "created tmp file at %s", tmpfile_template);
-	T_ATEND(cleanup_tmp_file);
-
-	/*
-	 * Write a pattern of bytes into the file -- the lowercase alphabet,
-	 * separated by newlines.
-	 */
-	for (size_t i = 0; i < BUFSZ; i++) {
-		wrbuf[i] = 'a' + (i % 27);
-		if (i % 27 == 26) {
-			wrbuf[i] = '\n';
-		}
-	}
-	for (size_t i = 0; i < filesz; i++) {
-		ssize_t byteswr = 0;
-		do {
-			byteswr = write(tmpfd, wrbuf, BUFSZ);
-		} while (byteswr == -1 && errno == EAGAIN);
-
-		T_QUIET; T_ASSERT_POSIX_SUCCESS(byteswr, "wrote %d bytes to tmp file",
-				BUFSZ);
-		T_QUIET; T_ASSERT_EQ(byteswr, (ssize_t)BUFSZ,
-				"wrote correct amount of bytes to tmp file");
-	}
-
-	close(tmpfd);
-
-	T_SETUPEND;
-
-	return tmpfile_template;
-}
-
-/*
- * Ensure that fflush on an input stream conforms to the SUSv3 definition, which
- * requires synchronizing the FILE position with the underlying file descriptor.
- */
-T_DECL(fflush_input, "fflush on a read-only FILE resets fd offset")
-{
-	const char *tmpfile = assert_full_tmp_file();
-
-	T_SETUPBEGIN;
-
-	FILE *tmpf = fopen(tmpfile, "r");
-	T_QUIET; T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(tmpf, "opened tmp file for reading");
-
-	/*
-	 * Move some way into the file.
-	 */
-	char buf[100] = "";
-	size_t nread = fread(buf, sizeof(buf), 1, tmpf);
-	T_ASSERT_EQ(nread, (size_t)1, "read correct number of items from FILE");
-	char last_read_char = buf[sizeof(buf) - 1];
-
-	off_t curoff = lseek(fileno(tmpf), 0, SEEK_CUR);
-	T_ASSERT_GT(curoff, (off_t)0, "file offset should be non-zero");
-
-	T_SETUPEND;
-
-	/*
-	 * fflush(3) to reset the fd back to the FILE offset.
-	 */
-	int ret = fflush(tmpf);
-	T_ASSERT_POSIX_SUCCESS(ret, "fflush on read-only FILE");
-
-	off_t flushoff = lseek(fileno(tmpf), 0, SEEK_CUR);
-	T_ASSERT_EQ(flushoff, (off_t)sizeof(buf),
-			"offset of file should be bytes read on FILE after fflush");
-
-	/*
-	 * Make sure the FILE is reading the right thing -- the next character
-	 * should be one letter after the last byte read, from the last call to
-	 * fread(3).
-	 */
-	char c = '\0';
-	nread = fread(&c, sizeof(c), 1, tmpf);
-	T_QUIET;
-	T_ASSERT_EQ(nread, (size_t)1, "read correct number of items from FILE");
-
-	/*
-	 * The pattern in the file is the alphabet -- and this doesn't land on
-	 * a newline.
-	 */
-	T_QUIET;
-	T_ASSERT_NE((flushoff) % 27, (off_t)0,
-			"previous offset shouldn't land on newline");
-	T_QUIET;
-	T_ASSERT_NE((flushoff + 1) % 27, (off_t)0,
-			"current offset shouldn't land on newline");
-
-	T_ASSERT_EQ(c, last_read_char + 1, "read correct byte after fflush");
-
-	ret = fflush(tmpf);
-	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "fflush on read-only FILE");
-
-	flushoff = lseek(fileno(tmpf), 0, SEEK_CUR);
-	T_ASSERT_EQ(flushoff, (off_t)(sizeof(buf) + sizeof(c)),
-			"offset of file should be incremented after subsequent read");
-
-	/*
-	 * Use ungetc(3) to induce the optimized ungetc behavior in the FILE.
-	 */
-	int ugret = ungetc(c, tmpf);
-	T_QUIET; T_ASSERT_NE(ugret, EOF, "ungetc after fflush");
-	T_QUIET; T_ASSERT_EQ((char)ugret, c, "ungetc un-got the correct char");
-
-	ret = fflush(tmpf);
-	T_ASSERT_POSIX_SUCCESS(ret, "fflush after ungetc");
-	flushoff = lseek(fileno(tmpf), 0, SEEK_CUR);
-	T_ASSERT_EQ(flushoff, (off_t)sizeof(buf),
-			"offset of file should be correct after ungetc and fflush");
-
-	nread = fread(&c, sizeof(c), 1, tmpf);
-	T_QUIET;
-	T_ASSERT_EQ(nread, (size_t)1, "read correct number of items from FILE");
-	T_ASSERT_EQ(c, last_read_char + 1,
-			"read correct byte after ungetc and fflush");
-}
-
-/*
- * Try to trick fclose into not reporting an ENOSPC error from the underlying
- * descriptor in update mode.  Previous versions of Libc only flushed the FILE
- * if it was write-only.
- */
-
-#if TARGET_OS_OSX
-/*
- * Only macOS contains a version of hdiutil that can create disk images.
- */
-
-#define DMGFILE "/tmp/test_fclose_enospc.dmg"
-#define VOLNAME "test_fclose_enospc"
-static const char *small_file = "/Volumes/" VOLNAME "/test.txt";
-
-static void
-cleanup_dmg(void)
-{
-	char *hdiutil_detach_argv[] = {
-		"/usr/bin/hdiutil", "detach", "/Volumes/" VOLNAME, NULL,
-	};
-	pid_t hdiutil_detach = -1;
-	int ret = dt_launch_tool(&hdiutil_detach, hdiutil_detach_argv, false, NULL,
-			NULL);
-	if (ret != -1) {
-		int status = 0;
-		(void)waitpid(hdiutil_detach, &status, 0);
-	}
-	(void)unlink(DMGFILE);
-}
-
-T_DECL(fclose_enospc, "ensure ENOSPC is preserved on fclose")
-{
-	T_SETUPBEGIN;
-
-	/*
-	 * Ensure a disk is available that will fill up and start returning ENOSPC.
-	 *
-	 * system(3) would be easier...
-	 */
-	char *hdiutil_argv[] = {
-		"/usr/bin/hdiutil", "create", "-size", "10m", "-type", "UDIF",
-		"-volname", VOLNAME, "-nospotlight", "-fs", "HFS+", DMGFILE, "-attach",
-		NULL,
-	};
-	pid_t hdiutil_create = -1;
-	int ret = dt_launch_tool(&hdiutil_create, hdiutil_argv, false, NULL, NULL);
-	T_ASSERT_POSIX_SUCCESS(ret, "created and attached 10MB DMG");
-	T_ATEND(cleanup_dmg);
-	int status = 0;
-	pid_t waited = waitpid(hdiutil_create, &status, 0);
-	T_QUIET; T_ASSERT_EQ(waited, hdiutil_create,
-			"should have waited for the process that was launched");
-	T_QUIET;
-	T_ASSERT_TRUE(WIFEXITED(status), "hdiutil should have exited");
-	T_QUIET;
-	T_ASSERT_EQ(WEXITSTATUS(status), 0,
-			"hdiutil should have exited successfully");
-
-	/*
-	 * Open for updating, as previously only write-only files would be flushed
-	 * on fclose.
-	 */
-	FILE *fp = fopen(small_file, "a+");
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fp, "opened file at %s for append-updating", small_file);
-
-	char *buf = malloc(BUFSIZ);
-	T_QUIET; T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(buf, "should allocate BUFSIZ bytes");
-
-	for (int i = 0; i < BUFSIZ; i++) {
-		buf[i] = (char)(i % 256);
-	}
-
-	/*
-	 * Fill up the disk -- induce ENOSPC.
-	 */
-	size_t wrsize = BUFSIZ;
-	for (int i = 0; i < 2; i++) {
-		for (;;) {
-			errno = 0;
-			if (write(fileno(fp), buf, wrsize) < 0) {
-				if (errno == ENOSPC) {
-					break;
-				}
-				T_WITH_ERRNO; T_ASSERT_FAIL("write(2) failed");
-			}
-		}
-		wrsize = 1;
-	}
-	T_PASS("filled up the file until ENOSPC");
-	free(buf);
-
-	/*
-	 * Make sure the FILE is at the end, so any writes it does hit ENOSPC.
-	 */
-	ret = fseek(fp, 0, SEEK_END);
-	T_ASSERT_POSIX_SUCCESS(ret, "fseek to the end of a complete file");
-
-	/*
-	 * Try to push a character into the file; since this is buffered, it should
-	 * succeed.
-	 */
-	ret = fputc('a', fp);
-	T_ASSERT_POSIX_SUCCESS(ret,
-			"fputc to put an additional character in the FILE");
-
-	T_SETUPEND;
-
-	/*
-	 * fclose should catch the ENOSPC error when it flushes the file, before it
-	 * closes the underlying descriptor.
-	 */
-	errno = 0;
-	ret = fclose(fp);
-	if (ret != EOF) {
-		T_ASSERT_FAIL("fclose should fail when the FILE is full");
-	}
-	if (errno != ENOSPC) {
-		T_WITH_ERRNO; T_ASSERT_FAIL("fclose should fail with ENOSPC");
-	}
-
-	T_PASS("fclose returned ENOSPC");
-}
-
-#endif // TARGET_OS_OSX
-
-/*
- * Ensure no errors are returned when flushing a read-only, unseekable input
- * stream.
- */
-T_DECL(fflush_unseekable_input,
-		"ensure sanity when an unseekable input stream is flushed")
-{
-	T_SETUPBEGIN;
-
-	/*
-	 * Use a pipe for the unseekable streams.
-	 */
-	int pipes[2];
-	int ret = pipe(pipes);
-	T_ASSERT_POSIX_SUCCESS(ret, "create a pipe");
-	FILE *in = fdopen(pipes[0], "r");
-	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(in,
-			"open input stream to read end of pipe");
-	FILE *out = fdopen(pipes[1], "w");
-	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(out,
-			"open output stream to write end of pipe");
-
-	/*
-	 * Fill the pipe with some text (but not too much that the write would
-	 * block!).
-	 */
-	fprintf(out, "this is a test and has some more text");
-	ret = fflush(out);
-	T_ASSERT_POSIX_SUCCESS(ret, "flushed the output stream");
-
-	/*
-	 * Protect stdio from delving too deep into the pipe.
-	 */
-	char inbuf[8] = {};
-	setbuffer(in, inbuf, sizeof(inbuf));
-
-	/*
-	 * Just read a teensy bit to get the FILE offset different from the
-	 * descriptor "offset."
-	 */
-	char rdbuf[2] = {};
-	size_t nitems = fread(rdbuf, sizeof(rdbuf), 1, in);
-	T_QUIET; T_ASSERT_GT(nitems, (size_t)0,
-			"read from the read end of the pipe");
-
-	T_SETUPEND;
-
-	ret = fflush(in);
-	T_ASSERT_POSIX_SUCCESS(ret,
-			"should successfully flush unseekable input stream after reading");
-}
-
-/*
- * Ensure that reading to the end of a file and then calling ftell() still
- * causes EOF.
- */
-T_DECL(ftell_feof,
-		"ensure ftell does not reset feof when actually at end of file") {
-	T_SETUPBEGIN;
-	FILE *fp = fopen("/System/Library/CoreServices/SystemVersion.plist", "rb");
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fp, "opened SystemVersion.plist");
-	struct stat sb;
-	T_ASSERT_POSIX_SUCCESS(fstat(fileno(fp), &sb), "fstat SystemVersion.plist");
-	void *buf = malloc((size_t)(sb.st_size * 2));
-	T_ASSERT_NOTNULL(buf, "allocating buffer for size of SystemVersion.plist");
-	T_SETUPEND;
-
-	T_ASSERT_POSIX_SUCCESS(fseek(fp, 0, SEEK_SET), "seek to beginning");
-	// fread can return short *or* zero, according to manpage
-	fread(buf, (size_t)(sb.st_size * 2), 1, fp);
-	T_ASSERT_EQ(ftell(fp), (long)sb.st_size, "ftell() == file size");
-	T_ASSERT_TRUE(feof(fp), "feof() reports end-of-file");
-	free(buf);
-}
-
-T_DECL(putc_flush, "ensure putc flushes to file on close") {
-	const char *fname = assert_empty_tmp_file();
-	FILE *fp = fopen(fname, "w");
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fp, "opened temporary file read/write");
-	T_WITH_ERRNO;
-	T_ASSERT_EQ(fwrite("testing", 1, 7, fp), 7UL, "write temp contents");
-	(void)fclose(fp);
-
-	fp = fopen(fname, "r+");
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fp, "opened temporary file read/write");
-
-	T_ASSERT_POSIX_SUCCESS(fseek(fp, -1, SEEK_END), "seek to end - 1");
-	T_ASSERT_EQ(fgetc(fp), 'g', "fgetc should read 'g'");
-	T_ASSERT_EQ(fgetc(fp), EOF, "fgetc should read EOF");
-	T_ASSERT_EQ(ftell(fp), 7L, "ftell should report position 7");
-
-	int ret = fputc('!', fp);
-	T_ASSERT_POSIX_SUCCESS(ret,
-			"fputc to put an additional character in the FILE");
-	T_ASSERT_EQ(ftell(fp), 8L, "ftell should report position 8");
-
-	T_QUIET;
-	T_ASSERT_POSIX_SUCCESS(fclose(fp), "close temp file");
-
-	fp = fopen(fname, "r");
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fp, "opened temporary file read/write");
-
-	char buf[9];
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fgets(buf, sizeof(buf), fp), "read file data");
-	T_ASSERT_EQ_STR(buf, "testing!", "read all the new data");
-
-	(void)fclose(fp);
-}
-
-T_DECL(putc_writedrop, "ensure writes are flushed with a pending read buffer") {
-	const char *fname = assert_empty_tmp_file();
-	FILE *fp = fopen(fname, "w");
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fp, "opened temporary file read/write");
-	T_WITH_ERRNO;
-	T_ASSERT_EQ(fwrite("testing", 1, 7, fp), 7UL, "write temp contents");
-	(void)fclose(fp);
-
-	fp = fopen(fname, "r+");
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fp, "opened temporary file read/write");
-
-	T_ASSERT_POSIX_SUCCESS(fseek(fp, -1, SEEK_END), "seek to end - 1");
-
-	int ret = fputc('!', fp);
-	T_ASSERT_POSIX_SUCCESS(ret,
-			"fputc to put an additional character in the FILE");
-	// flush the write buffer by reading a byte from the stream to put the
-	// FILE* into read mode
-	T_ASSERT_EQ(fgetc(fp), EOF, "fgetc should read EOF");
-
-	T_QUIET;
-	T_ASSERT_POSIX_SUCCESS(fclose(fp), "close temp file");
-
-	fp = fopen(fname, "r");
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fp, "opened temporary file read/write");
-
-	char buf[9];
-	T_WITH_ERRNO;
-	T_ASSERT_NOTNULL(fgets(buf, sizeof(buf), fp), "read file data");
-	T_ASSERT_EQ_STR(buf, "testin!", "read all the new data");
-
-	(void)fclose(fp);
-}
-
-T_DECL(libc_hooks_fflush, "Test libv_hooks for fflush")
-{
-    // Setup
-    T_SETUPBEGIN;
-    FILE *f = fopen("/dev/null", "w");
-    T_SETUPEND;
-
-    // Test
-    libc_hooks_log_start();
-    fflush(f);
-    libc_hooks_log_stop(1);
-
-    // Check
-    T_LOG("fflush(f)");
-    libc_hooks_log_expect(LIBC_HOOKS_LOG(libc_hooks_will_write, f, sizeof(*f)), "checking f");
-
-    // Cleanup
-    fclose(f);
-}