util/cexec: Implement exception helpers

See src/stdplus/util/cexec.hpp callCheckErrno for the motivation behind
this change.

Change-Id: I0225b87398b632624f2ef8ccd6c00b5dd6b7e056
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/test/util/cexec.cpp b/test/util/cexec.cpp
new file mode 100644
index 0000000..51fdb8b
--- /dev/null
+++ b/test/util/cexec.cpp
@@ -0,0 +1,230 @@
+#include <gtest/gtest.h>
+#include <stdplus/util/cexec.hpp>
+#include <string_view>
+#include <system_error>
+
+namespace stdplus
+{
+namespace util
+{
+namespace
+{
+
+int sample1()
+{
+    return 1;
+}
+
+int sample2(int val)
+{
+    return val;
+}
+
+ssize_t sample3(int val, ssize_t* val2)
+{
+    return *val2 + val;
+}
+
+const char* ptr(const char* p)
+{
+    return p;
+}
+
+struct sample
+{
+    int count = 3;
+
+    int one()
+    {
+        return count++;
+    }
+
+    int two(int val) const
+    {
+        return val;
+    }
+
+    int* ptr()
+    {
+        return &count;
+    }
+
+    int* ptr2()
+    {
+        return nullptr;
+    }
+
+    static int s(int val)
+    {
+        return val;
+    }
+};
+
+int makeTrivialError(int error, const char* msg)
+{
+    (void)msg;
+    return error;
+}
+
+TEST(Cexec, CallCheckErrnoInt)
+{
+    EXPECT_EQ(1, callCheckErrno("sample1", sample1));
+    EXPECT_EQ(2, callCheckErrno("sample2", &sample2, 2));
+    EXPECT_EQ(4, callCheckErrno("sample::s", sample::s, 4));
+    ssize_t v = 10;
+    EXPECT_EQ(12, callCheckErrno("sample3", sample3, 2, &v));
+
+    constexpr auto error = "sample2 error";
+    try
+    {
+        errno = EBADF;
+        callCheckErrno(error, sample2, -1);
+        EXPECT_TRUE(false);
+    }
+    catch (const std::system_error& e)
+    {
+        EXPECT_EQ(std::string_view(error),
+                  std::string_view(e.what(), strlen(error)));
+        EXPECT_EQ(EBADF, e.code().value());
+    }
+}
+
+TEST(Cexec, CallCheckErrnoIntMem)
+{
+    sample s;
+    const sample* sp = &s;
+    EXPECT_EQ(3, callCheckErrno("sample::one", &sample::one, s));
+    EXPECT_EQ(4, callCheckErrno("sample::one", &sample::one, &s));
+    EXPECT_EQ(5, callCheckErrno("sample::two", &sample::two, sp, 5));
+
+    constexpr auto error = "sample error";
+    try
+    {
+        errno = EBADF;
+        callCheckErrno(error, &sample::two, sp, -1);
+        EXPECT_TRUE(false);
+    }
+    catch (const std::system_error& e)
+    {
+        EXPECT_EQ(std::string_view(error),
+                  std::string_view(e.what(), strlen(error)));
+        EXPECT_EQ(EBADF, e.code().value());
+    }
+}
+
+TEST(Cexec, CallCheckErrnoPtr)
+{
+    constexpr auto sample = "sample";
+    EXPECT_EQ(sample, callCheckErrno("sample1", ptr, sample));
+
+    constexpr auto error = "sample error";
+    try
+    {
+        errno = EBADF;
+        callCheckErrno(error, &ptr, nullptr);
+        EXPECT_TRUE(false);
+    }
+    catch (const std::system_error& e)
+    {
+        EXPECT_EQ(std::string_view(error),
+                  std::string_view(e.what(), strlen(error)));
+        EXPECT_EQ(EBADF, e.code().value());
+    }
+}
+
+TEST(Cexec, CallCheckErrnoPtrMem)
+{
+    sample s;
+    EXPECT_EQ(&s.count, callCheckErrno("sample1", &sample::ptr, &s));
+
+    constexpr auto error = "sample error";
+    try
+    {
+        errno = EBADF;
+        callCheckErrno(error, &sample::ptr2, s);
+        EXPECT_TRUE(false);
+    }
+    catch (const std::system_error& e)
+    {
+        EXPECT_EQ(std::string_view(error),
+                  std::string_view(e.what(), strlen(error)));
+        EXPECT_EQ(EBADF, e.code().value());
+    }
+}
+
+TEST(Cexec, CallCheckErrnoErrorFunc)
+{
+    errno = EBADF;
+    try
+    {
+        callCheckErrno<makeTrivialError>("sample2", sample2, -1);
+        EXPECT_TRUE(false);
+    }
+    catch (int error)
+    {
+        EXPECT_EQ(errno, error);
+    }
+}
+
+TEST(Cexec, CallCheckRetInt)
+{
+    EXPECT_EQ(1, callCheckRet("sample1", sample1));
+    EXPECT_EQ(2, callCheckRet("sample2", &sample2, 2));
+    EXPECT_EQ(4, callCheckRet("sample::s", sample::s, 4));
+    ssize_t v = 10;
+    EXPECT_EQ(12, callCheckRet("sample3", sample3, 2, &v));
+
+    constexpr auto error = "sample2 error";
+    try
+    {
+        errno = EBADF;
+        callCheckRet(error, sample2, -EINTR);
+        EXPECT_TRUE(false);
+    }
+    catch (const std::system_error& e)
+    {
+        EXPECT_EQ(std::string_view(error),
+                  std::string_view(e.what(), strlen(error)));
+        EXPECT_EQ(EINTR, e.code().value());
+    }
+}
+
+TEST(Cexec, CallCheckRetIntMem)
+{
+    sample s;
+    const sample* sp = &s;
+    EXPECT_EQ(3, callCheckRet("sample::one", &sample::one, s));
+    EXPECT_EQ(4, callCheckRet("sample::one", &sample::one, &s));
+    EXPECT_EQ(5, callCheckRet("sample::two", &sample::two, sp, 5));
+
+    constexpr auto error = "sample error";
+    try
+    {
+        errno = EBADF;
+        callCheckRet(error, &sample::two, s, -EINTR);
+        EXPECT_TRUE(false);
+    }
+    catch (const std::system_error& e)
+    {
+        EXPECT_EQ(std::string_view(error),
+                  std::string_view(e.what(), strlen(error)));
+        EXPECT_EQ(EINTR, e.code().value());
+    }
+}
+
+TEST(Cexec, CallCheckRetErrorFunc)
+{
+    try
+    {
+        callCheckRet<makeTrivialError>("sample2", sample2, -EBADF);
+        EXPECT_TRUE(false);
+    }
+    catch (int error)
+    {
+        EXPECT_EQ(EBADF, error);
+    }
+}
+
+} // namespace
+} // namespace util
+} // namespace stdplus