[glib: 1/3] Convert tests/assert-msg-test* to glib/tests/assert-msg-test*




commit 207b8cb8a50d68e207d28b59e588311a5cbd9772
Author: Emmanuel Fleury <emmanuel fleury gmail com>
Date:   Tue Jun 21 18:49:25 2022 +0200

    Convert tests/assert-msg-test* to glib/tests/assert-msg-test*
    
    Closes issue #1434

 {tests => glib/tests}/assert-msg-test.c |   0
 glib/tests/assert-msg-test.py           | 155 ++++++++++++++++++++++++++
 glib/tests/meson.build                  |  41 +++++++
 glib/tests/taptestrunner.py             | 188 ++++++++++++++++++++++++++++++++
 meson.build                             |   3 -
 tests/assert-msg-test.gdb               |   5 -
 tests/meson.build                       |  29 -----
 tests/run-assert-msg-test.sh            |  49 ---------
 8 files changed, 384 insertions(+), 86 deletions(-)
---
diff --git a/tests/assert-msg-test.c b/glib/tests/assert-msg-test.c
similarity index 100%
rename from tests/assert-msg-test.c
rename to glib/tests/assert-msg-test.c
diff --git a/glib/tests/assert-msg-test.py b/glib/tests/assert-msg-test.py
new file mode 100755
index 0000000000..2d54a56192
--- /dev/null
+++ b/glib/tests/assert-msg-test.py
@@ -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())
diff --git a/glib/tests/meson.build b/glib/tests/meson.build
index d16a071e56..9b3b3bfa4e 100644
--- a/glib/tests/meson.build
+++ b/glib/tests/meson.build
@@ -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,
diff --git a/glib/tests/taptestrunner.py b/glib/tests/taptestrunner.py
new file mode 100644
index 0000000000..9adbd8daa4
--- /dev/null
+++ b/glib/tests/taptestrunner.py
@@ -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
diff --git a/meson.build b/meson.build
index e44bad35bd..5c85db9b2f 100644
--- a/meson.build
+++ b/meson.build
@@ -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)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]