]> Kevux Git Server - fll/commitdiff
Feature: Add f_file_select() for select().
authorKevin Day <kevin@kevux.org>
Sat, 24 Jun 2023 20:58:06 +0000 (15:58 -0500)
committerKevin Day <kevin@kevux.org>
Sat, 24 Jun 2023 20:58:06 +0000 (15:58 -0500)
This is more of a wrapper around select() (unlike f_file_poll() which is a bit more than a wrapper around poll()).
The arguments would be more difficult than worth it to simplify and normalize in the FLL typedef style.

level_0/f_file/c/file.c
level_0/f_file/c/file.h
level_0/f_file/data/build/settings-mocks
level_0/f_file/data/build/settings-tests
level_0/f_file/tests/unit/c/mock-file.c
level_0/f_file/tests/unit/c/mock-file.h
level_0/f_file/tests/unit/c/test-file-select.c [new file with mode: 0644]
level_0/f_file/tests/unit/c/test-file-select.h [new file with mode: 0644]
level_0/f_file/tests/unit/c/test-file.c
level_0/f_file/tests/unit/c/test-file.h

index dd5d4d26aaaa8ec58d5dbd1ea3b99b45453e70df..c6cbd8ca9998cb3feddb74b02cabfc11e7a7dddd 100644 (file)
@@ -1896,6 +1896,25 @@ extern "C" {
   }
 #endif // _di_f_file_seek_
 
+#ifndef _di_f_file_select_
+  f_status_t f_file_select(const int highest_plus_one, fd_set * const read, fd_set * const write, fd_set * const except, struct timeval * const timeout) {
+
+    if (!highest_plus_one) return F_data_not;
+    if (!read && !write && !except) return F_data_not;
+
+    if (select(highest_plus_one, read, write, except, timeout) == -1) {
+      if (errno == EBADF) return F_status_set_error(F_file_descriptor);
+      if (errno == EINTR) return F_status_set_error(F_interrupt);
+      if (errno == EINVAL) return F_status_set_error(F_parameter);
+      if (errno == ENOMEM) return F_status_set_error(F_memory_not);
+
+      return F_status_set_error(F_failure);
+    }
+
+    return F_none;
+  }
+#endif // _di_f_file_select_
+
 #ifndef _di_f_file_size_
   f_status_t f_file_size(const f_string_static_t path, const bool dereference, off_t * const size) {
     #ifndef _di_level_0_parameter_checking_
index 261fc5ec891436e02e3ec39a2a2fce2082381ffa..d913aeff6aac6fd090d1202aa5d5fb01a47d05be 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/select.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -1950,13 +1951,55 @@ extern "C" {
  *   F_parameter (with error bit) if a parameter is invalid.
  *   F_failure (with error bit) on any other error.
  *
- * @see lseek
+ * @see lseek()
  */
 #ifndef _di_f_file_seek_
   extern f_status_t f_file_seek(const f_file_t file, const int whence, const off_t offset, off_t * const seeked);
 #endif // _di_f_file_seek_
 
 /**
+ * Monitor one or more file descriptors.
+ *
+ * @todo Probably should implement a pselect().
+ *
+ * Warning: Some libc implementations, such as GLIBC, use an upper limit of 1023 file descriptors.
+ *          The linux kernel general does not have such a limit.
+ *          To more safely handle more than 1023 file desciptors, instead consider f_file_poll();
+ *
+ * @param highest
+ *   The value of the highest file descriptor between all provided sets (read, write, and except) plus one.
+ *   The caller must remember that one must be added to the highest file descriptor as per requirements by select().
+ *   This cannot be 0.
+ * @param read
+ *   (optional) The set of file descriptors for descriptors that become available for reading.
+ *   Set to NULL to not use.
+ * @param write
+ *   (optional) The set of file descriptors for descriptors that become available for writing.
+ *   Set to NULL to not use.
+ * @param except
+ *   (optional) The set of file descriptors for descriptors that become available for any error conditions.
+ *   Set to NULL to not use.
+ * @param timeout
+ *   (optional)
+ *   Set to NULL to not use.
+ *
+ * @return
+ *   F_none on success.
+ *   F_data_not if all three read, write, and except are NULL (having at least one is required) or when highest_plus_one is 0.
+ *
+ *   F_file_descriptor (with error bit) if the file descriptor is invalid.
+ *   F_interrupt (with error bit) when program received an interrupt signal, halting operation.
+ *   F_memory_not (with error bit) if out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_failure (with error bit) on any other error.
+ *
+ * @see select()
+ */
+#ifndef _di_f_file_select_
+  extern f_status_t f_file_select(const int highest_plus_one, fd_set * const read, fd_set * const write, fd_set * const except, struct timeval * const timeout);
+#endif // _di_f_file_select_
+
+/**
  * Read the size of file.
  *
  * @param path
index aeae42cb6089ab88e7bd06bf7269f21b339724a6..e42f3a46ea59e0a1e34e617a61cbcd97812ba0ce 100644 (file)
@@ -106,6 +106,7 @@ flags -Wl,--wrap=readlinkat
 flags -Wl,--wrap=rename
 flags -Wl,--wrap=renameat
 flags -Wl,--wrap=renameat2
+flags -Wl,--wrap=select
 flags -Wl,--wrap=stat
 flags -Wl,--wrap=symlink
 flags -Wl,--wrap=symlinkat
index b604c83bad4f54a35a08f431e41a463c4090b57d..58503af0cde01745975b3917ca7d8f6823cd1183 100644 (file)
@@ -25,7 +25,7 @@ build_language c
 build_libraries -lc -lcmocka
 build_libraries-individual -lf_memory -lf_string -lf_file
 
-build_sources_program test-file-access.c test-file-access_at.c test-file-clone.c test-file-close.c test-file-copy.c test-file-create.c test-file-create_at.c test-file-create_device.c test-file-create_device_at.c test-file-create_fifo.c test-file-create_fifo_at.c test-file-create_node.c test-file-create_node_at.c test-file-descriptor.c test-file-exists.c test-file-exists_at.c test-file-flush.c test-file-group_read.c test-file-is.c test-file-is_at.c test-file-is_stat.c test-file-link.c test-file-link_at.c test-file-link_hard.c test-file-link_hard_at.c test-file-link_read.c test-file-link_read_at.c test-file-manipulate.c test-file-mode_determine.c test-file-mode_from_string.c test-file-mode_read.c test-file-mode_read_at.c test-file-mode_set.c test-file-mode_set_at.c test-file-mode_to_mode.c test-file-name_base.c test-file-name_directory.c test-file-open.c test-file-open_at.c test-file-owner_read.c test-file-read.c test-file-read_block.c test-file-read_until.c test-file-remove.c test-file-remove_at.c test-file-rename.c test-file-rename_at.c test-file-role_change.c test-file-role_change_at.c test-file-poll.c test-file-seek.c test-file-size.c test-file-size_at.c test-file-size_by_id.c test-file-stat.c test-file-stat_at.c test-file-stat_by_id.c test-file-stream_close.c test-file-stream_open_descriptor.c test-file-stream_open.c test-file-stream_read.c test-file-stream_read_block.c test-file-stream_read_until.c test-file-stream_reopen.c test-file-stream_write.c test-file-stream_write_block.c test-file-stream_write_until.c test-file-stream_write_range.c test-file-touch.c test-file-touch_at.c test-file-type.c test-file-type_at.c test-file-umask_get.c test-file-umask_set.c test-file-write.c test-file-write_block.c test-file-write_until.c test-file-write_range.c
+build_sources_program test-file-access.c test-file-access_at.c test-file-clone.c test-file-close.c test-file-copy.c test-file-create.c test-file-create_at.c test-file-create_device.c test-file-create_device_at.c test-file-create_fifo.c test-file-create_fifo_at.c test-file-create_node.c test-file-create_node_at.c test-file-descriptor.c test-file-exists.c test-file-exists_at.c test-file-flush.c test-file-group_read.c test-file-is.c test-file-is_at.c test-file-is_stat.c test-file-link.c test-file-link_at.c test-file-link_hard.c test-file-link_hard_at.c test-file-link_read.c test-file-link_read_at.c test-file-manipulate.c test-file-mode_determine.c test-file-mode_from_string.c test-file-mode_read.c test-file-mode_read_at.c test-file-mode_set.c test-file-mode_set_at.c test-file-mode_to_mode.c test-file-name_base.c test-file-name_directory.c test-file-open.c test-file-open_at.c test-file-owner_read.c test-file-read.c test-file-read_block.c test-file-read_until.c test-file-remove.c test-file-remove_at.c test-file-rename.c test-file-rename_at.c test-file-role_change.c test-file-role_change_at.c test-file-poll.c test-file-seek.c test-file-select.c test-file-size.c test-file-size_at.c test-file-size_by_id.c test-file-stat.c test-file-stat_at.c test-file-stat_by_id.c test-file-stream_close.c test-file-stream_open_descriptor.c test-file-stream_open.c test-file-stream_read.c test-file-stream_read_block.c test-file-stream_read_until.c test-file-stream_reopen.c test-file-stream_write.c test-file-stream_write_block.c test-file-stream_write_until.c test-file-stream_write_range.c test-file-touch.c test-file-touch_at.c test-file-type.c test-file-type_at.c test-file-umask_get.c test-file-umask_set.c test-file-write.c test-file-write_block.c test-file-write_until.c test-file-write_range.c
 build_sources_program test-file.c
 
 build_script no
index 8069930a6480f528090265394b3fd05ad2c0184c..68d18ac92366564d2998970e64be3dd6b989333f 100644 (file)
@@ -602,6 +602,19 @@ int __wrap_renameat2(int olddirfd, const char *oldpath, int newdirfd, const char
   return mock_type(int);
 }
 
+int __wrap_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) {
+
+  const bool failure = mock_type(bool);
+
+  if (failure) {
+    errno = mock_type(int);
+
+    return -1;
+  }
+
+  return mock_type(int);
+}
+
 int __wrap_stat(const char *pathname, struct stat *statbuf) {
 
   const bool failure = mock_type(bool);
index 76c8fb971f8b19256d2c7a3b4be78e8b1c9a065e..dd584f492f40422d6b178f0e3fba5c0756473c38 100644 (file)
@@ -73,6 +73,7 @@ extern ssize_t __wrap_readlinkat(int dirfd, const char *pathname, char *buf, siz
 extern int __wrap_rename(const char *oldpath, const char *newpath);
 extern int __wrap_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
 extern int __wrap_renameat2(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, unsigned int flags);
+extern int __wrap_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
 extern int __wrap_stat(const char *pathname, struct stat *statbuf);
 extern int __wrap_symlink(const char *target, const char *linkpath);
 extern int __wrap_symlinkat(const char *target, int newdirfd, const char *linkpath);
diff --git a/level_0/f_file/tests/unit/c/test-file-select.c b/level_0/f_file/tests/unit/c/test-file-select.c
new file mode 100644 (file)
index 0000000..14b37ca
--- /dev/null
@@ -0,0 +1,293 @@
+#include "test-file.h"
+#include "test-file-select.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_file_select__fails(void **state) {
+
+  {
+    fd_set read;
+
+    memset(&read, 0, sizeof(fd_set));
+
+    int errnos[] = {
+      EBADF,
+      EINTR,
+      EINVAL,
+      ENOMEM,
+      mock_errno_generic,
+    };
+
+    f_status_t statuss[] = {
+      F_file_descriptor,
+      F_interrupt,
+      F_parameter,
+      F_memory_not,
+      F_failure,
+    };
+
+    for (int i = 0; i < 5; ++i) {
+
+      will_return(__wrap_select, true);
+      will_return(__wrap_select, errnos[i]);
+
+      const f_status_t status = f_file_select(1, &read, 0, 0, 0);
+
+      assert_int_equal(status, F_status_set_error(statuss[i]));
+    } // for
+  }
+}
+
+void test__f_file_select__returns_data_not(void **state) {
+
+  fd_set read;
+  fd_set write;
+  fd_set except;
+  struct timeval timeout;
+
+  memset(&read, 0, sizeof(fd_set));
+  memset(&write, 0, sizeof(fd_set));
+  memset(&except, 0, sizeof(fd_set));
+  memset(&timeout, 0, sizeof(struct timeval));
+
+  {
+    const f_status_t status = f_file_select(0, &read, &write, &except, &timeout);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, &read, &write, 0, &timeout);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, &read, 0, &except, &timeout);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, 0, &write, &except, &timeout);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, 0, 0, &except, &timeout);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, 0, &write, 0, &timeout);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, 0, 0, &except, &timeout);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, &read, &write, &except, 0);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, &read, &write, 0, 0);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, &read, 0, &except, 0);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, 0, &write, &except, 0);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, 0, 0, &except, 0);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, 0, &write, 0, 0);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(0, 0, 0, &except, 0);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(1, 0, 0, 0, &timeout);
+
+    assert_int_equal(status, F_data_not);
+  }
+
+  {
+    const f_status_t status = f_file_select(1, 0, 0, 0, 0);
+
+    assert_int_equal(status, F_data_not);
+  }
+}
+
+void test__f_file_select__works(void **state) {
+
+  fd_set read;
+  fd_set write;
+  fd_set except;
+  struct timeval timeout;
+
+  memset(&read, 0, sizeof(fd_set));
+  memset(&write, 0, sizeof(fd_set));
+  memset(&except, 0, sizeof(fd_set));
+  memset(&timeout, 0, sizeof(struct timeval));
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, &timeout);
+
+    const f_status_t status = f_file_select(1, &read, &write, &except, &timeout);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, &timeout);
+
+    const f_status_t status = f_file_select(1, &read, &write, 0, &timeout);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, &timeout);
+
+    const f_status_t status = f_file_select(1, &read, 0, &except, &timeout);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, &timeout);
+
+    const f_status_t status = f_file_select(1, 0, &write, &except, &timeout);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, &timeout);
+
+    const f_status_t status = f_file_select(1, &read, 0, 0, &timeout);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, &timeout);
+
+    const f_status_t status = f_file_select(1, 0, &write, 0, &timeout);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, &timeout);
+
+    const f_status_t status = f_file_select(1, 0, 0, &except, &timeout);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, 0);
+
+    const f_status_t status = f_file_select(1, &read, &write, &except, 0);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, 0);
+
+    const f_status_t status = f_file_select(1, &read, &write, 0, 0);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, 0);
+
+    const f_status_t status = f_file_select(1, &read, 0, &except, 0);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, 0);
+
+    const f_status_t status = f_file_select(1, 0, &write, &except, 0);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, 0);
+
+    const f_status_t status = f_file_select(1, &read, 0, 0, 0);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, 0);
+
+    const f_status_t status = f_file_select(1, 0, &write, 0, 0);
+
+    assert_int_equal(status, F_none);
+  }
+
+  {
+    will_return(__wrap_select, false);
+    will_return(__wrap_select, 0);
+
+    const f_status_t status = f_file_select(1, 0, 0, &except, 0);
+
+    assert_int_equal(status, F_none);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_file/tests/unit/c/test-file-select.h b/level_0/f_file/tests/unit/c/test-file-select.h
new file mode 100644 (file)
index 0000000..f84ea92
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: File
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the file project.
+ */
+#ifndef _TEST__F_file_select_h
+#define _TEST__F_file_select_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_file_select()
+ */
+extern void test__f_file_select__fails(void **state);
+
+/**
+ * Test that function works but the path is empty.
+ *
+ * @see f_file_select()
+ */
+extern void test__f_file_select__returns_data_not(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_file_select()
+ */
+extern void test__f_file_select__works(void **state);
+
+#endif // _TEST__F_file_select_h
index 55058f2d6c9ed4f081b60f70314fabac3d72cff4..947ab321d7840e3f046c0abc7fab1a997e0060df 100644 (file)
@@ -261,6 +261,10 @@ int main(void) {
     cmocka_unit_test(test__f_file_seek__returns_file_descriptor_not),
     cmocka_unit_test(test__f_file_seek__works),
 
+    cmocka_unit_test(test__f_file_select__fails),
+    cmocka_unit_test(test__f_file_select__returns_data_not),
+    cmocka_unit_test(test__f_file_select__works),
+
     cmocka_unit_test(test__f_file_size__fails),
     cmocka_unit_test(test__f_file_size__returns_data_not),
     cmocka_unit_test(test__f_file_size__works),
@@ -429,6 +433,7 @@ int main(void) {
       // f_file_role_change() doesn't use parameter checking.
       // f_file_role_change_at() doesn't use parameter checking.
       cmocka_unit_test(test__f_file_seek__parameter_checking),
+      // f_file_select() doesn't use parameter checking.
       cmocka_unit_test(test__f_file_size__parameter_checking),
       cmocka_unit_test(test__f_file_size_at__parameter_checking),
       cmocka_unit_test(test__f_file_size_by_id__parameter_checking),
index 0636fc513f2cceba33ae98b1eeefe6a4779f53a8..df14c5ac4ae37b39738e13e5b2d15b73fee5d8dd 100644 (file)
@@ -77,6 +77,7 @@
 #include "test-file-role_change.h"
 #include "test-file-role_change_at.h"
 #include "test-file-seek.h"
+#include "test-file-select.h"
 #include "test-file-size.h"
 #include "test-file-size_at.h"
 #include "test-file-size_by_id.h"