medit/moo/mooutils/moo-test-utils.cpp

562 lines
13 KiB
C++
Raw Normal View History

/*
* moo-test-utils.c
*
2010-12-21 20:15:45 -08:00
* Copyright (C) 2004-2010 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
*
* This file is part of medit. medit is free software; you can
* redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public
* License along with medit. If not, see <http://www.gnu.org/licenses/>.
*/
2008-01-30 23:16:57 -08:00
#include "moo-test-macros.h"
2008-02-10 10:56:31 -08:00
#include "mooutils/mooutils-fs.h"
2013-05-11 21:25:09 -07:00
#include "mooutils/mooutils-messages.h"
2016-10-23 12:43:28 -07:00
#include "moocpp/gstr.h"
2008-01-30 23:16:57 -08:00
#include <stdio.h>
2009-11-24 20:57:57 -08:00
#include <stdlib.h>
2011-01-02 01:41:49 -08:00
#include <string.h>
2016-10-23 12:43:28 -07:00
#include <vector>
2008-01-30 23:16:57 -08:00
2016-10-23 12:43:28 -07:00
struct TestAssertInfo
{
gstr text;
gstr file;
2008-01-30 23:16:57 -08:00
int line;
2016-10-23 12:43:28 -07:00
TestAssertInfo(const char* text,
const char* file,
int line)
: text(text)
, file(file)
, line(line)
{
}
};
struct MooTest
{
gstr name;
gstr description;
2008-01-30 23:16:57 -08:00
MooTestFunc func;
gpointer data;
2016-10-23 12:43:28 -07:00
std::vector<TestAssertInfo> failed_asserts;
};
2008-01-30 23:16:57 -08:00
2016-10-23 12:43:28 -07:00
struct MooTestSuite
{
gstr name;
gstr description;
std::vector<MooTest> tests;
2008-01-30 23:16:57 -08:00
MooTestSuiteInit init_func;
MooTestSuiteCleanup cleanup_func;
gpointer data;
};
2016-10-23 12:43:28 -07:00
struct MooTestRegistry
{
gstr data_dir;
std::vector<MooTestSuite> test_suites;
2008-01-30 23:16:57 -08:00
MooTestSuite *current_suite;
MooTest *current_test;
struct TestRun {
guint suites;
guint tests;
guint asserts;
2010-09-27 01:25:15 -07:00
guint suites_passed;
2008-01-30 23:16:57 -08:00
guint tests_passed;
guint asserts_passed;
} tr;
2016-10-23 12:43:28 -07:00
};
2008-01-30 23:16:57 -08:00
static MooTestRegistry registry;
2016-10-23 12:43:28 -07:00
MooTestSuite&
moo_test_suite_new(const char *name,
const char *description,
MooTestSuiteInit init_func,
MooTestSuiteCleanup cleanup_func,
gpointer data)
2008-01-30 23:16:57 -08:00
{
2016-10-23 12:43:28 -07:00
MooTestSuite ts;
2016-10-24 23:35:31 -07:00
ts.name.copy(name);
ts.description.copy(description);
2016-10-23 12:43:28 -07:00
ts.init_func = init_func;
ts.cleanup_func = cleanup_func;
ts.data = data;
registry.test_suites.push_back(std::move(ts));
return registry.test_suites.back();
2008-01-30 23:16:57 -08:00
}
void
2016-10-23 12:43:28 -07:00
moo_test_suite_add_test(MooTestSuite &ts,
const char *name,
const char *description,
MooTestFunc test_func,
gpointer data)
2008-01-30 23:16:57 -08:00
{
2016-10-23 12:43:28 -07:00
g_return_if_fail(name != NULL);
g_return_if_fail(test_func != NULL);
2008-01-30 23:16:57 -08:00
2016-10-23 12:43:28 -07:00
MooTest test;
2016-10-24 23:35:31 -07:00
test.name.copy(name);
test.description.copy(description);
2016-10-23 12:43:28 -07:00
test.func = test_func;
test.data = data;
2008-01-30 23:16:57 -08:00
2016-10-23 12:43:28 -07:00
ts.tests.push_back(std::move(test));
2008-01-30 23:16:57 -08:00
}
2010-09-27 01:25:15 -07:00
static gboolean
2016-10-23 12:43:28 -07:00
run_test(MooTest &test,
MooTestSuite &ts,
MooTestOptions opts)
2008-01-30 23:16:57 -08:00
{
gboolean failed;
2008-05-05 08:13:42 -07:00
if (opts & MOO_TEST_LIST_ONLY)
{
2016-10-23 12:43:28 -07:00
fprintf (stdout, " Test: %s - %s\n", test.name.get(), test.description.get());
2010-09-27 01:25:15 -07:00
return TRUE;
2008-05-05 08:13:42 -07:00
}
2016-10-23 12:43:28 -07:00
MooTestEnv env;
env.suite_data = ts.data;
env.test_data = test.data;
2008-01-30 23:16:57 -08:00
2016-10-23 12:43:28 -07:00
fprintf(stdout, " Test: %s ... ", test.name.get());
fflush(stdout);
2008-01-30 23:16:57 -08:00
2016-10-23 12:43:28 -07:00
registry.current_test = &test;
test.func(&env);
2008-01-30 23:16:57 -08:00
registry.current_test = NULL;
2016-10-23 12:43:28 -07:00
failed = !test.failed_asserts.empty();
2008-01-30 23:16:57 -08:00
if (failed)
fprintf (stdout, "FAILED\n");
else
fprintf (stdout, "passed\n");
2016-10-23 12:43:28 -07:00
if (!test.failed_asserts.empty())
2008-01-30 23:16:57 -08:00
{
2016-10-23 12:43:28 -07:00
int count = 1;
for (const auto& ai: test.failed_asserts)
2008-01-30 23:16:57 -08:00
{
2016-10-23 12:43:28 -07:00
fprintf (stdout, " %d. %s", count, !ai.file.empty() ? ai.file.get() : "<unknown>");
if (ai.line > -1)
fprintf (stdout, ":%d", ai.line);
fprintf (stdout, " - %s\n", !ai.text.empty() ? ai.text.get() : "FAILED");
++count;
2008-01-30 23:16:57 -08:00
}
}
registry.tr.tests += 1;
if (!failed)
registry.tr.tests_passed += 1;
2010-09-27 01:25:15 -07:00
return !failed;
2008-01-30 23:16:57 -08:00
}
static void
2016-10-23 12:43:28 -07:00
run_suite (MooTestSuite &ts,
2008-05-05 08:13:42 -07:00
MooTest *single_test,
MooTestOptions opts)
2008-01-30 23:16:57 -08:00
{
2008-05-05 08:13:42 -07:00
gboolean run = !(opts & MOO_TEST_LIST_ONLY);
2010-09-27 01:25:15 -07:00
gboolean passed = TRUE;
2008-01-30 23:16:57 -08:00
2016-10-23 12:43:28 -07:00
if (run && ts.init_func && !ts.init_func(ts.data))
2008-01-30 23:16:57 -08:00
return;
2016-10-23 12:43:28 -07:00
registry.current_suite = &ts;
2008-01-30 23:16:57 -08:00
2016-10-23 12:43:28 -07:00
g_print ("Suite: %s\n", ts.name.get());
2008-01-30 23:16:57 -08:00
2008-05-05 08:13:42 -07:00
if (single_test)
2016-10-23 12:43:28 -07:00
{
passed = run_test(*single_test, ts, opts) && passed;
}
2008-05-05 08:13:42 -07:00
else
2016-10-23 12:43:28 -07:00
{
for (auto& t: ts.tests)
passed = run_test(t, ts, opts) && passed;
}
2008-01-30 23:16:57 -08:00
2016-10-23 12:43:28 -07:00
if (run && ts.cleanup_func)
ts.cleanup_func(ts.data);
2008-01-30 23:16:57 -08:00
registry.current_suite = NULL;
registry.tr.suites += 1;
2010-09-27 01:25:15 -07:00
if (passed)
registry.tr.suites_passed += 1;
2008-01-30 23:16:57 -08:00
}
2008-05-05 08:13:42 -07:00
static gboolean
2016-10-23 12:43:28 -07:00
find_test (const gstr& name,
2008-05-05 08:13:42 -07:00
MooTestSuite **ts_p,
MooTest **test_p)
{
2016-10-23 12:43:28 -07:00
*ts_p = nullptr;
*test_p = nullptr;
2016-10-23 12:43:28 -07:00
std::vector<gstr> pieces = name.split("/", 2);
g_return_val_if_fail (!pieces.empty(), FALSE);
2016-10-23 12:43:28 -07:00
const gstr& suite_name = pieces[0];
const gstr& test_name = pieces.size() > 1 ? pieces[1] : gstr();
2016-10-23 12:43:28 -07:00
for (auto& ts: registry.test_suites)
2008-05-05 08:13:42 -07:00
{
2016-10-23 12:43:28 -07:00
if (ts.name == suite_name)
{
2016-10-23 12:43:28 -07:00
if (test_name.empty())
{
*ts_p = &ts;
*test_p = NULL;
return true;
}
2008-05-05 08:13:42 -07:00
2016-10-23 12:43:28 -07:00
for (auto& test : ts.tests)
{
2016-10-23 12:43:28 -07:00
if (test.name == test_name)
{
*ts_p = &ts;
*test_p = &test;
return true;
}
}
2008-05-05 08:13:42 -07:00
2016-10-23 12:43:28 -07:00
break;
}
2008-05-05 08:13:42 -07:00
}
2016-10-23 12:43:28 -07:00
return false;
2008-05-05 08:13:42 -07:00
}
2011-01-02 01:41:49 -08:00
gboolean
2016-10-24 23:35:31 -07:00
moo_test_run_tests (const gstrvec& tests,
2011-01-02 01:41:49 -08:00
const char *coverage_file,
2008-05-05 08:13:42 -07:00
MooTestOptions opts)
2008-01-30 23:16:57 -08:00
{
2011-01-02 01:41:49 -08:00
if (coverage_file)
moo_test_coverage_enable ();
2008-02-10 10:56:31 -08:00
2008-01-30 23:16:57 -08:00
fprintf (stdout, "\n");
2016-10-24 23:35:31 -07:00
if (!tests.empty())
{
2016-10-24 23:35:31 -07:00
for (const auto& name: tests)
{
MooTestSuite *single_ts = NULL;
MooTest *single_test = NULL;
if (!find_test (name, &single_ts, &single_test))
{
2017-10-23 15:23:48 -07:00
g_printerr ("could not find test %s", name.get());
exit (EXIT_FAILURE);
}
2016-10-23 12:43:28 -07:00
run_suite (*single_ts, single_test, opts);
}
}
2008-05-05 08:13:42 -07:00
else
{
2016-10-23 12:43:28 -07:00
for (auto& ts: registry.test_suites)
run_suite(ts, nullptr, opts);
}
2008-01-30 23:16:57 -08:00
fprintf (stdout, "\n");
2008-05-05 08:13:42 -07:00
if (!(opts & MOO_TEST_LIST_ONLY))
{
2011-01-25 23:31:24 -08:00
fprintf (stdout, "Run Summary: Type Total Ran Passed Failed\n");
fprintf (stdout, " suites %5d %4d %6d %6d\n",
2010-09-27 01:25:15 -07:00
registry.tr.suites, registry.tr.suites, registry.tr.suites_passed,
registry.tr.suites - registry.tr.suites_passed);
2011-01-25 23:31:24 -08:00
fprintf (stdout, " tests %5d %4d %6d %6d\n",
2008-05-05 08:13:42 -07:00
registry.tr.tests, registry.tr.tests, registry.tr.tests_passed,
registry.tr.tests - registry.tr.tests_passed);
2011-01-25 23:31:24 -08:00
fprintf (stdout, " asserts %5d %4d %6d %6d\n",
2008-05-05 08:13:42 -07:00
registry.tr.asserts, registry.tr.asserts, registry.tr.asserts_passed,
registry.tr.asserts - registry.tr.asserts_passed);
fprintf (stdout, "\n");
2011-01-02 01:41:49 -08:00
if (coverage_file)
moo_test_coverage_write (coverage_file);
2008-05-05 08:13:42 -07:00
}
2011-01-02 01:41:49 -08:00
return moo_test_get_result ();
2008-01-30 23:16:57 -08:00
}
void
moo_test_cleanup (void)
{
2008-02-10 10:56:31 -08:00
GError *error = NULL;
2016-10-23 12:43:28 -07:00
registry.test_suites.clear();
registry.data_dir.clear();
2008-02-10 10:56:31 -08:00
if (g_file_test (moo_test_get_working_dir (), G_FILE_TEST_IS_DIR) &&
!_moo_remove_dir (moo_test_get_working_dir (), TRUE, &error))
{
g_critical ("could not remove directory '%s': %s",
moo_test_get_working_dir (), error->message);
g_error_free (error);
}
2008-01-30 23:16:57 -08:00
}
gboolean
moo_test_get_result (void)
{
return registry.tr.asserts_passed == registry.tr.asserts;
}
2011-01-01 18:53:27 -08:00
/**
* moo_test_assert_impl: (moo.private 1)
*
* @passed:
* @text: (type const-utf8)
* @file: (type const-utf8) (allow-none) (default NULL)
* @line: (default -1)
**/
2008-01-30 23:16:57 -08:00
void
moo_test_assert_impl (gboolean passed,
const char *text,
const char *file,
int line)
{
g_return_if_fail (registry.current_test != NULL);
registry.tr.asserts += 1;
if (passed)
registry.tr.asserts_passed += 1;
else
2016-10-23 12:43:28 -07:00
registry.current_test->failed_asserts.push_back(TestAssertInfo(text, file, line));
2008-01-30 23:16:57 -08:00
}
void
moo_test_assert_msgv (gboolean passed,
const char *file,
int line,
const char *format,
va_list args)
{
char *text = NULL;
if (format)
text = g_strdup_vprintf (format, args);
moo_test_assert_impl (passed, text, file, line);
g_free (text);
}
void
moo_test_assert_msg (gboolean passed,
const char *file,
int line,
const char *format,
...)
{
va_list args;
va_start (args, format);
moo_test_assert_msgv (passed, file, line, format, args);
va_end (args);
}
2008-02-10 10:56:31 -08:00
2016-10-23 12:43:28 -07:00
const gstr&
2008-02-10 10:56:31 -08:00
moo_test_get_data_dir (void)
{
return registry.data_dir;
}
2011-01-02 01:41:49 -08:00
void
moo_test_set_data_dir (const char *dir)
{
g_return_if_fail (dir != NULL);
2011-01-02 01:41:49 -08:00
if (!g_file_test (dir, G_FILE_TEST_IS_DIR))
{
g_critical ("not a directory: %s", dir);
2011-01-02 01:41:49 -08:00
return;
}
2016-10-23 12:43:28 -07:00
registry.data_dir.steal(_moo_normalize_file_path(dir));
2011-01-02 01:41:49 -08:00
}
2008-02-10 10:56:31 -08:00
const char *
moo_test_get_working_dir (void)
{
return "test-working-dir";
}
2016-10-23 12:43:28 -07:00
gstr
2011-01-01 18:53:27 -08:00
moo_test_find_data_file (const char *basename)
{
2016-10-23 12:43:28 -07:00
g_return_val_if_fail(!registry.data_dir.empty(), gstr());
2011-01-01 18:53:27 -08:00
2016-10-23 12:43:28 -07:00
if (!_moo_path_is_absolute(basename))
return gstr::take(g_build_filename(registry.data_dir.get(), basename, NULL));
2011-01-01 18:53:27 -08:00
else
2016-10-24 23:35:31 -07:00
return gstr(basename);
2011-01-01 18:53:27 -08:00
}
2011-01-02 01:41:49 -08:00
char **
moo_test_list_data_files (const char *dir)
{
GDir *gdir;
2016-10-23 12:43:28 -07:00
gstr tmp;
2011-01-02 01:41:49 -08:00
GError *error = NULL;
const char *name;
GPtrArray *names = NULL;
if (!_moo_path_is_absolute (dir))
{
2016-10-23 12:43:28 -07:00
g_return_val_if_fail (registry.data_dir != nullptr, NULL);
tmp.steal(g_build_filename(registry.data_dir.get(), dir, NULL));
dir = tmp.get();
2011-01-02 01:41:49 -08:00
}
if (!(gdir = g_dir_open (dir, 0, &error)))
{
g_warning ("could not open directory '%s': %s",
dir, moo_error_message (error));
2011-01-02 01:41:49 -08:00
g_error_free (error);
error = NULL;
}
names = g_ptr_array_new ();
while (gdir && (name = g_dir_read_name (gdir)))
g_ptr_array_add (names, g_strdup (name));
g_ptr_array_add (names, NULL);
if (gdir)
g_dir_close (gdir);
return (char**) g_ptr_array_free (names, FALSE);
}
2008-02-10 10:56:31 -08:00
char *
moo_test_load_data_file (const char *basename)
{
char *fullname;
char *contents = NULL;
GError *error = NULL;
2016-10-23 12:43:28 -07:00
g_return_val_if_fail (registry.data_dir != nullptr, NULL);
2008-02-10 10:56:31 -08:00
2010-09-27 01:25:15 -07:00
if (!_moo_path_is_absolute (basename))
2016-10-23 12:43:28 -07:00
fullname = g_build_filename (registry.data_dir.get(), basename, NULL);
2010-09-27 01:25:15 -07:00
else
fullname = g_strdup (basename);
2008-02-10 10:56:31 -08:00
if (!g_file_get_contents (fullname, &contents, NULL, &error))
{
TEST_FAILED_MSG ("could not open file `%s': %s",
fullname, error->message);
g_error_free (error);
}
g_free (fullname);
return contents;
}
2011-01-02 01:41:49 -08:00
/************************************************************************************
* coverage
*/
static GHashTable *called_functions = NULL;
void
moo_test_coverage_enable (void)
{
g_return_if_fail (called_functions == NULL);
2011-01-02 01:41:49 -08:00
called_functions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}
static void
add_func (gpointer key,
G_GNUC_UNUSED gpointer value,
GString *string)
{
2016-10-23 12:43:28 -07:00
g_string_append (string, (const char*) key);
2011-01-02 01:41:49 -08:00
g_string_append (string, "\n");
}
void
moo_test_coverage_write (const char *filename)
{
GString *content;
GError *error = NULL;
g_return_if_fail (called_functions != NULL);
g_return_if_fail (filename != NULL);
2011-01-02 01:41:49 -08:00
content = g_string_new (NULL);
g_hash_table_foreach (called_functions, (GHFunc) add_func, content);
if (!g_file_set_contents (filename, content->str, -1, &error))
{
g_critical ("could not save file %s: %s", filename, moo_error_message (error));
2011-01-02 01:41:49 -08:00
g_error_free (error);
}
g_string_free (content, TRUE);
g_hash_table_destroy (called_functions);
called_functions = NULL;
}
void
2011-01-09 23:33:47 -08:00
moo_test_coverage_record (const char *lang,
const char *function)
2011-01-02 01:41:49 -08:00
{
if (G_UNLIKELY (called_functions))
g_hash_table_insert (called_functions,
2011-01-09 23:33:47 -08:00
g_strdup_printf ("%s.%s", lang, function),
2011-01-02 01:41:49 -08:00
GINT_TO_POINTER (TRUE));
}
2011-01-22 01:31:21 -08:00
/************************************************************************************
* log handlers
*/
static gboolean silent_messages;
static void
silent_log_handler (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
gpointer data)
{
if (log_level & G_LOG_LEVEL_ERROR)
g_log_default_handler (log_domain, log_level, message, data);
}
/**
2011-01-24 03:21:24 -08:00
* moo_test_set_silent_messages: (moo.private 1)
2011-01-22 01:31:21 -08:00
**/
gboolean
moo_test_set_silent_messages (gboolean silent)
{
if (silent == silent_messages)
return silent_messages;
if (silent)
g_log_set_default_handler (silent_log_handler, NULL);
else
g_log_set_default_handler (g_log_default_handler, NULL);
silent_messages = silent;
return !silent;
}