Various coverage improvements.

Mostly from extra test cases, but also:

* Move the fgets size < 0 assertion into fgets.

* Use ELF aliases for strtoq/strtouq rather than duplicating code.

* Don't check uname() succeeded, since it can't fail.

Test: treehugger
Change-Id: I2e6b3b88b0a3eb16bd68be68b9bc9f40d8043291
diff --git a/tests/Android.bp b/tests/Android.bp
index 7e6e742..586ef34 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -422,6 +422,7 @@
         "sys_uio_test.cpp",
         "sys_un_test.cpp",
         "sys_vfs_test.cpp",
+        "sys_wait_test.cpp",
         "sys_xattr_test.cpp",
         "system_properties_test.cpp",
         "system_properties_test2.cpp",
diff --git a/tests/dirent_test.cpp b/tests/dirent_test.cpp
index 378aea4..56929d1 100644
--- a/tests/dirent_test.cpp
+++ b/tests/dirent_test.cpp
@@ -113,6 +113,18 @@
   ASSERT_EQ(unsorted_name_list, unsorted_name_list_at64);
 }
 
+static int is_version_filter(const dirent* de) {
+  return !strcmp(de->d_name, "version");
+}
+
+TEST(dirent, scandir_filter) {
+  dirent** entries;
+  errno = 0;
+  ASSERT_EQ(1, scandir("/proc", &entries, is_version_filter, nullptr));
+  ASSERT_STREQ("version", entries[0]->d_name);
+  free(entries);
+}
+
 TEST(dirent, scandir_ENOENT) {
   dirent** entries;
   errno = 0;
diff --git a/tests/grp_pwd_test.cpp b/tests/grp_pwd_test.cpp
index 99117e4..bf65720 100644
--- a/tests/grp_pwd_test.cpp
+++ b/tests/grp_pwd_test.cpp
@@ -820,6 +820,24 @@
 #endif
 }
 
+TEST(grp, getgrouplist) {
+#if defined(__BIONIC__)
+  // Query the number of groups.
+  int ngroups = 0;
+  ASSERT_EQ(-1, getgrouplist("root", 123, nullptr, &ngroups));
+  ASSERT_EQ(1, ngroups);
+
+  // Query the specific groups (just the one you pass in on Android).
+  ngroups = 8;
+  gid_t groups[ngroups];
+  ASSERT_EQ(1, getgrouplist("root", 123, groups, &ngroups));
+  ASSERT_EQ(1, ngroups);
+  ASSERT_EQ(123u, groups[0]);
+#else
+  GTEST_SKIP() << "bionic-only test (groups too unpredictable)";
+#endif
+}
+
 #if defined(__BIONIC__)
 static void TestAidNamePrefix(const std::string& file_path) {
   std::string file_contents;
diff --git a/tests/netinet_ether_test.cpp b/tests/netinet_ether_test.cpp
index faa3db4..af020ec 100644
--- a/tests/netinet_ether_test.cpp
+++ b/tests/netinet_ether_test.cpp
@@ -34,7 +34,7 @@
 TEST(netinet_ether, ether_aton_r__ether_ntoa_r) {
   ether_addr addr;
   memset(&addr, 0, sizeof(addr));
-  ether_addr* a = ether_aton_r("12:34:56:78:9a:bc", &addr);
+  ether_addr* a = ether_aton_r("12:34:56:78:9a:Bc", &addr);
   ASSERT_EQ(&addr, a);
   ASSERT_EQ(0x12, addr.ether_addr_octet[0]);
   ASSERT_EQ(0x34, addr.ether_addr_octet[1]);
@@ -49,3 +49,11 @@
   ASSERT_EQ(buf, p);
   ASSERT_STREQ("12:34:56:78:9a:bc", buf);
 }
+
+TEST(netinet_ether, ether_aton_r_failures) {
+  ether_addr addr;
+  ASSERT_TRUE(ether_aton_r("12:34:56:78:9a;bc", &addr) == nullptr);
+  ASSERT_TRUE(ether_aton_r("12:34:56:78:9a:bc ", &addr) == nullptr);
+  ASSERT_TRUE(ether_aton_r("g2:34:56:78:9a:bc ", &addr) == nullptr);
+  ASSERT_TRUE(ether_aton_r("1G:34:56:78:9a:bc ", &addr) == nullptr);
+}
diff --git a/tests/netinet_in_test.cpp b/tests/netinet_in_test.cpp
index 2606082..437e180 100644
--- a/tests/netinet_in_test.cpp
+++ b/tests/netinet_in_test.cpp
@@ -31,8 +31,15 @@
 static constexpr uint64_t be64 = 0xf0debc9a78563412;
 
 TEST(netinet_in, bindresvport) {
-  // This isn't something we can usually test, so just check the symbol's there.
+  // This isn't something we can usually test (because you need to be root),
+  // so just check the symbol's there.
   ASSERT_EQ(-1, bindresvport(-1, nullptr));
+
+  // Only AF_INET is supported.
+  sockaddr_in sin = {.sin_family = AF_INET6};
+  errno = 0;
+  ASSERT_EQ(-1, bindresvport(-1, &sin));
+  ASSERT_EQ(EPFNOSUPPORT, errno);
 }
 
 TEST(netinet_in, in6addr_any) {
diff --git a/tests/sched_test.cpp b/tests/sched_test.cpp
index 9309a7f..03e8062 100644
--- a/tests/sched_test.cpp
+++ b/tests/sched_test.cpp
@@ -301,3 +301,7 @@
   // don't behave as POSIX specifies. http://b/26203902.
   ASSERT_EQ(0, sched_setscheduler(getpid(), original_policy, &p));
 }
+
+TEST(sched, sched_getaffinity_failure) {
+  ASSERT_EQ(-1, sched_getaffinity(getpid(), 0, nullptr));
+}
diff --git a/tests/search_test.cpp b/tests/search_test.cpp
index 1509199..8b8359d 100644
--- a/tests/search_test.cpp
+++ b/tests/search_test.cpp
@@ -114,6 +114,11 @@
   ASSERT_EQ(3U, g_free_calls);
 }
 
+TEST(search, tdestroy_null) {
+  // It's okay to pass a null node, and your callback will not be called.
+  tdestroy(nullptr, nullptr);
+}
+
 struct pod_node {
   explicit pod_node(int i) : i(i) {}
   int i;
@@ -285,3 +290,26 @@
   AssertEntry(e, "a", "B");
   hdestroy_r(&h2);
 }
+
+TEST(search, hsearch_resizing) {
+  ASSERT_NE(0, hcreate(1));
+
+  std::vector<char*> entries;
+  // Add enough entries to ensure that we've had to resize.
+  for (char ch = ' '; ch <= '~'; ++ch) {
+    char* p;
+    asprintf(&p, "%c", ch);
+    ENTRY e;
+    e.data = e.key = p;
+    ASSERT_TRUE(hsearch(e, ENTER) != nullptr);
+    entries.push_back(p);
+  }
+
+  // Check they're all there.
+  for (auto& p : entries) {
+    ENTRY* e = hsearch(ENTRY{.key = p, .data = nullptr}, FIND);
+    AssertEntry(e, p, p);
+  }
+
+  for (auto& p : entries) free(p);
+}
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index f6eca05..c21c3b8 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -371,6 +371,11 @@
 #endif
 }
 
+TEST(STDIO_TEST, snprintf_measure) {
+  char buf[16];
+  ASSERT_EQ(11, snprintf(buf, 0, "Hello %s", "world"));
+}
+
 TEST(STDIO_TEST, snprintf_smoke) {
   char buf[BUFSIZ];
 
@@ -1155,7 +1160,6 @@
   free(p1);
 }
 
-
 TEST(STDIO_TEST, sscanf_mlc) {
   // This is so useless that clang doesn't even believe it exists...
 #pragma clang diagnostic push
@@ -1189,7 +1193,6 @@
 #pragma clang diagnostic pop
 }
 
-
 TEST(STDIO_TEST, sscanf_ms) {
   CheckScanfM(sscanf, "hello", "%ms", 1, "hello");
   CheckScanfM(sscanf, "hello", "%4ms", 1, "hell");
@@ -2533,6 +2536,16 @@
   eth.Run([&]() { exit(puts("a b c")); }, 0, "a b c\n");
 }
 
+TEST(STDIO_TEST, putchar) {
+  ExecTestHelper eth;
+  eth.Run([&]() { exit(putchar('A')); }, 65, "A");
+}
+
+TEST(STDIO_TEST, putchar_unlocked) {
+  ExecTestHelper eth;
+  eth.Run([&]() { exit(putchar('B')); }, 66, "B");
+}
+
 TEST(STDIO_TEST, unlocked) {
   TemporaryFile tf;
 
@@ -2733,3 +2746,73 @@
  ASSERT_NE(0, RENAME_WHITEOUT);
 #endif
 }
+
+TEST(STDIO_TEST, fdopen_failures) {
+  FILE* fp;
+  int fd = open("/proc/version", O_RDONLY);
+  ASSERT_TRUE(fd != -1);
+
+  // Nonsense mode.
+  errno = 0;
+  fp = fdopen(fd, "nonsense");
+  ASSERT_TRUE(fp == nullptr);
+  ASSERT_EQ(EINVAL, errno);
+
+  // Mode that isn't a subset of the fd's actual mode.
+  errno = 0;
+  fp = fdopen(fd, "w");
+  ASSERT_TRUE(fp == nullptr);
+  ASSERT_EQ(EINVAL, errno);
+
+  // Can't set append on the underlying fd.
+  errno = 0;
+  fp = fdopen(fd, "a");
+  ASSERT_TRUE(fp == nullptr);
+  ASSERT_EQ(EINVAL, errno);
+
+  // Bad fd.
+  errno = 0;
+  fp = fdopen(-1, "re");
+  ASSERT_TRUE(fp == nullptr);
+  ASSERT_EQ(EBADF, errno);
+
+  close(fd);
+}
+
+TEST(STDIO_TEST, fmemopen_invalid_mode) {
+  errno = 0;
+  FILE* fp = fmemopen(nullptr, 16, "nonsense");
+  ASSERT_TRUE(fp == nullptr);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(STDIO_TEST, fopen_invalid_mode) {
+  errno = 0;
+  FILE* fp = fopen("/proc/version", "nonsense");
+  ASSERT_TRUE(fp == nullptr);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(STDIO_TEST, freopen_invalid_mode) {
+  FILE* fp = fopen("/proc/version", "re");
+  ASSERT_TRUE(fp != nullptr);
+
+  errno = 0;
+  fp = freopen("/proc/version", "nonsense", fp);
+  ASSERT_TRUE(fp == nullptr);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(STDIO_TEST, asprintf_smoke) {
+  char* p = nullptr;
+  ASSERT_EQ(11, asprintf(&p, "hello %s", "world"));
+  ASSERT_STREQ("hello world", p);
+  free(p);
+}
+
+TEST(STDIO_TEST, fopen_ENOENT) {
+  errno = 0;
+  FILE* fp = fopen("/proc/does-not-exist", "re");
+  ASSERT_TRUE(fp == nullptr);
+  ASSERT_EQ(ENOENT, errno);
+}
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index 3f1ec86..c7b2ad8 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -800,10 +800,25 @@
   ASSERT_EQ(T(0), fn("123", &end_p, 37));
   ASSERT_EQ(EINVAL, errno);
 
+  // Both leading + or - are always allowed (even for the strtou* family).
+  ASSERT_EQ(T(-123), fn("-123", &end_p, 10));
+  ASSERT_EQ(T(123), fn("+123", &end_p, 10));
+
   // If we see "0x" *not* followed by a hex digit, we shouldn't swallow the 'x'.
   ASSERT_EQ(T(0), fn("0xy", &end_p, 16));
   ASSERT_EQ('x', *end_p);
 
+  // Hexadecimal (both the 0x and the digits) is case-insensitive.
+  ASSERT_EQ(T(0xab), fn("0xab", &end_p, 0));
+  ASSERT_EQ(T(0xab), fn("0Xab", &end_p, 0));
+  ASSERT_EQ(T(0xab), fn("0xAB", &end_p, 0));
+  ASSERT_EQ(T(0xab), fn("0XAB", &end_p, 0));
+  ASSERT_EQ(T(0xab), fn("0xAb", &end_p, 0));
+  ASSERT_EQ(T(0xab), fn("0XAb", &end_p, 0));
+
+  // Octal lives! (Sadly.)
+  ASSERT_EQ(T(0666), fn("0666", &end_p, 0));
+
   if (std::numeric_limits<T>::is_signed) {
     // Minimum (such as -128).
     std::string min{std::to_string(std::numeric_limits<T>::min())};
@@ -878,6 +893,18 @@
   CheckStrToInt(strtoumax);
 }
 
+TEST(stdlib, atoi) {
+  // Implemented using strtol in bionic, so extensive testing unnecessary.
+  ASSERT_EQ(123, atoi("123four"));
+  ASSERT_EQ(0, atoi("hello"));
+}
+
+TEST(stdlib, atol) {
+  // Implemented using strtol in bionic, so extensive testing unnecessary.
+  ASSERT_EQ(123L, atol("123four"));
+  ASSERT_EQ(0L, atol("hello"));
+}
+
 TEST(stdlib, abs) {
   ASSERT_EQ(INT_MAX, abs(-INT_MAX));
   ASSERT_EQ(INT_MAX, abs(INT_MAX));
diff --git a/tests/string_test.cpp b/tests/string_test.cpp
index fd7a551..22be852 100644
--- a/tests/string_test.cpp
+++ b/tests/string_test.cpp
@@ -64,6 +64,11 @@
   ASSERT_STREQ("Unknown error 134", strerror(EHWPOISON + 1));
 }
 
+TEST(STRING_TEST, strerror_l) {
+  // bionic just forwards to strerror(3).
+  ASSERT_STREQ("Success", strerror_l(0, LC_GLOBAL_LOCALE));
+}
+
 #if defined(__BIONIC__)
 static void* ConcurrentStrErrorFn(void*) {
   bool equal = (strcmp("Unknown error 2002", strerror(2002)) == 0);
@@ -1607,6 +1612,13 @@
   ASSERT_TRUE(strcoll("aac", "aab") > 0);
 }
 
+TEST(STRING_TEST, strcoll_l_smoke) {
+  // bionic just forwards to strcoll(3).
+  ASSERT_TRUE(strcoll_l("aab", "aac", LC_GLOBAL_LOCALE) < 0);
+  ASSERT_TRUE(strcoll_l("aab", "aab", LC_GLOBAL_LOCALE) == 0);
+  ASSERT_TRUE(strcoll_l("aac", "aab", LC_GLOBAL_LOCALE) > 0);
+}
+
 TEST(STRING_TEST, strxfrm_smoke) {
   const char* src1 = "aab";
   char dst1[16] = {};
@@ -1628,6 +1640,16 @@
   ASSERT_TRUE(strcmp(dst1, dst2) < 0);
 }
 
+TEST(STRING_TEST, strxfrm_l_smoke) {
+  // bionic just forwards to strxfrm(3), so this is a subset of the
+  // strxfrm test.
+  const char* src1 = "aab";
+  char dst1[16] = {};
+  ASSERT_EQ(strxfrm_l(dst1, src1, 0, LC_GLOBAL_LOCALE), 3U);
+  ASSERT_STREQ(dst1, "");
+  ASSERT_EQ(strxfrm_l(dst1, src1, sizeof(dst1), LC_GLOBAL_LOCALE), 3U);
+}
+
 TEST(STRING_TEST, memccpy_smoke) {
   char dst[32];
 
diff --git a/tests/sys_vfs_test.cpp b/tests/sys_vfs_test.cpp
index a521967..f82f505 100644
--- a/tests/sys_vfs_test.cpp
+++ b/tests/sys_vfs_test.cpp
@@ -44,12 +44,26 @@
   Check(sb);
 }
 
+TEST(sys_vfs, statfs_failure) {
+  struct statfs sb;
+  errno = 0;
+  ASSERT_EQ(-1, statfs("/does-not-exist", &sb));
+  ASSERT_EQ(ENOENT, errno);
+}
+
 TEST(sys_vfs, statfs64) {
   struct statfs64 sb;
   ASSERT_EQ(0, statfs64("/proc", &sb));
   Check(sb);
 }
 
+TEST(sys_vfs, statfs64_failure) {
+  struct statfs64 sb;
+  errno = 0;
+  ASSERT_EQ(-1, statfs64("/does-not-exist", &sb));
+  ASSERT_EQ(ENOENT, errno);
+}
+
 TEST(sys_vfs, fstatfs) {
   struct statfs sb;
   int fd = open("/proc", O_RDONLY);
@@ -58,6 +72,13 @@
   Check(sb);
 }
 
+TEST(sys_vfs, fstatfs_failure) {
+  struct statfs sb;
+  errno = 0;
+  ASSERT_EQ(-1, fstatfs(-1, &sb));
+  ASSERT_EQ(EBADF, errno);
+}
+
 TEST(sys_vfs, fstatfs64) {
   struct statfs64 sb;
   int fd = open("/proc", O_RDONLY);
@@ -65,3 +86,10 @@
   close(fd);
   Check(sb);
 }
+
+TEST(sys_vfs, fstatfs64_failure) {
+  struct statfs sb;
+  errno = 0;
+  ASSERT_EQ(-1, fstatfs(-1, &sb));
+  ASSERT_EQ(EBADF, errno);
+}
diff --git a/tests/sys_wait_test.cpp b/tests/sys_wait_test.cpp
new file mode 100644
index 0000000..c006972
--- /dev/null
+++ b/tests/sys_wait_test.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <gtest/gtest.h>
+
+#include <sys/wait.h>
+
+TEST(sys_wait, waitid) {
+  pid_t pid = fork();
+  ASSERT_NE(pid, -1);
+
+  if (pid == 0) _exit(66);
+
+  siginfo_t si = {};
+  ASSERT_EQ(0, waitid(P_PID, pid, &si, WEXITED));
+  ASSERT_EQ(pid, si.si_pid);
+  ASSERT_EQ(66, si.si_status);
+  ASSERT_EQ(CLD_EXITED, si.si_code);
+}
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 5a977c2..3d745ea 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -1008,4 +1008,5 @@
 
 TEST(time, difftime) {
   ASSERT_EQ(1.0, difftime(1, 0));
+  ASSERT_EQ(-1.0, difftime(0, 1));
 }