kunit: test: implement formatters for error codes

Formatters convert integer error codes to their menmonic
string representation. For example, for error code 1 we will
get string "EPERM".

Additionally this change also adds test macroses and modifies
existing macroses to use the newly introduced library where
it's appropriate, so that error messages for failed tests
contain mnemonic representations instead of just error codes.

One note about the implementation is that it basically hardcodes
mapping between error codes and their string representation. An
obvious downside of the approach is that if at some point error
codes are changed (or new error codes are introduced) the
library implementation need to be changed as well.

Assumption here that error codes do not change very often as
user space depends on values of some error codes returned by
kernel. This reasoning however doesn't apply to newly added
errors.

Google-Bug-Id: 118506277
Change-Id: If0628de9db9727e61d0bb8e8d6a9e53afb51baaf
Signed-off-by: Mike Krinkin <krinkin@google.com>
diff --git a/include/test/strerror.h b/include/test/strerror.h
new file mode 100644
index 0000000..da20f3d
--- /dev/null
+++ b/include/test/strerror.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * C++ stream style string formatter and printer used in KUnit for outputting
+ * KUnit messages.
+ *
+ * Copyright (C) 2019, Google LLC.
+ * Author: Mike Krinkin <krinkin@google.com>
+ */
+#include <linux/types.h>
+
+/**
+ * strerror() - returns a string representation for the given error code.
+ * @errno: an error code defined in include/uapi/asm-generic/errno-base.h or
+ * include/uapi/asm-generic/errno.h
+ *
+ * This function returns mnemonic representation of error code (for example,
+ * EPERM, ENOENT, ESRCH, etc). For unsupported errors this function returns
+ * NULL.
+ */
+const char *strerror(int errno);
+
+/**
+ * strerror_r() - returns a string representation of the given error code.
+ * Unlike strerror() it may use provided buffer to store the string, so in
+ * the case of unknown error it returns a message containing an error code
+ * instead of returning NULL.
+ * @errno: an error code defined in include/uapi/asm-generic/errno-base.h or
+ * include/uapi/asm-generic/errno.h
+ * @buf: pointer to a buffer that could be used to store the string.
+ * @buflen: contains the capacity of the buffer
+ *
+ * When function uses provided buffer and it's capacity is not enough to
+ * store the whole string the string is truncated and always contains '\0'.
+ * If buflen == 0, the function returns NULL pointer as there is not enough
+ * space to store even '\0'.
+ */
+const char *strerror_r(int errno, char *buf, size_t buflen);
diff --git a/include/test/test.h b/include/test/test.h
index b5f2d24..b0007c4 100644
--- a/include/test/test.h
+++ b/include/test/test.h
@@ -11,6 +11,7 @@
 
 #include <linux/types.h>
 #include <linux/slab.h>
+#include <test/strerror.h>
 #include <test/test-stream.h>
 
 /**
@@ -384,6 +385,81 @@
 		EXPECT(test, !(condition),				       \
 		       "Expected " #condition " is false, but is true.")
 
+/**
+ * EXPECT_NOT_NULL() - Causes a test failure when @expression is NULL.
+ * @test: The test context object.
+ * @expression: an arbitrary pointer expression. The test fails when this
+ * evaluates to NULL.
+ *
+ * Sets an expectation that @expression does not evaluate to NULL. Similar to
+ * EXPECT_TRUE() but supposed to be used with pointer expressions.
+ */
+#define EXPECT_NOT_NULL(test, expression)				       \
+		EXPECT(test, (expression),				       \
+		       "Expected " #expression " is not NULL, but is NULL.")
+
+/**
+ * EXPECT_NULL() - Causes a test failure when @expression is not NULL.
+ * @test: The test context object.
+ * @expression: an arbitrary pointer expression. The test fails when this does
+ * not evaluate to NULL.
+ *
+ * Sets an expectation that @expression evaluates to NULL. Similar to
+ * EXPECT_FALSE() but supposed to be used with pointer expressions.
+ */
+#define EXPECT_NULL(test, expression)					       \
+		EXPECT(test, !(expression),				       \
+		       "Expected " #expression " is NULL, but is not NULL.")
+
+/**
+ * EXPECT_SUCCESS() - Causes a test failure if @expression does not evaluate
+ * to 0.
+ * @test: The test context object.
+ * @expression: an arbitrary expression evaluating to an int error code. The
+ * test fails when this does not evaluate to 0.
+ *
+ * Sets an expectation that @expression evaluates to 0. Implementation assumes
+ * that error codes are represented as negative values and if expression
+ * evaluates to a negative value failure message will contain a mnemonic
+ * representation of the error code (for example, for -1 it will contain EPERM).
+ */
+#define EXPECT_SUCCESS(test, expression) do {				       \
+	struct test_stream *__stream = EXPECT_START(test);		       \
+	typeof(expression) __result = (expression);			       \
+	char buf[64];							       \
+									       \
+	if (result != 0)						       \
+		__stream->add(__stream,					       \
+			      "Expected " #expression " is not error, "	       \
+			      "but is: %s.",				       \
+			      strerror_r(-result, buf, sizeof(buf)));	       \
+	EXPECT_END(test, result != 0, __stream);			       \
+} while (0)
+
+/**
+ * EXPECT_ERROR() - Causes a test failure when @expression evaluates to @errno.
+ * @test: The test context object.
+ * @expression: an arbitrary expression evaluating to an int error code. The
+ * test fails when this does not evaluate to @errno.
+ * @errno: expected error value, error values are expected to be negative.
+ *
+ * Sets an expectation that @expression evaluates to @errno, so as opposed to
+ * EXPECT_SUCCESS it verifies that @expression evaluates to an error.
+ */
+#define EXPECT_ERROR(test, expression, errno) do {			       \
+	struct test_stream *__stream = EXPECT_START(test);		       \
+	typeof(expression) __result = (expression);			       \
+	char buf1[64];							       \
+	char buf2[64];							       \
+									       \
+	if (result != errno)						       \
+		__stream->add(__stream,					       \
+			      "Expected " #expression " is %s, but is: %s.",   \
+			      strerror_t(-errno, buf1, sizeof(buf1)),	       \
+			      strerror_r(-result, buf2, sizeof(buf2)));	       \
+	EXPECT_END(test, result == errno, __stream);			       \
+} while (0)
+
 static inline void test_expect_binary(struct test *test,
 				      long long left, const char *left_name,
 				      long long right, const char *right_name,
@@ -531,22 +607,23 @@
  * @test: The test context object.
  * @ptr: an arbitrary pointer.
  *
- * Sets an expectation that the value that @ptr evaluates to is not null and not
- * an errno stored in a pointer. This is semantically equivalent to
+ * Sets an expectation that the value that @ptr evaluates to is not null and
+ * not an errno stored in a pointer. This is semantically equivalent to
  * EXPECT_TRUE(@test, !IS_ERR_OR_NULL(@ptr)). See EXPECT_TRUE() for more
  * information.
  */
 #define EXPECT_NOT_ERR_OR_NULL(test, ptr) do {				       \
 	struct test_stream *__stream = EXPECT_START(test);		       \
 	typeof(ptr) __ptr = (ptr);					       \
+	char buf[64];							       \
 									       \
 	if (!__ptr)							       \
 		__stream->add(__stream,					       \
 			      "Expected " #ptr " is not null, but is.");       \
 	if (IS_ERR(__ptr))						       \
 		__stream->add(__stream,					       \
-			      "Expected " #ptr " is not error, but is: %ld",   \
-			      PTR_ERR(__ptr));				       \
+			      "Expected " #ptr " is not error, but is: %s",    \
+			      strerror_r(-PTR_ERR(__ptr), buf, sizeof(buf)));  \
 									       \
 	EXPECT_END(test, !IS_ERR_OR_NULL(__ptr), __stream);		       \
 } while (0)
@@ -616,6 +693,74 @@
 		ASSERT(test, !(condition),				       \
 		       "Asserted " #condition " is false, but is true.")
 
+/**
+ * ASSERT_NOT_NULL() - Asserts that @expression does not evaluate to NULL.
+ * @test: The test context object.
+ * @expression: an arbitrary pointer expression. The test fails when this
+ * evaluates to NULL.
+ *
+ * Asserts that @expression does not evaluate to NULL, see EXPECT_NOT_NULL().
+ */
+#define ASSERT_NOT_NULL(test, expression)				       \
+		ASSERT(test, (expression),				       \
+		       "Expected " #expression " is not NULL, but is NULL.")
+
+/**
+ * ASSERT_NULL() - Asserts that @expression evaluates to NULL.
+ * @test: The test context object.
+ * @expression: an arbitrary pointer expression. The test fails when this does
+ * not evaluate to NULL.
+ *
+ * Asserts that @expression evaluates to NULL, see EXPECT_NULL().
+ */
+#define ASSERT_NULL(test, expression)					       \
+		ASSERT(test, !(expression),				       \
+		       "Expected " #expression " is NULL, but is not NULL.")
+
+/**
+ * ASSERT_SUCCESS() - Asserts that @expression is 0.
+ * @test: The test context object.
+ * @expression: an arbitrary expression evaluating to an int error code.
+ *
+ * Asserts that @expression evaluates to 0. It's the same as EXPECT_SUCCESS.
+ */
+#define ASSERT_SUCCESS(test, expression) do {				       \
+	struct test_stream *__stream = ASSERT_START(test);		       \
+	typeof(expression) __result = (expression);			       \
+	char buf[64];							       \
+									       \
+	if (result != 0)						       \
+		__stream->add(__stream,					       \
+			      "Asserted " #expression " is not error, "	       \
+			      "but is: %s.",				       \
+			      strerror_r(-result, buf, sizeof(buf)));	       \
+	ASSERT_END(test, result != 0, __stream);			       \
+} while (0)
+
+/**
+ * ASSERT_ERROR() - Causes a test failure when @expression does not evaluate to
+ * @errno.
+ * @test: The test context object.
+ * @expression: an arbitrary expression evaluating to an int error code. The
+ * test fails when this does not evaluate to @errno.
+ * @errno: expected error value, error values are expected to be negative.
+ *
+ * Asserts that @expression evaluates to @errno, similar to EXPECT_ERROR.
+ */
+#define ASSERT_ERROR(test, expression, errno) do {			       \
+	struct test_stream *__stream = ASSERT_START(test);		       \
+	typeof(expression) __result = (expression);			       \
+	char buf1[64];							       \
+	char buf2[64];							       \
+									       \
+	if (result != errno)						       \
+		__stream->add(__stream,					       \
+			      "Expected " #expression " is %s, but is: %s.",   \
+			      strerror_t(-errno, buf1, sizeof(buf1)),	       \
+			      strerror_r(-result, buf2, sizeof(buf2)));	       \
+	ASSERT_END(test, result == errno, __stream);			       \
+} while (0)
+
 static inline void test_assert_binary(struct test *test,
 				      long long left, const char *left_name,
 				      long long right, const char *right_name,
@@ -770,6 +915,7 @@
 #define ASSERT_NOT_ERR_OR_NULL(test, ptr) do {				       \
 	struct test_stream *__stream = ASSERT_START(test);		       \
 	typeof(ptr) __ptr = (ptr);					       \
+	char buf[64];							       \
 									       \
 	if (!__ptr)							       \
 		__stream->add(__stream,					       \
@@ -777,7 +923,7 @@
 	if (IS_ERR(__ptr))						       \
 		__stream->add(__stream,					       \
 			      "Asserted " #ptr " is not error, but is: %ld",   \
-			      PTR_ERR(__ptr));				       \
+			      strerror_r(-PTR_ERR(__ptr), buf, sizeof(buf)));  \
 									       \
 	ASSERT_END(test, !IS_ERR_OR_NULL(__ptr), __stream);		       \
 } while (0)
diff --git a/test/Makefile b/test/Makefile
index 84a336f..f840886 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,7 +1,7 @@
-obj-$(CONFIG_TEST)		+= test.o mock.o common-mocks.o string-stream.o \
-  test-stream.o test-executor.o
+obj-$(CONFIG_TEST)		+= test.o mock.o common-mocks.o strerror.o \
+  string-stream.o test-stream.o test-executor.o
 obj-$(CONFIG_TEST_TEST)		+= \
-  test-mock.o mock-macro-test.o mock-test.o string-stream-test.o \
-  test-stream-test.o test-test.o
+  test-mock.o mock-macro-test.o mock-test.o strerror-test.o \
+  string-stream-test.o test-stream-test.o test-test.o
 obj-$(CONFIG_TEST_DEATH_TEST)	+= test-death-test.o
 obj-$(CONFIG_EXAMPLE_TEST)	+= example-test.o
diff --git a/test/strerror-test.c b/test/strerror-test.c
new file mode 100644
index 0000000..2de126a
--- /dev/null
+++ b/test/strerror-test.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for strerror and strerror_r
+ *
+ * Copyright (C) 2019, Google LLC.
+ * Author: Mike Krinkin <krinkin@google.com>
+ */
+
+#include <linux/err.h>
+#include <test/strerror.h>
+#include <test/test.h>
+
+
+static void test_strerror_returns_null_for_unknown_errors(struct test *test)
+{
+	EXPECT_NULL(test, strerror(-1));
+	EXPECT_NULL(test, strerror(MAX_ERRNO + 1));
+}
+
+static void test_strerror_r_returns_null_if_buflen_is_zero(struct test *test)
+{
+	EXPECT_NULL(test, strerror_r(-1, NULL, 0));
+}
+
+static void test_strerror_returns_string(struct test *test)
+{
+	const char *err;
+	char buf[64];
+
+	err = strerror(EAGAIN);
+	ASSERT_NOT_NULL(test, err);
+	EXPECT_STREQ(test, err, "EAGAIN");
+
+	err = strerror_r(EAGAIN, buf, sizeof(buf));
+	ASSERT_NOT_NULL(test, err);
+	EXPECT_STREQ(test, err, "EAGAIN");
+}
+
+static void test_strerror_r_correctly_truncates_message_to_buffer_size(
+		struct test *test)
+{
+	const char *err;
+	char buf[64];
+
+	err = strerror_r(EAGAIN, buf, 1);
+	ASSERT_NOT_NULL(test, err);
+	EXPECT_EQ(test, strlen(err), 0);
+
+	err = strerror_r(EAGAIN, buf, 2);
+	ASSERT_NOT_NULL(test, err);
+	EXPECT_EQ(test, strlen(err), 1);
+
+	err = strerror_r(EAGAIN, buf, sizeof(buf));
+	ASSERT_NOT_NULL(test, err);
+	EXPECT_STREQ(test, err, "EAGAIN");
+}
+
+static void test_strerror_r_returns_string_for_unknown_errors(struct test *test)
+{
+	char buf[64];
+
+	EXPECT_NOT_NULL(test, strerror_r(-1, buf, sizeof(buf)));
+	EXPECT_NOT_NULL(test, strerror_r(MAX_ERRNO + 1, buf, sizeof(buf)));
+}
+
+static struct test_case strerror_test_cases[] = {
+	TEST_CASE(test_strerror_returns_null_for_unknown_errors),
+	TEST_CASE(test_strerror_r_returns_null_if_buflen_is_zero),
+	TEST_CASE(test_strerror_returns_string),
+	TEST_CASE(test_strerror_r_correctly_truncates_message_to_buffer_size),
+	TEST_CASE(test_strerror_r_returns_string_for_unknown_errors),
+	{},
+};
+
+static struct test_module strerror_test_module = {
+	.name = "strerror-test",
+	.test_cases = strerror_test_cases,
+};
+module_test(strerror_test_module);
diff --git a/test/strerror.c b/test/strerror.c
new file mode 100644
index 0000000..0bdfbc7
--- /dev/null
+++ b/test/strerror.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * C++ stream style string formatter and printer used in KUnit for outputting
+ * KUnit messages.
+ *
+ * Copyright (C) 2019, Google LLC.
+ * Author: Mike Krinkin <krinkin@google.com>
+ */
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <test/strerror.h>
+
+
+#define SUCCESS	0
+#define MAKE_ERROR(errno)	[errno] = #errno
+
+static const char * const errmap[] = {
+	MAKE_ERROR(SUCCESS),
+	MAKE_ERROR(EPERM),
+	MAKE_ERROR(ENOENT),
+	MAKE_ERROR(ESRCH),
+	MAKE_ERROR(EINTR),
+	MAKE_ERROR(EIO),
+	MAKE_ERROR(ENXIO),
+	MAKE_ERROR(E2BIG),
+	MAKE_ERROR(ENOEXEC),
+	MAKE_ERROR(EBADF),
+	MAKE_ERROR(ECHILD),
+	MAKE_ERROR(EAGAIN),
+	MAKE_ERROR(ENOMEM),
+	MAKE_ERROR(EACCES),
+	MAKE_ERROR(EFAULT),
+	MAKE_ERROR(ENOTBLK),
+	MAKE_ERROR(EBUSY),
+	MAKE_ERROR(EEXIST),
+	MAKE_ERROR(EXDEV),
+	MAKE_ERROR(ENODEV),
+	MAKE_ERROR(ENOTDIR),
+	MAKE_ERROR(EISDIR),
+	MAKE_ERROR(EINVAL),
+	MAKE_ERROR(ENFILE),
+	MAKE_ERROR(EMFILE),
+	MAKE_ERROR(ENOTTY),
+	MAKE_ERROR(ETXTBSY),
+	MAKE_ERROR(EFBIG),
+	MAKE_ERROR(ENOSPC),
+	MAKE_ERROR(ESPIPE),
+	MAKE_ERROR(EROFS),
+	MAKE_ERROR(EMLINK),
+	MAKE_ERROR(EPIPE),
+	MAKE_ERROR(EDOM),
+	MAKE_ERROR(ERANGE),
+	MAKE_ERROR(EDEADLK),
+	MAKE_ERROR(ENAMETOOLONG),
+	MAKE_ERROR(ENOLCK),
+	MAKE_ERROR(ENOSYS),
+	MAKE_ERROR(ENOTEMPTY),
+	MAKE_ERROR(ELOOP),
+	MAKE_ERROR(ENOMSG),
+	MAKE_ERROR(EIDRM),
+	MAKE_ERROR(ECHRNG),
+	MAKE_ERROR(EL2NSYNC),
+	MAKE_ERROR(EL3HLT),
+	MAKE_ERROR(EL3RST),
+	MAKE_ERROR(ELNRNG),
+	MAKE_ERROR(EUNATCH),
+	MAKE_ERROR(ENOCSI),
+	MAKE_ERROR(EL2HLT),
+	MAKE_ERROR(EBADE),
+	MAKE_ERROR(EBADR),
+	MAKE_ERROR(EXFULL),
+	MAKE_ERROR(ENOANO),
+	MAKE_ERROR(EBADRQC),
+	MAKE_ERROR(EBADSLT),
+	MAKE_ERROR(EBFONT),
+	MAKE_ERROR(ENOSTR),
+	MAKE_ERROR(ENODATA),
+	MAKE_ERROR(ETIME),
+	MAKE_ERROR(ENOSR),
+	MAKE_ERROR(ENONET),
+	MAKE_ERROR(ENOPKG),
+	MAKE_ERROR(EREMOTE),
+	MAKE_ERROR(ENOLINK),
+	MAKE_ERROR(EADV),
+	MAKE_ERROR(ESRMNT),
+	MAKE_ERROR(ECOMM),
+	MAKE_ERROR(EPROTO),
+	MAKE_ERROR(EMULTIHOP),
+	MAKE_ERROR(EDOTDOT),
+	MAKE_ERROR(EBADMSG),
+	MAKE_ERROR(EOVERFLOW),
+	MAKE_ERROR(ENOTUNIQ),
+	MAKE_ERROR(EBADFD),
+	MAKE_ERROR(EREMCHG),
+	MAKE_ERROR(ELIBACC),
+	MAKE_ERROR(ELIBBAD),
+	MAKE_ERROR(ELIBSCN),
+	MAKE_ERROR(ELIBMAX),
+	MAKE_ERROR(ELIBEXEC),
+	MAKE_ERROR(EILSEQ),
+	MAKE_ERROR(ERESTART),
+	MAKE_ERROR(ESTRPIPE),
+	MAKE_ERROR(EUSERS),
+	MAKE_ERROR(ENOTSOCK),
+	MAKE_ERROR(EDESTADDRREQ),
+	MAKE_ERROR(EMSGSIZE),
+	MAKE_ERROR(EPROTOTYPE),
+	MAKE_ERROR(ENOPROTOOPT),
+	MAKE_ERROR(EPROTONOSUPPORT),
+	MAKE_ERROR(ESOCKTNOSUPPORT),
+	MAKE_ERROR(EOPNOTSUPP),
+	MAKE_ERROR(EPFNOSUPPORT),
+	MAKE_ERROR(EAFNOSUPPORT),
+	MAKE_ERROR(EADDRINUSE),
+	MAKE_ERROR(EADDRNOTAVAIL),
+	MAKE_ERROR(ENETDOWN),
+	MAKE_ERROR(ENETUNREACH),
+	MAKE_ERROR(ENETRESET),
+	MAKE_ERROR(ECONNABORTED),
+	MAKE_ERROR(ECONNRESET),
+	MAKE_ERROR(ENOBUFS),
+	MAKE_ERROR(EISCONN),
+	MAKE_ERROR(ENOTCONN),
+	MAKE_ERROR(ESHUTDOWN),
+	MAKE_ERROR(ETOOMANYREFS),
+	MAKE_ERROR(ETIMEDOUT),
+	MAKE_ERROR(ECONNREFUSED),
+	MAKE_ERROR(EHOSTDOWN),
+	MAKE_ERROR(EHOSTUNREACH),
+	MAKE_ERROR(EALREADY),
+	MAKE_ERROR(EINPROGRESS),
+	MAKE_ERROR(ESTALE),
+	MAKE_ERROR(EUCLEAN),
+	MAKE_ERROR(ENOTNAM),
+	MAKE_ERROR(ENAVAIL),
+	MAKE_ERROR(EISNAM),
+	MAKE_ERROR(EREMOTEIO),
+	MAKE_ERROR(EDQUOT),
+	MAKE_ERROR(ENOMEDIUM),
+	MAKE_ERROR(EMEDIUMTYPE),
+	MAKE_ERROR(ECANCELED),
+	MAKE_ERROR(ENOKEY),
+	MAKE_ERROR(EKEYEXPIRED),
+	MAKE_ERROR(EKEYREVOKED),
+	MAKE_ERROR(EKEYREJECTED),
+	MAKE_ERROR(EOWNERDEAD),
+	MAKE_ERROR(ENOTRECOVERABLE),
+	MAKE_ERROR(ERFKILL),
+	MAKE_ERROR(EHWPOISON)
+};
+
+const char *strerror(int errno)
+{
+	if (errno < 0 || errno >= ARRAY_SIZE(errmap))
+		return NULL;
+	return errmap[errno];
+}
+
+const char *strerror_r(int errno, char *buf, size_t buflen)
+{
+	const char *err = strerror(errno);
+
+	if (!buflen)
+		return NULL;
+
+	// GNU version of strerror_r() may sometimes return a pointer to a
+	// static string, this implementation doesn't do it mostly to keep
+	// the behavior consistent and predicatble.
+	buf[buflen - 1] = '\0';
+	if (err)
+		return strncpy(buf, err, buflen - 1);
+	snprintf(buf, buflen - 1, "%d (unknown error)", errno);
+	return buf;
+}