+/*
+ * A parameterised tests runs a loop of test cases.
+ * |num_test_cases| counts the total number of test cases
+ * across all tests.
+ */
+static int num_test_cases = 0;
+
+void add_test(const char *test_case_name, int (*test_fn) ())
+{
+ assert(num_tests != OSSL_NELEM(all_tests));
+ all_tests[num_tests].test_case_name = test_case_name;
+ all_tests[num_tests].test_fn = test_fn;
+ all_tests[num_tests].num = -1;
+ ++num_test_cases;
+ ++num_tests;
+}
+
+void add_all_tests(const char *test_case_name, int(*test_fn)(int idx),
+ int num)
+{
+ assert(num_tests != OSSL_NELEM(all_tests));
+ all_tests[num_tests].test_case_name = test_case_name;
+ all_tests[num_tests].param_test_fn = test_fn;
+ all_tests[num_tests].num = num;
+ ++num_tests;
+ num_test_cases += num;
+}
+
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+static int should_report_leaks()
+{
+ /*
+ * When compiled with enable-crypto-mdebug, OPENSSL_DEBUG_MEMORY=0
+ * can be used to disable leak checking at runtime.
+ * Note this only works when running the test binary manually;
+ * the test harness always enables OPENSSL_DEBUG_MEMORY.
+ */
+ char *mem_debug_env = getenv("OPENSSL_DEBUG_MEMORY");
+
+ return mem_debug_env == NULL
+ || (strcmp(mem_debug_env, "0") && strcmp(mem_debug_env, ""));
+}
+#endif
+
+
+void setup_test()
+{
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+ if (should_report_leaks()) {
+ CRYPTO_set_mem_debug(1);
+ CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+ }
+#endif
+}
+
+int finish_test(int ret)
+{
+#ifndef OPENSSL_NO_CRYPTO_MDEBUG
+ if (should_report_leaks() && CRYPTO_mem_leaks_fp(stderr) <= 0)
+ return EXIT_FAILURE;
+#endif
+ return ret;
+}
+
+static void finalize(int success)
+{
+ if (success)
+ ERR_clear_error();
+ else
+ ERR_print_errors_fp(stderr);
+}
+
+int run_tests(const char *test_prog_name)
+{
+ int num_failed = 0;
+
+ int i, j;
+
+ printf("%s: %d test case%s\n", test_prog_name, num_test_cases,
+ num_test_cases == 1 ? "" : "s");
+
+ for (i = 0; i != num_tests; ++i) {
+ if (all_tests[i].num == -1) {
+ int ret = all_tests[i].test_fn();
+
+ if (!ret) {
+ printf("** %s failed **\n--------\n",
+ all_tests[i].test_case_name);
+ ++num_failed;
+ }
+ finalize(ret);
+ } else {
+ for (j = 0; j < all_tests[i].num; j++) {
+ int ret = all_tests[i].param_test_fn(j);
+
+ if (!ret) {
+ printf("** %s failed test %d\n--------\n",
+ all_tests[i].test_case_name, j);
+ ++num_failed;
+ }
+ finalize(ret);
+ }
+ }
+ }
+
+ if (num_failed != 0) {
+ printf("%s: %d test%s failed (out of %d)\n", test_prog_name,
+ num_failed, num_failed != 1 ? "s" : "", num_test_cases);
+ return EXIT_FAILURE;
+ }
+ printf(" All tests passed.\n");
+ return EXIT_SUCCESS;
+}
+
+static const char *print_string_maybe_null(const char *s)
+{
+ return s == NULL ? "(NULL)" : s;
+}