mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 15:36:14 +01:00
Merge branch '1612-gdbus-codegen-tests' into 'master'
tests: Add tests for the gdbus-codegen executable See merge request GNOME/glib!1262
This commit is contained in:
commit
32c84b076d
@ -29,10 +29,11 @@ from . import dbustypes
|
||||
from .utils import print_error
|
||||
|
||||
LICENSE_STR = '''/*
|
||||
* Generated by gdbus-codegen {!s} from {!s}. DO NOT EDIT.
|
||||
* This file is generated by gdbus-codegen, do not modify it.
|
||||
*
|
||||
* The license of this code is the same as for the D-Bus interface description
|
||||
* it was derived from.
|
||||
* it was derived from. Note that it links to GLib, so must comply with the
|
||||
* LGPL linking clauses.
|
||||
*/\n'''
|
||||
|
||||
def generate_namespace(namespace):
|
||||
|
@ -149,7 +149,7 @@ def apply_annotations(iface_list, annotation_list):
|
||||
|
||||
def codegen_main():
|
||||
arg_parser = argparse.ArgumentParser(description='D-Bus code and documentation generator')
|
||||
arg_parser.add_argument('files', metavar='FILE', nargs='*',
|
||||
arg_parser.add_argument('files', metavar='FILE', nargs='+',
|
||||
help='D-Bus introspection XML file')
|
||||
arg_parser.add_argument('--xml-files', metavar='FILE', action='append', default=[],
|
||||
help=argparse.SUPPRESS)
|
||||
|
367
gio/tests/codegen.py
Normal file
367
gio/tests/codegen.py
Normal file
@ -0,0 +1,367 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright © 2018, 2019 Endless Mobile, Inc.
|
||||
#
|
||||
# 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 gdbus-codegen utility."""
|
||||
|
||||
import collections
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
import unittest
|
||||
|
||||
import taptestrunner
|
||||
|
||||
|
||||
Result = collections.namedtuple('Result', ('info', 'out', 'err', 'subs'))
|
||||
|
||||
|
||||
class TestCodegen(unittest.TestCase):
|
||||
"""Integration test for running gdbus-codegen.
|
||||
|
||||
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 the gdbus-codegen utility, its
|
||||
handling of command line arguments, its exit statuses, and its handling of
|
||||
various C source codes. In future we could split out tests for the core
|
||||
parsing and generation code of gdbus-codegen into separate unit tests, and
|
||||
just test command line behaviour in this integration test.
|
||||
"""
|
||||
# Track the cwd, we want to back out to that to clean up our tempdir
|
||||
cwd = ''
|
||||
|
||||
def setUp(self):
|
||||
self.timeout_seconds = 10 # seconds per test
|
||||
self.tmpdir = tempfile.TemporaryDirectory()
|
||||
self.cwd = os.getcwd()
|
||||
os.chdir(self.tmpdir.name)
|
||||
print('tmpdir:', self.tmpdir.name)
|
||||
if 'G_TEST_BUILDDIR' in os.environ:
|
||||
self.__codegen = \
|
||||
os.path.join(os.environ['G_TEST_BUILDDIR'], '..',
|
||||
'gdbus-2.0', 'codegen', 'gdbus-codegen')
|
||||
else:
|
||||
self.__codegen = shutil.which('gdbus-codegen')
|
||||
print('codegen:', self.__codegen)
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self.cwd)
|
||||
self.tmpdir.cleanup()
|
||||
|
||||
def runCodegen(self, *args):
|
||||
argv = [self.__codegen]
|
||||
|
||||
# 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)
|
||||
info.check_returncode()
|
||||
out = info.stdout.strip()
|
||||
err = info.stderr.strip()
|
||||
|
||||
# Known substitutions for standard boilerplate
|
||||
subs = {
|
||||
'standard_top_comment':
|
||||
'/*\n'
|
||||
' * This file is generated by gdbus-codegen, do not modify it.\n'
|
||||
' *\n'
|
||||
' * The license of this code is the same as for the D-Bus interface description\n'
|
||||
' * it was derived from. Note that it links to GLib, so must comply with the\n'
|
||||
' * LGPL linking clauses.\n'
|
||||
' */',
|
||||
'standard_config_h_include':
|
||||
'#ifdef HAVE_CONFIG_H\n'
|
||||
'# include "config.h"\n'
|
||||
'#endif',
|
||||
'standard_header_includes':
|
||||
'#include <string.h>\n'
|
||||
'#ifdef G_OS_UNIX\n'
|
||||
'# include <gio/gunixfdlist.h>\n'
|
||||
'#endif',
|
||||
'standard_typedefs_and_helpers':
|
||||
'typedef struct\n'
|
||||
'{\n'
|
||||
' GDBusArgInfo parent_struct;\n'
|
||||
' gboolean use_gvariant;\n'
|
||||
'} _ExtendedGDBusArgInfo;\n'
|
||||
'\n'
|
||||
'typedef struct\n'
|
||||
'{\n'
|
||||
' GDBusMethodInfo parent_struct;\n'
|
||||
' const gchar *signal_name;\n'
|
||||
' gboolean pass_fdlist;\n'
|
||||
'} _ExtendedGDBusMethodInfo;\n'
|
||||
'\n'
|
||||
'typedef struct\n'
|
||||
'{\n'
|
||||
' GDBusSignalInfo parent_struct;\n'
|
||||
' const gchar *signal_name;\n'
|
||||
'} _ExtendedGDBusSignalInfo;\n'
|
||||
'\n'
|
||||
'typedef struct\n'
|
||||
'{\n'
|
||||
' GDBusPropertyInfo parent_struct;\n'
|
||||
' const gchar *hyphen_name;\n'
|
||||
' guint use_gvariant : 1;\n'
|
||||
' guint emits_changed_signal : 1;\n'
|
||||
'} _ExtendedGDBusPropertyInfo;\n'
|
||||
'\n'
|
||||
'typedef struct\n'
|
||||
'{\n'
|
||||
' GDBusInterfaceInfo parent_struct;\n'
|
||||
' const gchar *hyphen_name;\n'
|
||||
'} _ExtendedGDBusInterfaceInfo;\n'
|
||||
'\n'
|
||||
'typedef struct\n'
|
||||
'{\n'
|
||||
' const _ExtendedGDBusPropertyInfo *info;\n'
|
||||
' guint prop_id;\n'
|
||||
' GValue orig_value; /* the value before the change */\n'
|
||||
'} ChangedProperty;\n'
|
||||
'\n'
|
||||
'static void\n'
|
||||
'_changed_property_free (ChangedProperty *data)\n'
|
||||
'{\n'
|
||||
' g_value_unset (&data->orig_value);\n'
|
||||
' g_free (data);\n'
|
||||
'}\n'
|
||||
'\n'
|
||||
'static gboolean\n'
|
||||
'_g_strv_equal0 (gchar **a, gchar **b)\n'
|
||||
'{\n'
|
||||
' gboolean ret = FALSE;\n'
|
||||
' guint n;\n'
|
||||
' if (a == NULL && b == NULL)\n'
|
||||
' {\n'
|
||||
' ret = TRUE;\n'
|
||||
' goto out;\n'
|
||||
' }\n'
|
||||
' if (a == NULL || b == NULL)\n'
|
||||
' goto out;\n'
|
||||
' if (g_strv_length (a) != g_strv_length (b))\n'
|
||||
' goto out;\n'
|
||||
' for (n = 0; a[n] != NULL; n++)\n'
|
||||
' if (g_strcmp0 (a[n], b[n]) != 0)\n'
|
||||
' goto out;\n'
|
||||
' ret = TRUE;\n'
|
||||
'out:\n'
|
||||
' return ret;\n'
|
||||
'}\n'
|
||||
'\n'
|
||||
'static gboolean\n'
|
||||
'_g_variant_equal0 (GVariant *a, GVariant *b)\n'
|
||||
'{\n'
|
||||
' gboolean ret = FALSE;\n'
|
||||
' if (a == NULL && b == NULL)\n'
|
||||
' {\n'
|
||||
' ret = TRUE;\n'
|
||||
' goto out;\n'
|
||||
' }\n'
|
||||
' if (a == NULL || b == NULL)\n'
|
||||
' goto out;\n'
|
||||
' ret = g_variant_equal (a, b);\n'
|
||||
'out:\n'
|
||||
' return ret;\n'
|
||||
'}\n'
|
||||
'\n'
|
||||
'G_GNUC_UNUSED static gboolean\n'
|
||||
'_g_value_equal (const GValue *a, const GValue *b)\n'
|
||||
'{\n'
|
||||
' gboolean ret = FALSE;\n'
|
||||
' g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b));\n'
|
||||
' switch (G_VALUE_TYPE (a))\n'
|
||||
' {\n'
|
||||
' case G_TYPE_BOOLEAN:\n'
|
||||
' ret = (g_value_get_boolean (a) == g_value_get_boolean (b));\n'
|
||||
' break;\n'
|
||||
' case G_TYPE_UCHAR:\n'
|
||||
' ret = (g_value_get_uchar (a) == g_value_get_uchar (b));\n'
|
||||
' break;\n'
|
||||
' case G_TYPE_INT:\n'
|
||||
' ret = (g_value_get_int (a) == g_value_get_int (b));\n'
|
||||
' break;\n'
|
||||
' case G_TYPE_UINT:\n'
|
||||
' ret = (g_value_get_uint (a) == g_value_get_uint (b));\n'
|
||||
' break;\n'
|
||||
' case G_TYPE_INT64:\n'
|
||||
' ret = (g_value_get_int64 (a) == g_value_get_int64 (b));\n'
|
||||
' break;\n'
|
||||
' case G_TYPE_UINT64:\n'
|
||||
' ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b));\n'
|
||||
' break;\n'
|
||||
' case G_TYPE_DOUBLE:\n'
|
||||
' {\n'
|
||||
' /* Avoid -Wfloat-equal warnings by doing a direct bit compare */\n'
|
||||
' gdouble da = g_value_get_double (a);\n'
|
||||
' gdouble db = g_value_get_double (b);\n'
|
||||
' ret = memcmp (&da, &db, sizeof (gdouble)) == 0;\n'
|
||||
' }\n'
|
||||
' break;\n'
|
||||
' case G_TYPE_STRING:\n'
|
||||
' ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0);\n'
|
||||
' break;\n'
|
||||
' case G_TYPE_VARIANT:\n'
|
||||
' ret = _g_variant_equal0 (g_value_get_variant (a), g_value_get_variant (b));\n'
|
||||
' break;\n'
|
||||
' default:\n'
|
||||
' if (G_VALUE_TYPE (a) == G_TYPE_STRV)\n'
|
||||
' ret = _g_strv_equal0 (g_value_get_boxed (a), g_value_get_boxed (b));\n'
|
||||
' else\n'
|
||||
' g_critical ("_g_value_equal() does not handle type %s", g_type_name (G_VALUE_TYPE (a)));\n'
|
||||
' break;\n'
|
||||
' }\n'
|
||||
' return ret;\n'
|
||||
'}',
|
||||
}
|
||||
|
||||
result = Result(info, out, err, subs)
|
||||
|
||||
print('Output:', result.out)
|
||||
return result
|
||||
|
||||
def runCodegenWithInterface(self, interface_contents, *args):
|
||||
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
|
||||
suffix='.xml',
|
||||
delete=False) as interface_file:
|
||||
# Write out the interface.
|
||||
interface_file.write(interface_contents.encode('utf-8'))
|
||||
print(interface_file.name + ':', interface_contents)
|
||||
interface_file.flush()
|
||||
|
||||
return self.runCodegen(interface_file.name, *args)
|
||||
|
||||
def test_help(self):
|
||||
"""Test the --help argument."""
|
||||
result = self.runCodegen('--help')
|
||||
self.assertIn('usage: gdbus-codegen', result.out)
|
||||
|
||||
def test_no_args(self):
|
||||
"""Test running with no arguments at all."""
|
||||
with self.assertRaises(subprocess.CalledProcessError):
|
||||
self.runCodegen()
|
||||
|
||||
def test_empty_interface_header(self):
|
||||
"""Test generating a header with an empty interface file."""
|
||||
result = self.runCodegenWithInterface('',
|
||||
'--output', '/dev/stdout',
|
||||
'--header')
|
||||
self.assertEqual('', result.err)
|
||||
self.assertEqual('''{standard_top_comment}
|
||||
|
||||
#ifndef __STDOUT__
|
||||
#define __STDOUT__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __STDOUT__ */'''.format(**result.subs),
|
||||
result.out.strip())
|
||||
|
||||
def test_empty_interface_body(self):
|
||||
"""Test generating a body with an empty interface file."""
|
||||
result = self.runCodegenWithInterface('',
|
||||
'--output', '/dev/stdout',
|
||||
'--body')
|
||||
self.assertEqual('', result.err)
|
||||
self.assertEqual('''{standard_top_comment}
|
||||
|
||||
{standard_config_h_include}
|
||||
|
||||
#include "stdout.h"
|
||||
|
||||
{standard_header_includes}
|
||||
|
||||
{standard_typedefs_and_helpers}'''.format(**result.subs),
|
||||
result.out.strip())
|
||||
|
||||
def test_reproducible(self):
|
||||
"""Test builds are reproducible regardless of file ordering."""
|
||||
xml_contents1 = '''
|
||||
<node>
|
||||
<interface name="com.acme.Coyote">
|
||||
<method name="Run"/>
|
||||
<method name="Sleep"/>
|
||||
<method name="Attack"/>
|
||||
<signal name="Surprised"/>
|
||||
<property name="Mood" type="s" access="read"/>
|
||||
</interface>
|
||||
</node>
|
||||
'''
|
||||
|
||||
xml_contents2 = '''
|
||||
<node>
|
||||
<interface name="org.project.Bar.Frobnicator">
|
||||
<method name="RandomMethod"/>
|
||||
</interface>
|
||||
</node>
|
||||
'''
|
||||
|
||||
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
|
||||
suffix='1.xml', delete=False) as xml_file1, \
|
||||
tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
|
||||
suffix='2.xml', delete=False) as xml_file2:
|
||||
# Write out the interfaces.
|
||||
xml_file1.write(xml_contents1.encode('utf-8'))
|
||||
xml_file2.write(xml_contents2.encode('utf-8'))
|
||||
|
||||
xml_file1.flush()
|
||||
xml_file2.flush()
|
||||
|
||||
# Repeat this for headers and bodies.
|
||||
for header_or_body in ['--header', '--body']:
|
||||
# Run gdbus-codegen with the interfaces in one order, and then
|
||||
# again in another order.
|
||||
result1 = self.runCodegen(xml_file1.name, xml_file2.name,
|
||||
'--output', '/dev/stdout',
|
||||
header_or_body)
|
||||
self.assertEqual('', result1.err)
|
||||
|
||||
result2 = self.runCodegen(xml_file2.name, xml_file1.name,
|
||||
'--output', '/dev/stdout',
|
||||
header_or_body)
|
||||
self.assertEqual('', result2.err)
|
||||
|
||||
# The output should be the same.
|
||||
self.assertEqual(result1.out, result2.out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(testRunner=taptestrunner.TAPTestRunner())
|
@ -87,6 +87,10 @@ test_extra_programs = {
|
||||
'gsubprocess-testprog' : {},
|
||||
}
|
||||
|
||||
python_tests = [
|
||||
'codegen.py',
|
||||
]
|
||||
|
||||
test_env = environment()
|
||||
test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
|
||||
test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
|
||||
@ -714,5 +718,42 @@ foreach program_name, extra_args : test_extra_programs
|
||||
)
|
||||
endforeach
|
||||
|
||||
foreach test_name : python_tests
|
||||
test(
|
||||
test_name,
|
||||
python,
|
||||
args: ['-B', files(test_name)],
|
||||
env: test_env,
|
||||
suite: ['gio', '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
|
||||
|
||||
# TAP test runner for Python tests
|
||||
if installed_tests_enabled
|
||||
install_data(
|
||||
files('taptestrunner.py'),
|
||||
install_dir: installed_tests_execdir,
|
||||
)
|
||||
endif
|
||||
|
||||
# FIXME: subdir('services')
|
||||
subdir('modules')
|
||||
|
176
gio/tests/taptestrunner.py
Normal file
176
gio/tests/taptestrunner.py
Normal file
@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright (c) 2015 Remko Tronçon (https://el-tramo.be)
|
||||
# Copied from https://github.com/remko/pycotap/
|
||||
#
|
||||
# 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
|
Loading…
Reference in New Issue
Block a user