crt: tests: add helper library libtest.a

This static library contains helper functions for use by test programs;
it is linked into every test program.

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 1bf8f5a..0b6336e 100644
--- a/mingw-w64-crt/testcases/Makefile.am
+++ b/mingw-w64-crt/testcases/Makefile.am
@@ -4,6 +4,21 @@
 AM_CFLAGS=@ADD_C_CXX_WARNING_FLAGS@ @ADD_C_ONLY_WARNING_FLAGS@ $(TEST_CFLAGS) -Wno-missing-prototypes
 AM_CXXFLAGS=@ADD_C_CXX_WARNING_FLAGS@ @ADD_CXX_ONLY_WARNING_FLAGS@ $(TEST_CFLAGS)
 
+# Link all test programs against libtest.a; for targets with their own
+# target-specifc *_LDADD variable, make sure to add $(LDADD).
+LDADD = $(builddir)/libtest.a
+
+check_LIBRARIES = \
+  libtest.a
+
+# libtest.a is a static library containing helper function for use by tests
+# programs.
+#
+# When you write a new test, please include libtest.h and add a call to
+# mingw_test_init() function upon entry to main().
+libtest_a_SOURCES = \
+  libtest.c libtest.h
+
 # Used by t_safe_flush
 libprocdetach.dll: libprocdetach.o
 	$(LINK) -shared libprocdetach.o -o libprocdetach.dll
@@ -165,7 +180,7 @@
 t_intrinc_CFLAGS = -std=c99 $(AM_CFLAGS)
 
 # Link t_safe_flush against libprocdetach.dll
-t_safe_flush_LDADD = libprocdetach.dll
+t_safe_flush_LDADD = libprocdetach.dll $(LDADD)
 
 # Compile t_tls_ansi in C89 mode
 t_tls_ansi_CFLAGS = -std=c89 $(AM_CFLAGS)
@@ -174,7 +189,7 @@
 t_tls_c11_CFLAGS = -std=c11 $(AM_CFLAGS)
 
 # Link t_winmain against gdi32.dll
-t_winmain_LDADD = -lgdi32
+t_winmain_LDADD = -lgdi32 $(LDADD)
 
 if ENABLE_TESTS_UNICODE
   # The following tests require compiler support for -municode option
@@ -188,8 +203,6 @@
   t__wstat_all_CFLAGS = -municode $(AM_CFLAGS)
 endif
 
-check_LIBRARIES =
-
 check_PROGRAMS = \
   $(testcase_compile_only) \
   $(testcase_progs)
diff --git a/mingw-w64-crt/testcases/libtest.c b/mingw-w64-crt/testcases/libtest.c
new file mode 100644
index 0000000..9d39c40
--- /dev/null
+++ b/mingw-w64-crt/testcases/libtest.c
@@ -0,0 +1,113 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <assert.h>
+#include <crtdbg.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "libtest.h"
+
+/**
+ * Invalid parameter handler.
+ */
+__attribute__ ((__noreturn__))
+static void __cdecl mingw_test_invalid_parameter_handler (
+  const wchar_t *expression,
+  const wchar_t *function,
+  const wchar_t *file,
+  unsigned int line,
+  uintptr_t pReserved
+) {
+  fprintf(stderr, "Invalid parameter: %ls", expression);
+  /* Additional arguments are non-NULL only for debug CRT DLLs */
+  if (function)
+    fprintf(stderr, ", function %ls", function);
+  if (file)
+    fprintf(stderr, ", file %ls", file);
+  if (line)
+    fprintf(stderr, ", line %d", line);
+  fprintf(stderr, "\n");
+  (void)pReserved;
+  /* Ensure that this handler does not return, as the whole handler replaces calling of Dr. Watson */
+	abort ();
+}
+
+static int __cdecl mingw_test_fini (void) {
+  _CrtCheckMemory ();
+  _CrtDumpMemoryLeaks ();
+
+  return EXIT_SUCCESS;
+}
+
+void mingw_test_init (void) {
+  UINT oldErrorMode = SetErrorMode (0);
+  SetErrorMode (oldErrorMode | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
+
+  /**
+   * msvcr80.dll and later attempt to set multibyte code page to active
+   * ANSI code page during initialization.
+   *
+   * If active ANSI code page is 65001 (UTF-8), _setmbcp will fail
+   * and set `errno` to EINVAL.
+   *
+   * Some tests may assume `errno` to be set to zero when entering main.
+   */
+  errno = 0;
+
+  /* Set CRT _invalid_parameter() output to CRT stderr and call abort (the default is to just call Dr. Watson) */
+  _set_invalid_parameter_handler (mingw_test_invalid_parameter_handler);
+
+  /**
+   * Disable buffering.
+   * This ensures that all output is written to log files.
+   */
+  setvbuf (stdout, NULL, _IONBF, 0);
+  setvbuf (stderr, NULL, _IONBF, 0);
+
+#if __MSVCRT_VERSION__ >= 0x0800
+  /**
+   * Make `abort` as silent as possible.
+   */
+  _set_abort_behavior (0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
+#endif
+
+#if __MSVCRT_VERSION__ >= 0x0400
+  /**
+   * We don't want failed assertions to pop-up a message box.
+   */
+  assert (_set_error_mode (_OUT_TO_STDERR) != -1);
+#endif
+
+  /**
+   * Redirect `_CRT_WARN` debug messages to `stderr`.
+   */
+  assert (_CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE) != -1);
+  assert (_CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR) != _CRTDBG_HFILE_ERROR);
+
+  /**
+   * Redirect `_CRT_ERROR` debug messages to `stderr`.
+   */
+  assert (_CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE) != -1);
+  assert (_CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR) != _CRTDBG_HFILE_ERROR);
+
+  /**
+   * Redirect `_CRT_ASSERT` debug messages to `stderr`.
+   */
+  assert (_CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE) != -1);
+  assert (_CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR) != _CRTDBG_HFILE_ERROR);
+
+  _onexit (mingw_test_fini);
+}
diff --git a/mingw-w64-crt/testcases/libtest.h b/mingw-w64-crt/testcases/libtest.h
new file mode 100644
index 0000000..18a612b
--- /dev/null
+++ b/mingw-w64-crt/testcases/libtest.h
@@ -0,0 +1,37 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#ifndef __MINGW_LIBTEST_H
+#define __MINGW_LIBTEST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This function should be called at the beginning of every test program.
+ *
+ * Currently, this function:
+ *
+ * 1. Calls `SetErrorMode`
+ * 2. Sets `errno` to zero, so that tests can assume it to be zero upon entry
+ *   to main()
+ * 3. Sets invalid parameter handler which terminates the process by calling
+ *   `abort` (instead of default behavior to invoke Dr. Watson)
+ * 4. Disables buffering on `stdout` and `stderr` to ensure that all output
+ *   is written to log files
+ * 5. Calls `_set_abort_behavior` so that `abort` does not pop up a message box
+ *   and does not invoke Dr. Watson
+ * 6. Calls `_set_error_mode` so that `abort` and failed `assert` does not
+ *   pop up a message box
+ */
+void mingw_test_init (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MINGW_LIBTEST_H */