glib/gobject/tests/genmarshal.py
Chun-wei Fan 75e3f92cd0 GObject: Fix mkenums.py and genmarshal.py tests on Windows
The two test scripts actually assumed some *NIX paradigms, so we need
to adapt them so that they can work on Windows as well, the changes are
namely:

-Call the glib-mkenums and glib-genmarshal Python scripts with the
 Python interpreter, not just relying on shebang lines, on Windows.
 This is because the native Windows console (cmd.exe) does not support
 shebang lines, for subprocess.run().

-Use NamedTemporaryFile with delete=False, otherwise Windows cannot find
 the temp files we need when running the tests.

-Use universal_newlines=True for subprocess.run() so that we do not need
 to worry out line ending differences on different systems.

-Make sure we are not in the temp directories we create, where the tests
 are being run, upon cleanup.  Windows does not like deleting
 directories that we are currently in.
2019-06-25 00:04:48 +08:00

615 lines
25 KiB
Python

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Copyright © 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 glib-genmarshal utility."""
import collections
import os
import shutil
import subprocess
import sys
import tempfile
from textwrap import dedent
import unittest
import taptestrunner
Result = collections.namedtuple('Result', ('info', 'out', 'err', 'subs'))
class TestGenmarshal(unittest.TestCase):
"""Integration test for running glib-genmarshal.
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 glib-genmarshal utility, its
handling of command line arguments, its exit statuses, and its handling of
various marshaller lists. In future we could split the core glib-genmarshal
parsing and generation code out into a library and unit test that, and
convert this test to just check command line behaviour.
"""
# 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.__genmarshal = \
os.path.join(os.environ['G_TEST_BUILDDIR'], '..',
'glib-genmarshal')
else:
self.__genmarshal = shutil.which('glib-genmarshal')
print('genmarshal:', self.__genmarshal)
def tearDown(self):
os.chdir(self.cwd)
self.tmpdir.cleanup()
def runGenmarshal(self, *args):
argv = [self.__genmarshal]
# 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':
'This file is generated by glib-genmarshal, do not modify '
'it. This code is licensed under the same license as the '
'containing project. Note that it links to GLib, so must '
'comply with the LGPL linking clauses.',
'standard_top_pragma': dedent(
'''
#ifndef __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
#define __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
''').strip(),
'standard_bottom_pragma': dedent(
'''
#endif /* __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__ */
''').strip(),
'standard_includes': dedent(
'''
#include <glib-object.h>
''').strip(),
'standard_marshal_peek_defines': dedent(
'''
#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
#define g_marshal_value_peek_char(v) g_value_get_schar (v)
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
#define g_marshal_value_peek_int(v) g_value_get_int (v)
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
#define g_marshal_value_peek_long(v) g_value_get_long (v)
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
#define g_marshal_value_peek_float(v) g_value_get_float (v)
#define g_marshal_value_peek_double(v) g_value_get_double (v)
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v) g_value_get_param (v)
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
#define g_marshal_value_peek_object(v) g_value_get_object (v)
#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
* Do not access GValues directly in your code. Instead, use the
* g_value_get_*() functions
*/
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */
''').strip(),
}
result = Result(info, out, err, subs)
print('Output:', result.out)
return result
def runGenmarshalWithList(self, list_contents, *args):
with tempfile.NamedTemporaryFile(dir=self.tmpdir.name,
suffix='.list',
delete=False) as list_file:
# Write out the list.
list_file.write(list_contents.encode('utf-8'))
print(list_file.name + ':', list_contents)
list_file.flush()
header_result = self.runGenmarshal(list_file.name,
'--header', *args)
body_result = self.runGenmarshal(list_file.name,
'--body', *args)
header_result.subs['list_path'] = list_file.name
body_result.subs['list_path'] = list_file.name
return (header_result, body_result)
def test_help(self):
"""Test the --help argument."""
result = self.runGenmarshal('--help')
self.assertIn('usage: glib-genmarshal', result.out)
def test_no_args(self):
"""Test running with no arguments at all."""
result = self.runGenmarshal()
self.assertEqual('', result.err)
self.assertEqual('', result.out)
def test_empty_list(self):
"""Test running with an empty list."""
(header_result, body_result) = \
self.runGenmarshalWithList('', '--quiet')
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_top_pragma}
{standard_includes}
G_BEGIN_DECLS
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
''').strip().format(**body_result.subs),
body_result.out.strip())
def test_void_boolean(self):
"""Test running with a basic VOID:BOOLEAN list."""
(header_result, body_result) = \
self.runGenmarshalWithList('VOID:BOOLEAN', '--quiet')
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_top_pragma}
{standard_includes}
G_BEGIN_DECLS
/* VOID:BOOLEAN ({list_path}:1) */
#define g_cclosure_user_marshal_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
''').strip().format(**body_result.subs),
body_result.out.strip())
def test_void_boolean_int64(self):
"""Test running with a non-trivial VOID:BOOLEAN,INT64 list."""
(header_result, body_result) = \
self.runGenmarshalWithList('VOID:BOOLEAN,INT64', '--quiet')
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_top_pragma}
{standard_includes}
G_BEGIN_DECLS
/* VOID:BOOLEAN,INT64 ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
/* VOID:BOOLEAN,INT64 ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__BOOLEAN_INT64) (gpointer data1,
gboolean arg1,
gint64 arg2,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__BOOLEAN_INT64 callback;
g_return_if_fail (n_param_values == 3);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__BOOLEAN_INT64) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_boolean (param_values + 1),
g_marshal_value_peek_int64 (param_values + 2),
data2);
}}
''').strip().format(**body_result.subs),
body_result.out.strip())
def test_void_variant_nostdinc_valist_marshaller(self):
"""Test running with a basic VOID:VARIANT list, but without the
standard marshallers, and with valist support enabled. This checks that
the valist marshaller for VARIANT correctly sinks floating variants.
See issue #1793.
"""
(header_result, body_result) = \
self.runGenmarshalWithList('VOID:VARIANT', '--quiet', '--nostdinc',
'--valist-marshaller')
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_top_pragma}
G_BEGIN_DECLS
/* VOID:VARIANT ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__VARIANT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
extern
void g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_marshal_peek_defines}
/* VOID:VARIANT ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__VARIANT (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__VARIANT callback;
g_return_if_fail (n_param_values == 2);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_variant (param_values + 1),
data2);
}}
void
g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{{
typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__VARIANT callback;
gpointer arg0;
va_list args_copy;
G_VA_COPY (args_copy, args);
arg0 = (gpointer) va_arg (args_copy, gpointer);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
arg0 = g_variant_ref_sink (arg0);
va_end (args_copy);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = instance;
}}
else
{{
data1 = instance;
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
callback (data1,
arg0,
data2);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_variant_unref (arg0);
}}
''').strip().format(**body_result.subs),
body_result.out.strip())
def test_void_string_nostdinc(self):
"""Test running with a basic VOID:STRING list, but without the
standard marshallers, and with valist support enabled. This checks that
the valist marshaller for STRING correctly skips a string copy if the
argument is static.
See issue #1792.
"""
(header_result, body_result) = \
self.runGenmarshalWithList('VOID:STRING', '--quiet', '--nostdinc',
'--valist-marshaller')
self.assertEqual('', header_result.err)
self.assertEqual('', body_result.err)
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_top_pragma}
G_BEGIN_DECLS
/* VOID:STRING ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__STRING (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
extern
void g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
{standard_bottom_pragma}
''').strip().format(**header_result.subs),
header_result.out.strip())
self.assertEqual(dedent(
'''
/* {standard_top_comment} */
{standard_marshal_peek_defines}
/* VOID:STRING ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__STRING (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__STRING callback;
g_return_if_fail (n_param_values == 2);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_string (param_values + 1),
data2);
}}
void
g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{{
typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__STRING callback;
gpointer arg0;
va_list args_copy;
G_VA_COPY (args_copy, args);
arg0 = (gpointer) va_arg (args_copy, gpointer);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
arg0 = g_strdup (arg0);
va_end (args_copy);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = instance;
}}
else
{{
data1 = instance;
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
callback (data1,
arg0,
data2);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_free (arg0);
}}
''').strip().format(**body_result.subs),
body_result.out.strip())
if __name__ == '__main__':
unittest.main(testRunner=taptestrunner.TAPTestRunner())