crt: tests: add new test t_wassert

This test is similar to t_assert, except it tests CRT function `_wassert`.

Unlike `_assert`, CRT function `_wassert` properly behaves in case when
`stderr` was set to Unicode translation mode.

When this test is compiled with msvcrt.dll, or msvcr80.dll and later, it also
sets `stderr` to Unicode transaltion mode before triggering an `assert`.

Signed-off-by: Kirill Makurin <maiddaisuki@outlook.com>
Signed-off-by: LIU Hao <lh_mouse@126.com>
diff --git a/mingw-w64-crt/testcases/Makefile.am b/mingw-w64-crt/testcases/Makefile.am
index b4cb67e..6cfa3f8 100644
--- a/mingw-w64-crt/testcases/Makefile.am
+++ b/mingw-w64-crt/testcases/Makefile.am
@@ -187,6 +187,7 @@
   t_tsearch \
   t_utime \
   t_vsscanf \
+  t_wassert \
   t_wcrtomb \
   t_wcsrtombs \
   t_wcstok_s \
diff --git a/mingw-w64-crt/testcases/t_wassert.c b/mingw-w64-crt/testcases/t_wassert.c
new file mode 100644
index 0000000..b76a776
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_wassert.c
@@ -0,0 +1,85 @@
+#define _UNICODE
+#include <assert.h>
+#include <fcntl.h>
+#include <io.h>
+#include <process.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* mingw-w64 headers */
+#include "libtest.h"
+
+/**
+ * On some Windows versions, crtdll.dll, msvcrt20.dll and msvcrt40.dll may be
+ * redirected to system msvcrt.dll.
+ *
+ * This may result in `_setmode` succeeding to set `stderr` to Unicode
+ * translation mode, while `_wassert` emulation still calls `_assert`.
+ *
+ * This would result in `assert` macro silently terminating the process without
+ * producing any output.
+ *
+ * CRT function `_wassert` is available since msvcr80.dll, and msvcrt.dll
+ * since Windows Vista. Only attempt to set `stderr` to Unicode translation
+ * mode with msvcrt.dll, or msvcr80.dll and later.
+ */
+#if __MSVCRT_VERSION__ == 0x0600 || __MSVCRT_VERSION__ >= 0x0800
+#define TEST_UNICODE_MODE
+#endif
+
+int main(int argc, char *argv[]) {
+    mingw_test_init ();
+
+    if (argc != 2 || strcmp(argv[1], "assert_test") != 0) {
+        int exit_code;
+        int pipefd[2];
+        int back_errfd;
+        intptr_t process;
+        ssize_t size;
+        char buf[512];
+
+        assert(_pipe(pipefd, 0, O_NOINHERIT) == 0);
+
+        /* set stderr fd to write side of pipe, will be used by _spawnl() */
+        assert((back_errfd = dup(STDERR_FILENO)) >= 0);
+        assert(dup2(pipefd[1], STDERR_FILENO) == 0);
+        assert(close(pipefd[1]) == 0);
+
+        process = _spawnl(_P_NOWAIT, _pgmptr, argv[0], "assert_test", NULL);
+
+        /* revert back stderr fd */
+        assert(dup2(back_errfd, STDERR_FILENO) == 0);
+        assert(close(back_errfd) == 0);
+
+        assert(process != -1);
+
+        size = read(pipefd[0], buf, sizeof(buf));
+        assert(size > 0); /* some data were written by child process */
+        assert(strnlen(buf, sizeof(buf)) > 0);
+
+        /* wait until child process exits */
+        assert(_cwait(&exit_code, process, _WAIT_CHILD) == process);
+        assert(exit_code != 0);
+
+        /* read the rest of data in the pipe and write it to stdout */
+        if (size < (ssize_t)sizeof(buf)) {
+            size += read(pipefd[0], &buf[size], sizeof(buf) - size);
+        }
+        close(pipefd[0]);
+        write(STDOUT_FILENO, buf, size);
+
+        return 0;
+    }
+
+#ifdef TEST_UNICODE_MODE
+    /* change stderr to translated UNICODE mode */
+    _setmode(_fileno(stderr), _O_U8TEXT);
+#endif
+
+    /* call assert, it prints to stderr, parent process will check that our stderr is not empty */
+    assert(0);
+
+    /* assert(0) does not return, this process pass when returns non-zero */
+    return 0;
+}