mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 00:12:19 +01:00 
			
		
		
		
	Merge branch 'move_assert-msg-test' into 'main'
Convert tests/assert-msg-test* to glib/tests/assert-msg-test* Closes #1434 See merge request GNOME/glib!2767
This commit is contained in:
		| @@ -25,9 +25,9 @@ for filename in in_files: | ||||
|     with open(filename, "rb") as f: | ||||
|         for line in f: | ||||
|             line = line.rstrip(b"\n").rstrip(b"\r") | ||||
|             match = re.search(br"\bg_[a-zA-Z0-9_]*_get_type\b", line) | ||||
|             match = re.search(rb"\bg_[a-zA-Z0-9_]*_get_type\b", line) | ||||
|             if match: | ||||
|                 func = match.group(0).decode('utf-8') | ||||
|                 func = match.group(0).decode("utf-8") | ||||
|                 if func not in funcs: | ||||
|                     funcs.append(func) | ||||
|                     if debug: | ||||
|   | ||||
							
								
								
									
										155
									
								
								glib/tests/assert-msg-test.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										155
									
								
								glib/tests/assert-msg-test.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| # Copyright © 2022 Emmanuel Fleury <emmanuel.fleury@gmail.com> | ||||
| # | ||||
| # This library 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. | ||||
| # | ||||
| # This library is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
| # Lesser General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU Lesser General Public | ||||
| # License along with this library; if not, write to the Free Software | ||||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||||
| # MA  02110-1301  USA | ||||
|  | ||||
| """ Integration tests for g_assert() functions. """ | ||||
|  | ||||
| import collections | ||||
| import os | ||||
| import shutil | ||||
| import subprocess | ||||
| import sys | ||||
| import tempfile | ||||
| import unittest | ||||
|  | ||||
| import taptestrunner | ||||
|  | ||||
| Result = collections.namedtuple("Result", ("info", "out", "err")) | ||||
|  | ||||
| GDB_SCRIPT = """ | ||||
| # Work around https://sourceware.org/bugzilla/show_bug.cgi?id=22501 | ||||
| set confirm off | ||||
| set print elements 0 | ||||
| set auto-load safe-path / | ||||
| run | ||||
| print *((char**) &__glib_assert_msg) | ||||
| quit | ||||
| """ | ||||
|  | ||||
|  | ||||
| class TestAssertMessage(unittest.TestCase): | ||||
|     """Integration test for throwing message on g_assert(). | ||||
|  | ||||
|     This can be run when installed or uninstalled. When uninstalled, | ||||
|     it requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set. | ||||
|  | ||||
|     The idea with this test harness is to test if g_assert() prints | ||||
|     an error message when called, and that it saves this error | ||||
|     message in a global variable accessible to gdb, so that developers | ||||
|     and automated tools can more easily debug assertion failures. | ||||
|     """ | ||||
|  | ||||
|     def setUp(self): | ||||
|         self.__gdb = shutil.which("gdb") | ||||
|         self.timeout_seconds = 10  # seconds per test | ||||
|  | ||||
|         if "G_TEST_BUILDDIR" in os.environ: | ||||
|             self.__assert_msg_test = os.path.join( | ||||
|                 os.environ["G_TEST_BUILDDIR"], "assert-msg-test" | ||||
|             ) | ||||
|         else: | ||||
|             self.__assert_msg_test = shutil.which("assert-msg-test") | ||||
|         print("assert-msg-test:", self.__assert_msg_test) | ||||
|  | ||||
|     def runAssertMessage(self, *args): | ||||
|         argv = [self.__assert_msg_test] | ||||
|         # shebang lines are not supported on native | ||||
|         # Windows consoles | ||||
|         if os.name == "nt": | ||||
|             argv.insert(0, sys.executable) | ||||
|         argv.extend(args) | ||||
|         print("Running:", argv) | ||||
|  | ||||
|         env = os.environ.copy() | ||||
|         env["LC_ALL"] = "C.UTF-8" | ||||
|         print("Environment:", env) | ||||
|  | ||||
|         # We want to ensure consistent line endings... | ||||
|         info = subprocess.run( | ||||
|             argv, | ||||
|             timeout=self.timeout_seconds, | ||||
|             stdout=subprocess.PIPE, | ||||
|             stderr=subprocess.PIPE, | ||||
|             env=env, | ||||
|             universal_newlines=True, | ||||
|         ) | ||||
|         out = info.stdout.strip() | ||||
|         err = info.stderr.strip() | ||||
|  | ||||
|         result = Result(info, out, err) | ||||
|  | ||||
|         print("Output:", result.out) | ||||
|         return result | ||||
|  | ||||
|     def runGdbAssertMessage(self, *args): | ||||
|         if self.__gdb is None: | ||||
|             return Result(None, "", "") | ||||
|  | ||||
|         argv = ["gdb", "--batch"] | ||||
|         argv.extend(args) | ||||
|         print("Running:", argv) | ||||
|  | ||||
|         env = os.environ.copy() | ||||
|         env["LC_ALL"] = "C.UTF-8" | ||||
|         print("Environment:", env) | ||||
|  | ||||
|         # We want to ensure consistent line endings... | ||||
|         info = subprocess.run( | ||||
|             argv, | ||||
|             timeout=self.timeout_seconds, | ||||
|             stdout=subprocess.PIPE, | ||||
|             stderr=subprocess.PIPE, | ||||
|             env=env, | ||||
|             universal_newlines=True, | ||||
|         ) | ||||
|         info.check_returncode() | ||||
|         out = info.stdout.strip() | ||||
|         err = info.stderr.strip() | ||||
|  | ||||
|         result = Result(info, out, err) | ||||
|  | ||||
|         print("Output:", result.out) | ||||
|         return result | ||||
|  | ||||
|     def test_gassert(self): | ||||
|         """Test running g_assert() and fail the program.""" | ||||
|         result = self.runAssertMessage() | ||||
|  | ||||
|         self.assertEqual(result.info.returncode, -6) | ||||
|         self.assertIn("assertion failed: (42 < 0)", result.out) | ||||
|  | ||||
|     def test_gdb_gassert(self): | ||||
|         """Test running g_assert() within gdb and fail the program.""" | ||||
|         if self.__gdb is None: | ||||
|             self.skipTest("GDB is not installed, skipping this test!") | ||||
|  | ||||
|         with tempfile.NamedTemporaryFile( | ||||
|             prefix="assert-msg-test-", suffix=".gdb", mode="w" | ||||
|         ) as tmp: | ||||
|             tmp.write(GDB_SCRIPT) | ||||
|             tmp.flush() | ||||
|  | ||||
|             result = self.runGdbAssertMessage("-x", tmp.name, self.__assert_msg_test) | ||||
|             self.assertEqual(result.info.returncode, 0) | ||||
|             self.assertIn("$1 = 0x", result.out) | ||||
|             self.assertIn("assertion failed: (42 < 0)", result.out) | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     unittest.main(testRunner=taptestrunner.TAPTestRunner()) | ||||
| @@ -282,6 +282,47 @@ if installed_tests_enabled | ||||
|   ) | ||||
| endif | ||||
|  | ||||
| python_tests = [ | ||||
|   'assert-msg-test.py', | ||||
| ] | ||||
|  | ||||
| executable('assert-msg-test', ['assert-msg-test.c'], | ||||
|   c_args : test_cargs, | ||||
|   dependencies :  test_deps, | ||||
|   install_dir : installed_tests_execdir, | ||||
|   install : installed_tests_enabled, | ||||
|   win_subsystem : extra_args.get('win_subsystem', 'console'), | ||||
| ) | ||||
|  | ||||
| foreach test_name : python_tests | ||||
|   test( | ||||
|     test_name, | ||||
|     python, | ||||
|     args: ['-B', files(test_name)], | ||||
|     env: test_env, | ||||
|     suite: ['glib', 'no-valgrind'], | ||||
|   ) | ||||
|  | ||||
|   if installed_tests_enabled | ||||
|     install_data( | ||||
|       files(test_name), | ||||
|       install_dir: installed_tests_execdir, | ||||
|       install_mode: 'rwxr-xr-x', | ||||
|     ) | ||||
|  | ||||
|     test_conf = configuration_data() | ||||
|     test_conf.set('installed_tests_dir', installed_tests_execdir) | ||||
|     test_conf.set('program', test_name) | ||||
|     test_conf.set('env', '') | ||||
|     configure_file( | ||||
|       input: installed_tests_template_tap, | ||||
|       output: test_name + '.test', | ||||
|       install_dir: installed_tests_metadir, | ||||
|       configuration: test_conf, | ||||
|     ) | ||||
|   endif | ||||
| endforeach | ||||
|  | ||||
| executable('spawn-path-search-helper', 'spawn-path-search-helper.c', | ||||
|   c_args : test_cargs, | ||||
|   dependencies : test_deps, | ||||
|   | ||||
							
								
								
									
										188
									
								
								glib/tests/taptestrunner.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								glib/tests/taptestrunner.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| #!/usr/bin/env python | ||||
| # coding=utf-8 | ||||
|  | ||||
| # Copyright (c) 2015 Remko Tronçon (https://el-tramo.be) | ||||
| # Copied from https://github.com/remko/pycotap/ | ||||
| # | ||||
| # SPDX-License-Identifier: MIT | ||||
| # | ||||
| # Released under the MIT license | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to deal | ||||
| # in the Software without restriction, including without limitation the rights | ||||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| # copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| # SOFTWARE. | ||||
|  | ||||
|  | ||||
| import unittest | ||||
| import sys | ||||
| import base64 | ||||
| from io import StringIO | ||||
|  | ||||
|  | ||||
| # Log modes | ||||
| class LogMode(object): | ||||
|     LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4) | ||||
|  | ||||
|  | ||||
| class TAPTestResult(unittest.TestResult): | ||||
|     def __init__(self, output_stream, error_stream, message_log, test_output_log): | ||||
|         super(TAPTestResult, self).__init__(self, output_stream) | ||||
|         self.output_stream = output_stream | ||||
|         self.error_stream = error_stream | ||||
|         self.orig_stdout = None | ||||
|         self.orig_stderr = None | ||||
|         self.message = None | ||||
|         self.test_output = None | ||||
|         self.message_log = message_log | ||||
|         self.test_output_log = test_output_log | ||||
|         self.output_stream.write("TAP version 13\n") | ||||
|         self._set_streams() | ||||
|  | ||||
|     def printErrors(self): | ||||
|         self.print_raw("1..%d\n" % self.testsRun) | ||||
|         self._reset_streams() | ||||
|  | ||||
|     def _set_streams(self): | ||||
|         self.orig_stdout = sys.stdout | ||||
|         self.orig_stderr = sys.stderr | ||||
|         if self.message_log == LogMode.LogToError: | ||||
|             self.message = self.error_stream | ||||
|         else: | ||||
|             self.message = StringIO() | ||||
|         if self.test_output_log == LogMode.LogToError: | ||||
|             self.test_output = self.error_stream | ||||
|         else: | ||||
|             self.test_output = StringIO() | ||||
|  | ||||
|         if self.message_log == self.test_output_log: | ||||
|             self.test_output = self.message | ||||
|         sys.stdout = sys.stderr = self.test_output | ||||
|  | ||||
|     def _reset_streams(self): | ||||
|         sys.stdout = self.orig_stdout | ||||
|         sys.stderr = self.orig_stderr | ||||
|  | ||||
|     def print_raw(self, text): | ||||
|         self.output_stream.write(text) | ||||
|         self.output_stream.flush() | ||||
|  | ||||
|     def print_result(self, result, test, directive=None): | ||||
|         self.output_stream.write("%s %d %s" % (result, self.testsRun, test.id())) | ||||
|         if directive: | ||||
|             self.output_stream.write(" # " + directive) | ||||
|         self.output_stream.write("\n") | ||||
|         self.output_stream.flush() | ||||
|  | ||||
|     def ok(self, test, directive=None): | ||||
|         self.print_result("ok", test, directive) | ||||
|  | ||||
|     def not_ok(self, test): | ||||
|         self.print_result("not ok", test) | ||||
|  | ||||
|     def startTest(self, test): | ||||
|         super(TAPTestResult, self).startTest(test) | ||||
|  | ||||
|     def stopTest(self, test): | ||||
|         super(TAPTestResult, self).stopTest(test) | ||||
|         if self.message_log == self.test_output_log: | ||||
|             logs = [(self.message_log, self.message, "output")] | ||||
|         else: | ||||
|             logs = [ | ||||
|                 (self.test_output_log, self.test_output, "test_output"), | ||||
|                 (self.message_log, self.message, "message"), | ||||
|             ] | ||||
|         for log_mode, log, log_name in logs: | ||||
|             if log_mode != LogMode.LogToError: | ||||
|                 output = log.getvalue() | ||||
|                 if len(output): | ||||
|                     if log_mode == LogMode.LogToYAML: | ||||
|                         self.print_raw("  ---\n") | ||||
|                         self.print_raw("    " + log_name + ": |\n") | ||||
|                         self.print_raw( | ||||
|                             "      " + output.rstrip().replace("\n", "\n      ") + "\n" | ||||
|                         ) | ||||
|                         self.print_raw("  ...\n") | ||||
|                     elif log_mode == LogMode.LogToAttachment: | ||||
|                         self.print_raw("  ---\n") | ||||
|                         self.print_raw("    " + log_name + ":\n") | ||||
|                         self.print_raw("      File-Name: " + log_name + ".txt\n") | ||||
|                         self.print_raw("      File-Type: text/plain\n") | ||||
|                         self.print_raw( | ||||
|                             "      File-Content: " + base64.b64encode(output) + "\n" | ||||
|                         ) | ||||
|                         self.print_raw("  ...\n") | ||||
|                     else: | ||||
|                         self.print_raw( | ||||
|                             "# " + output.rstrip().replace("\n", "\n# ") + "\n" | ||||
|                         ) | ||||
|                 # Truncate doesn't change the current stream position. | ||||
|                 # Seek to the beginning to avoid extensions on subsequent writes. | ||||
|                 log.seek(0) | ||||
|                 log.truncate(0) | ||||
|  | ||||
|     def addSuccess(self, test): | ||||
|         super(TAPTestResult, self).addSuccess(test) | ||||
|         self.ok(test) | ||||
|  | ||||
|     def addError(self, test, err): | ||||
|         super(TAPTestResult, self).addError(test, err) | ||||
|         self.message.write(self.errors[-1][1] + "\n") | ||||
|         self.not_ok(test) | ||||
|  | ||||
|     def addFailure(self, test, err): | ||||
|         super(TAPTestResult, self).addFailure(test, err) | ||||
|         self.message.write(self.failures[-1][1] + "\n") | ||||
|         self.not_ok(test) | ||||
|  | ||||
|     def addSkip(self, test, reason): | ||||
|         super(TAPTestResult, self).addSkip(test, reason) | ||||
|         self.ok(test, "SKIP " + reason) | ||||
|  | ||||
|     def addExpectedFailure(self, test, err): | ||||
|         super(TAPTestResult, self).addExpectedFailure(test, err) | ||||
|         self.ok(test) | ||||
|  | ||||
|     def addUnexpectedSuccess(self, test): | ||||
|         super(TAPTestResult, self).addUnexpectedSuccess(test) | ||||
|         self.message.write("Unexpected success" + "\n") | ||||
|         self.not_ok(test) | ||||
|  | ||||
|  | ||||
| class TAPTestRunner(object): | ||||
|     def __init__( | ||||
|         self, | ||||
|         message_log=LogMode.LogToYAML, | ||||
|         test_output_log=LogMode.LogToDiagnostics, | ||||
|         output_stream=sys.stdout, | ||||
|         error_stream=sys.stderr, | ||||
|     ): | ||||
|         self.output_stream = output_stream | ||||
|         self.error_stream = error_stream | ||||
|         self.message_log = message_log | ||||
|         self.test_output_log = test_output_log | ||||
|  | ||||
|     def run(self, test): | ||||
|         result = TAPTestResult( | ||||
|             self.output_stream, | ||||
|             self.error_stream, | ||||
|             self.message_log, | ||||
|             self.test_output_log, | ||||
|         ) | ||||
|         test(result) | ||||
|         result.printErrors() | ||||
|  | ||||
|         return result | ||||
| @@ -25,7 +25,6 @@ import os | ||||
| import shutil | ||||
| import subprocess | ||||
| import sys | ||||
| from textwrap import dedent | ||||
| import unittest | ||||
|  | ||||
| import taptestrunner | ||||
|   | ||||
| @@ -2346,9 +2346,6 @@ subdir('gthread') | ||||
| subdir('gmodule') | ||||
| subdir('gio') | ||||
| subdir('fuzzing') | ||||
| if build_tests | ||||
|   subdir('tests') | ||||
| endif | ||||
| subdir('tools') | ||||
|  | ||||
| # xgettext is optional (on Windows for instance) | ||||
|   | ||||
| @@ -1,5 +0,0 @@ | ||||
| run | ||||
| set print elements 0 | ||||
| # Work around https://sourceware.org/bugzilla/show_bug.cgi?id=22501 | ||||
| print *((char**) &__glib_assert_msg) | ||||
| quit | ||||
| @@ -1,29 +0,0 @@ | ||||
| # tests | ||||
|  | ||||
| test_env = environment() | ||||
| test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) | ||||
| test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) | ||||
| test_env.set('G_DEBUG', 'gc-friendly') | ||||
| test_env.set('MALLOC_CHECK_', '2') | ||||
|  | ||||
| test_cargs = ['-DG_LOG_DOMAIN="GLib"', '-UG_DISABLE_ASSERT'] | ||||
|  | ||||
| test_extra_programs = { | ||||
|   'assert-msg-test' : {}, | ||||
| } | ||||
|  | ||||
| common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS'] | ||||
| common_deps = [libm, thread_dep, libglib_dep] | ||||
|  | ||||
| foreach program_name, extra_args : test_extra_programs | ||||
|   source = extra_args.get('source', program_name + '.c') | ||||
|   extra_sources = extra_args.get('extra_sources', []) | ||||
|   install = installed_tests_enabled and extra_args.get('install', true) | ||||
|   executable(program_name, [source, extra_sources], | ||||
|       c_args : common_c_args, | ||||
|       dependencies : common_deps + extra_args.get('dependencies', []), | ||||
|       install_dir : installed_tests_execdir, | ||||
|       install : install, | ||||
|       win_subsystem : extra_args.get('win_subsystem', 'console'), | ||||
|   ) | ||||
| endforeach | ||||
| @@ -1,49 +0,0 @@ | ||||
| #! /bin/sh | ||||
|  | ||||
| fail () | ||||
| { | ||||
|   echo "Test failed: $*" | ||||
|   exit 1 | ||||
| } | ||||
|  | ||||
| echo_v () | ||||
| { | ||||
|   if [ "$verbose" = "1" ]; then | ||||
|     echo "$*" | ||||
|   fi | ||||
| } | ||||
|  | ||||
| error_out=/dev/null | ||||
| if [ "$1" = "-v" ]; then | ||||
|   verbose=1 | ||||
|   error_out=/dev/stderr | ||||
| fi   | ||||
|  | ||||
| if [ -z "$LIBTOOL" ]; then | ||||
|   if [ -f ../libtool ]; then | ||||
|     LIBTOOL=../libtool | ||||
|   else | ||||
|     LIBTOOL=libtool | ||||
|   fi | ||||
| fi | ||||
|  | ||||
| echo_v "Running assert-msg-test" | ||||
| OUT=$(./assert-msg-test 2>&1) && fail "assert-msg-test should abort" | ||||
| echo "$OUT" | grep -q '^GLib:ERROR:.*assert-msg-test.c:.*:.*main.*: assertion failed: (42 < 0)' || \ | ||||
|   fail "does not print assertion message" | ||||
|  | ||||
| if ! type gdb >/dev/null 2>&1; then | ||||
|   echo_v "Skipped (no gdb installed)" | ||||
|   exit 0 | ||||
| fi | ||||
|  | ||||
| echo_v "Running gdb on assert-msg-test" | ||||
| OUT=$($LIBTOOL --mode=execute gdb --batch -x "${srcdir:-.}/assert-msg-test.gdb" ./assert-msg-test 2> $error_out) || fail "failed to run gdb" | ||||
|  | ||||
| echo_v "Checking if assert message is in __glib_assert_msg" | ||||
| # shellcheck disable=SC2016 | ||||
| if ! echo "$OUT" | grep -q '^$1.*"GLib:ERROR:.*assert-msg-test.c:.*:.*main.*: assertion failed: (42 < 0)"'; then | ||||
|   fail "__glib_assert_msg does not have assertion message" | ||||
| fi | ||||
|  | ||||
| echo_v "All tests passed." | ||||
		Reference in New Issue
	
	Block a user