libio: Add test case for fflush
Checks
Context |
Check |
Description |
redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
redhat-pt-bot/TryBot-32bit |
success
|
Build for i686
|
linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_glibc_check--master-arm |
success
|
Test passed
|
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
success
|
Test passed
|
Commit Message
Hello,
This patch adds a test to verify that `fflush (FILE)` and `fflush (NULL)` are
semantically equivalent from the FILE perspective.
Fred.
--- 8< ---
Subject: [PATCH] libio: Add test case for fflush
Since one path uses _IO_SYNC and the other _IO_OVERFLOW, the newly added
test case verifies that `fflush (FILE)` and `fflush (NULL)` are
semantically equivalent from the FILE perspective.
---
libio/Makefile | 1 +
libio/tst-fflush.c | 171 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 172 insertions(+)
create mode 100644 libio/tst-fflush.c
Comments
* Frédéric Bérat:
> + TEST_VERIFY_EXIT (fstat (files[to_check].fd, &stats) >= 0);
> + TEST_VERIFY_EXIT (stats.st_size == 3);
I think we should add one more test here:
TEST_COMPARE (lseek (files[to_check].fd, 0, SEEK_CUR), 3);
Is there a reason why you do not test the behavior on read-only and
read-write streams? Even for read-only streams and read-write streams
that are currently reading, there is an observable file position update
on the underlying file description (just like above for streams written
to), and any pending ungetc calls are cancelled.
I think fflush (NULL) currently does not flush read-only streams at
least, so we do have bugs here.
Thanks,
Florian
On Tue, Oct 22, 2024 at 4:15 PM Florian Weimer <fweimer@redhat.com> wrote:
> * Frédéric Bérat:
>
> > + TEST_VERIFY_EXIT (fstat (files[to_check].fd, &stats) >= 0);
> > + TEST_VERIFY_EXIT (stats.st_size == 3);
>
> I think we should add one more test here:
>
> TEST_COMPARE (lseek (files[to_check].fd, 0, SEEK_CUR), 3);
>
> Is there a reason why you do not test the behavior on read-only and
> read-write streams? Even for read-only streams and read-write streams
> that are currently reading, there is an observable file position update
> on the underlying file description (just like above for streams written
> to), and any pending ungetc calls are cancelled.
>
I actually assumed it was already covered by unget{w}c tests. But I may
have been wrong.
>
> I think fflush (NULL) currently does not flush read-only streams at
> least, so we do have bugs here.
>
Ok, I'll have a look.
>
> Thanks,
> Florian
>
>
@@ -98,6 +98,7 @@ tests = \
tst-fclose-unopened \
tst-fclose-unopened2 \
tst-fdopen-seek-failure \
+ tst-fflush \
tst-fgetc-after-eof \
tst-fgetwc \
tst-fgetws \
new file mode 100644
@@ -0,0 +1,171 @@
+/* Test that fflush (FILE) and fflush (NULL) are semantically equivalent.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* A success on this test doesn't imply the effectiveness of fflush as
+ we can't ensure that the file wasn't already in the expected state
+ before the call of the function. It only ensures that, if the test
+ fails, fflush is broken. */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+#define CONTENT_SZ_MAX 32
+#define TEST_FILE_COUNT 10
+
+#define FILE_FLUSH 0
+#define GLOBAL_FLUSH 1
+
+#define FILE_UNCHANGED 0
+#define FILE_CHANGED 1
+
+struct
+{
+ FILE *file;
+ char *name;
+ int fd;
+ char *mfile;
+} files[TEST_FILE_COUNT];
+
+static void
+base_init (int file)
+{
+ files[file].file = NULL;
+ files[file].fd = -1;
+ files[file].name = NULL;
+ files[file].mfile = NULL;
+}
+
+static void
+file_init (int file)
+{
+ int fd = -1;
+
+ base_init (file);
+
+ if (file >= TEST_FILE_COUNT)
+ return;
+
+ xclose (create_temp_file ("tst-fflush", &files[file].name));
+
+ fd = xopen (files[file].name, O_RDONLY, 0);
+ files[file].mfile = (char *) xmmap (NULL, CONTENT_SZ_MAX, PROT_READ,
+ MAP_SHARED, fd);
+ xclose (fd);
+
+ files[file].file = xfopen (files[file].name, "w");
+ TEST_VERIFY_EXIT (files[file].file != NULL);
+ files[file].fd = fileno (files[file].file);
+}
+
+static void
+file_cleanup (int file)
+{
+ xfclose (files[file].file);
+ free (files[file].name);
+ xmunmap (files[file].mfile, CONTENT_SZ_MAX);
+
+ base_init (file);
+}
+
+static int
+file_changed (int to_check, int global_flush)
+{
+ struct stat stats = { };
+ bool content_matches = 0;
+ char expected[CONTENT_SZ_MAX] = { };
+
+ verbose_printf ("Check that %s (%d) exactly contains the data we put in\n",
+ files[to_check].name, to_check);
+
+ /* File should contain "N:M" where both N and M are one digit exactly. */
+ snprintf (expected, sizeof (expected), "%d:%d", global_flush, to_check);
+ content_matches
+ = (strncmp (files[to_check].mfile, expected, sizeof (expected)) == 0);
+
+ TEST_VERIFY_EXIT (content_matches != 0);
+
+ TEST_VERIFY_EXIT (fstat (files[to_check].fd, &stats) >= 0);
+ TEST_VERIFY_EXIT (stats.st_size == 3);
+
+ /* Not reached if the data doesn't match. */
+ return FILE_CHANGED;
+}
+
+static void
+file_flush (int global_flush)
+{
+ for (int i = 0; i < TEST_FILE_COUNT; i++)
+ file_init (i);
+
+ /* Print a unique identifier in each file, that is not too long nor contain
+ new line to not trigger _IO_OVERFLOW/_IO_SYNC. */
+ for (int i = 0; i < TEST_FILE_COUNT; i++)
+ fprintf (files[i].file, "%d:%d", global_flush, i);
+
+ if (global_flush)
+ {
+ fflush (NULL);
+ }
+ else
+ {
+ for (int i = 0; i < TEST_FILE_COUNT; i++)
+ fflush (files[i].file);
+ }
+
+ for (int i = 0; i < TEST_FILE_COUNT; i++)
+ {
+ int changed = file_changed (i, global_flush);
+
+ verbose_printf ("Check that file %s has been modified after fflush\n",
+ files[i].name);
+ verbose_printf ("File %s; changed %d; global fflush %d\n",
+ files[i].name, changed, !!global_flush);
+ TEST_VERIFY_EXIT (changed != FILE_UNCHANGED);
+ }
+
+ for (int i = 0; i < TEST_FILE_COUNT; i++)
+ file_cleanup (i);
+}
+
+static int
+do_test (void)
+{
+ verbose_printf ("Checking fflush(FILE)\n");
+ file_flush (FILE_FLUSH);
+
+ verbose_printf ("checking fflush(NULL)\n");
+ file_flush (GLOBAL_FLUSH);
+
+ return 0;
+}
+
+#include <support/test-driver.c>