2 # -*- coding: utf-8 -*-
4 # Copyright © 2018 Endless Mobile, Inc.
6 # SPDX-License-Identifier: LGPL-2.1-or-later
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23 """Integration tests for glib-mkenums utility."""
37 Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
40 class TestMkenums(unittest.TestCase):
41 """Integration test for running glib-mkenums.
43 This can be run when installed or uninstalled. When uninstalled, it
44 requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set.
46 The idea with this test harness is to test the glib-mkenums utility, its
47 handling of command line arguments, its exit statuses, and its handling of
48 various C source codes. In future we could split the core glib-mkenums
49 parsing and generation code out into a library and unit test that, and
50 convert this test to just check command line behaviour.
53 # Track the cwd, we want to back out to that to clean up our tempdir
58 self.timeout_seconds = 10 # seconds per test
59 self.tmpdir = tempfile.TemporaryDirectory()
60 self.cwd = os.getcwd()
61 os.chdir(self.tmpdir.name)
62 print("tmpdir:", self.tmpdir.name)
63 if "G_TEST_BUILDDIR" in os.environ:
64 self.__mkenums = os.path.join(
65 os.environ["G_TEST_BUILDDIR"], "..", "glib-mkenums"
68 self.__mkenums = shutil.which("glib-mkenums")
69 print("rspfile: {}, mkenums:".format(self.rspfile), self.__mkenums)
75 def _write_rspfile(self, argv):
78 with tempfile.NamedTemporaryFile(
79 dir=self.tmpdir.name, mode="w", delete=False
81 contents = " ".join([shlex.quote(arg) for arg in argv])
82 print("Response file contains:", contents)
87 def runMkenums(self, *args):
89 rspfile = self._write_rspfile(args)
90 args = ["@" + rspfile]
91 argv = [self.__mkenums]
93 # shebang lines are not supported on native
96 argv.insert(0, sys.executable)
99 print("Running:", argv)
101 env = os.environ.copy()
102 env["LC_ALL"] = "C.UTF-8"
103 print("Environment:", env)
105 # We want to ensure consistent line endings...
106 info = subprocess.run(
108 timeout=self.timeout_seconds,
109 stdout=subprocess.PIPE,
110 stderr=subprocess.PIPE,
112 universal_newlines=True,
114 info.check_returncode()
115 out = info.stdout.strip()
116 err = info.stderr.strip()
118 # Known substitutions for standard boilerplate
120 "standard_top_comment": "This file is generated by glib-mkenums, do not modify "
121 "it. This code is licensed under the same license as the "
122 "containing project. Note that it links to GLib, so must "
123 "comply with the LGPL linking clauses.",
124 "standard_bottom_comment": "Generated data ends here",
127 result = Result(info, out, err, subs)
129 print("Output:", result.out)
132 def runMkenumsWithTemplate(self, template_contents, *args):
133 with tempfile.NamedTemporaryFile(
134 dir=self.tmpdir.name, suffix=".template", delete=False
136 # Write out the template.
137 template_file.write(template_contents.encode("utf-8"))
138 print(template_file.name + ":", template_contents)
139 template_file.flush()
141 return self.runMkenums("--template", template_file.name, *args)
143 def runMkenumsWithAllSubstitutions(self, *args):
144 """Run glib-mkenums with a template which outputs all substitutions."""
145 template_contents = """
146 /*** BEGIN file-header ***/
148 /*** END file-header ***/
150 /*** BEGIN file-production ***/
154 /*** END file-production ***/
156 /*** BEGIN enumeration-production ***/
157 enumeration-production
159 enum_name: @enum_name@
161 ENUMSHORT: @ENUMSHORT@
162 ENUMPREFIX: @ENUMPREFIX@
163 enumsince: @enumsince@
167 /*** END enumeration-production ***/
169 /*** BEGIN value-header ***/
172 enum_name: @enum_name@
174 ENUMSHORT: @ENUMSHORT@
175 ENUMPREFIX: @ENUMPREFIX@
176 enumsince: @enumsince@
180 /*** END value-header ***/
182 /*** BEGIN value-production ***/
184 VALUENAME: @VALUENAME@
185 valuenick: @valuenick@
190 /*** END value-production ***/
192 /*** BEGIN value-tail ***/
195 enum_name: @enum_name@
197 ENUMSHORT: @ENUMSHORT@
198 ENUMPREFIX: @ENUMPREFIX@
199 enumsince: @enumsince@
203 /*** END value-tail ***/
205 /*** BEGIN comment ***/
208 /*** END comment ***/
210 /*** BEGIN file-tail ***/
212 /*** END file-tail ***/
214 return self.runMkenumsWithTemplate(template_contents, *args)
216 def runMkenumsWithHeader(self, h_contents, encoding="utf-8"):
217 with tempfile.NamedTemporaryFile(
218 dir=self.tmpdir.name, suffix=".h", delete=False
220 # Write out the header to be scanned.
221 h_file.write(h_contents.encode(encoding))
222 print(h_file.name + ":", h_contents)
225 # Run glib-mkenums with a template which outputs all substitutions.
226 result = self.runMkenumsWithAllSubstitutions(h_file.name)
228 # Known substitutions for generated filenames.
230 {"filename": h_file.name, "basename": os.path.basename(h_file.name)}
235 def assertSingleEnum(
251 """Assert that out (from runMkenumsWithHeader()) contains a single
252 enum and value matching the given arguments."""
255 "enum_name_camel": enum_name_camel,
256 "enum_name_lower": enum_name_lower,
257 "enum_name_upper": enum_name_upper,
258 "enum_name_short": enum_name_short,
259 "enum_prefix": enum_prefix,
260 "enum_since": enum_since,
261 "type_lower": type_lower,
262 "type_camel": type_camel,
263 "type_upper": type_upper,
264 "value_name": value_name,
265 "value_nick": value_nick,
266 "value_num": value_num,
274 comment: {standard_top_comment}
281 enumeration-production
282 EnumName: {enum_name_camel}
283 enum_name: {enum_name_lower}
284 ENUMNAME: {enum_name_upper}
285 ENUMSHORT: {enum_name_short}
286 ENUMPREFIX: {enum_prefix}
287 enumsince: {enum_since}
292 EnumName: {enum_name_camel}
293 enum_name: {enum_name_lower}
294 ENUMNAME: {enum_name_upper}
295 ENUMSHORT: {enum_name_short}
296 ENUMPREFIX: {enum_prefix}
297 enumsince: {enum_since}
302 VALUENAME: {value_name}
303 valuenick: {value_nick}
304 valuenum: {value_num}
309 EnumName: {enum_name_camel}
310 enum_name: {enum_name_lower}
311 ENUMNAME: {enum_name_upper}
312 ENUMSHORT: {enum_name_short}
313 ENUMPREFIX: {enum_prefix}
314 enumsince: {enum_since}
321 comment: {standard_bottom_comment}
329 """Test the --help argument."""
330 result = self.runMkenums("--help")
331 self.assertIn("usage: glib-mkenums", result.out)
333 def test_no_args(self):
334 """Test running with no arguments at all."""
335 result = self.runMkenums()
336 self.assertEqual("", result.err)
338 """/* {standard_top_comment} */
341 /* {standard_bottom_comment} */""".format(
347 def test_empty_template(self):
348 """Test running with an empty template and no header files."""
349 result = self.runMkenumsWithTemplate("")
350 self.assertEqual("", result.err)
352 """/* {standard_top_comment} */
355 /* {standard_bottom_comment} */""".format(
361 def test_no_headers(self):
362 """Test running with a complete template, but no header files."""
363 result = self.runMkenumsWithAllSubstitutions()
364 self.assertEqual("", result.err)
368 comment: {standard_top_comment}
375 comment: {standard_bottom_comment}
382 def test_empty_header(self):
383 """Test an empty header."""
384 result = self.runMkenumsWithHeader("")
385 self.assertEqual("", result.err)
389 comment: {standard_top_comment}
396 comment: {standard_bottom_comment}
403 def test_enum_name(self):
404 """Test typedefs with an enum and a typedef name. Bug #794506."""
406 typedef enum _SomeEnumIdentifier {
408 } SomeEnumIdentifier;
410 result = self.runMkenumsWithHeader(h_contents)
411 self.assertEqual("", result.err)
412 self.assertSingleEnum(
414 "SomeEnumIdentifier",
415 "some_enum_identifier",
416 "SOME_ENUM_IDENTIFIER",
428 def test_non_utf8_encoding(self):
429 """Test source files with non-UTF-8 encoding. Bug #785113."""
431 /* Copyright © La Peña */
434 } SomeEnumIdentifier;
436 result = self.runMkenumsWithHeader(h_contents, encoding="iso-8859-1")
437 self.assertIn("WARNING: UnicodeWarning: ", result.err)
438 self.assertSingleEnum(
440 "SomeEnumIdentifier",
441 "some_enum_identifier",
442 "SOME_ENUM_IDENTIFIER",
454 def test_reproducible(self):
455 """Test builds are reproducible regardless of file ordering.
457 template_contents = "template"
471 with tempfile.NamedTemporaryFile(
472 dir=self.tmpdir.name, suffix="1.h", delete=False
473 ) as h_file1, tempfile.NamedTemporaryFile(
474 dir=self.tmpdir.name, suffix="2.h", delete=False
476 # Write out the headers.
477 h_file1.write(h_contents1.encode("utf-8"))
478 h_file2.write(h_contents2.encode("utf-8"))
483 # Run glib-mkenums with the headers in one order, and then again
485 result1 = self.runMkenumsWithTemplate(
486 template_contents, h_file1.name, h_file2.name
488 self.assertEqual("", result1.err)
490 result2 = self.runMkenumsWithTemplate(
491 template_contents, h_file2.name, h_file1.name
493 self.assertEqual("", result2.err)
495 # The output should be the same.
496 self.assertEqual(result1.out, result2.out)
498 def test_no_nick(self):
499 """Test trigraphs with a desc but no nick. Issue #1360."""
502 GEGL_SAMPLER_NEAREST = 0, /*< desc="nearest" >*/
505 result = self.runMkenumsWithHeader(h_contents)
506 self.assertEqual("", result.err)
507 self.assertSingleEnum(
518 "GEGL_SAMPLER_NEAREST",
523 def test_with_double_quotes(self):
524 """Test trigraphs with double-quoted expressions. Issue #65."""
527 FOO_VALUE /*< nick="eek, a comma" >*/
530 result = self.runMkenumsWithHeader(h_contents)
531 self.assertEqual("", result.err)
532 self.assertSingleEnum(
548 def test_filename_basename_in_fhead_ftail(self):
549 template_contents = """
550 /*** BEGIN file-header ***/
554 /*** END file-header ***/
556 /*** BEGIN comment ***/
559 /*** END comment ***/
561 /*** BEGIN file-tail ***/
565 /*** END file-tail ***/"""
566 result = self.runMkenumsWithTemplate(template_contents)
570 WARNING: @filename@ used in file-header section.
571 WARNING: @basename@ used in file-header section.
572 WARNING: @filename@ used in file-tail section.
573 WARNING: @basename@ used in file-tail section.
581 comment: {standard_top_comment}
592 comment: {standard_bottom_comment}
599 def test_since(self):
600 """Test user-provided 'since' version handling
601 https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1492"""
603 typedef enum { /*< since=1.0 >*/
604 QMI_WMS_MESSAGE_PROTOCOL_CDMA = 0,
605 } QmiWmsMessageProtocol;
607 result = self.runMkenumsWithHeader(h_contents)
608 self.assertEqual("", result.err)
609 self.assertSingleEnum(
611 "QmiWmsMessageProtocol",
612 "qmi_wms_message_protocol",
613 "QMI_WMS_MESSAGE_PROTOCOL",
614 "WMS_MESSAGE_PROTOCOL",
620 "QMI_WMS_MESSAGE_PROTOCOL_CDMA",
625 def test_enum_private_public(self):
626 """Test private/public enums. Bug #782162."""
644 result = self.runMkenumsWithHeader(h_contents1)
646 self.assertEqual("", result.err)
647 self.assertSingleEnum(
658 "ENUM_VALUE_PUBLIC1",
662 result = self.runMkenumsWithHeader(h_contents2)
663 self.assertEqual("", result.err)
664 self.assertSingleEnum(
675 "ENUM_VALUE_PUBLIC2",
680 def test_available_in(self):
681 """Test GLIB_AVAILABLE_ENUMERATOR_IN_2_68 handling
682 https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
685 G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_AVAILABLE_ENUMERATOR_IN_2_68 = (1<<2)
688 result = self.runMkenumsWithHeader(h_contents)
689 self.assertEqual("", result.err)
690 self.assertSingleEnum(
693 "g_dbus_server_flags",
694 "G_DBUS_SERVER_FLAGS",
701 "G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
706 def test_deprecated_in(self):
707 """Test GLIB_DEPRECATED_ENUMERATOR_IN_2_68 handling
708 https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
711 G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_DEPRECATED_ENUMERATOR_IN_2_68 = (1<<2)
714 result = self.runMkenumsWithHeader(h_contents)
715 self.assertEqual("", result.err)
716 self.assertSingleEnum(
719 "g_dbus_server_flags",
720 "G_DBUS_SERVER_FLAGS",
727 "G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
732 def test_deprecated_in_for(self):
733 """Test GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR() handling
734 https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
737 G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR(G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER2) = (1<<2)
740 result = self.runMkenumsWithHeader(h_contents)
741 self.assertEqual("", result.err)
742 self.assertSingleEnum(
745 "g_dbus_server_flags",
746 "G_DBUS_SERVER_FLAGS",
753 "G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
758 def test_enum_symbolic_expression(self):
759 """Test use of symbol in value expression."""
763 ENUM_VALUE_PRIVATE = 5,
765 ENUM_VALUE_PUBLIC = ENUM_VALUE_PRIVATE + 2,
769 result = self.runMkenumsWithHeader(h_contents)
770 self.assertEqual("", result.err)
771 self.assertSingleEnum(
774 "test_symbolic_enum",
775 "TEST_SYMBOLIC_ENUM",
788 class TestRspMkenums(TestMkenums):
789 """Run all tests again in @rspfile mode"""
794 if __name__ == "__main__":
795 unittest.main(testRunner=taptestrunner.TAPTestRunner())