2 # -*- coding: utf-8 -*-
4 # Copyright © 2018 Endless Mobile, Inc.
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 """Integration tests for glib-mkenums utility."""
35 Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
38 class TestMkenums(unittest.TestCase):
39 """Integration test for running glib-mkenums.
41 This can be run when installed or uninstalled. When uninstalled, it
42 requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set.
44 The idea with this test harness is to test the glib-mkenums utility, its
45 handling of command line arguments, its exit statuses, and its handling of
46 various C source codes. In future we could split the core glib-mkenums
47 parsing and generation code out into a library and unit test that, and
48 convert this test to just check command line behaviour.
51 # Track the cwd, we want to back out to that to clean up our tempdir
56 self.timeout_seconds = 10 # seconds per test
57 self.tmpdir = tempfile.TemporaryDirectory()
58 self.cwd = os.getcwd()
59 os.chdir(self.tmpdir.name)
60 print("tmpdir:", self.tmpdir.name)
61 if "G_TEST_BUILDDIR" in os.environ:
62 self.__mkenums = os.path.join(
63 os.environ["G_TEST_BUILDDIR"], "..", "glib-mkenums"
66 self.__mkenums = shutil.which("glib-mkenums")
67 print("rspfile: {}, mkenums:".format(self.rspfile), self.__mkenums)
73 def _write_rspfile(self, argv):
76 with tempfile.NamedTemporaryFile(
77 dir=self.tmpdir.name, mode="w", delete=False
79 contents = " ".join([shlex.quote(arg) for arg in argv])
80 print("Response file contains:", contents)
85 def runMkenums(self, *args):
87 rspfile = self._write_rspfile(args)
88 args = ["@" + rspfile]
89 argv = [self.__mkenums]
91 # shebang lines are not supported on native
94 argv.insert(0, sys.executable)
97 print("Running:", argv)
99 env = os.environ.copy()
100 env["LC_ALL"] = "C.UTF-8"
101 print("Environment:", env)
103 # We want to ensure consistent line endings...
104 info = subprocess.run(
106 timeout=self.timeout_seconds,
107 stdout=subprocess.PIPE,
108 stderr=subprocess.PIPE,
110 universal_newlines=True,
112 info.check_returncode()
113 out = info.stdout.strip()
114 err = info.stderr.strip()
116 # Known substitutions for standard boilerplate
118 "standard_top_comment": "This file is generated by glib-mkenums, do not modify "
119 "it. This code is licensed under the same license as the "
120 "containing project. Note that it links to GLib, so must "
121 "comply with the LGPL linking clauses.",
122 "standard_bottom_comment": "Generated data ends here",
125 result = Result(info, out, err, subs)
127 print("Output:", result.out)
130 def runMkenumsWithTemplate(self, template_contents, *args):
131 with tempfile.NamedTemporaryFile(
132 dir=self.tmpdir.name, suffix=".template", delete=False
134 # Write out the template.
135 template_file.write(template_contents.encode("utf-8"))
136 print(template_file.name + ":", template_contents)
137 template_file.flush()
139 return self.runMkenums("--template", template_file.name, *args)
141 def runMkenumsWithAllSubstitutions(self, *args):
142 """Run glib-mkenums with a template which outputs all substitutions."""
143 template_contents = """
144 /*** BEGIN file-header ***/
146 /*** END file-header ***/
148 /*** BEGIN file-production ***/
152 /*** END file-production ***/
154 /*** BEGIN enumeration-production ***/
155 enumeration-production
157 enum_name: @enum_name@
159 ENUMSHORT: @ENUMSHORT@
160 ENUMPREFIX: @ENUMPREFIX@
161 enumsince: @enumsince@
165 /*** END enumeration-production ***/
167 /*** BEGIN value-header ***/
170 enum_name: @enum_name@
172 ENUMSHORT: @ENUMSHORT@
173 ENUMPREFIX: @ENUMPREFIX@
174 enumsince: @enumsince@
178 /*** END value-header ***/
180 /*** BEGIN value-production ***/
182 VALUENAME: @VALUENAME@
183 valuenick: @valuenick@
188 /*** END value-production ***/
190 /*** BEGIN value-tail ***/
193 enum_name: @enum_name@
195 ENUMSHORT: @ENUMSHORT@
196 ENUMPREFIX: @ENUMPREFIX@
197 enumsince: @enumsince@
201 /*** END value-tail ***/
203 /*** BEGIN comment ***/
206 /*** END comment ***/
208 /*** BEGIN file-tail ***/
210 /*** END file-tail ***/
212 return self.runMkenumsWithTemplate(template_contents, *args)
214 def runMkenumsWithHeader(self, h_contents, encoding="utf-8"):
215 with tempfile.NamedTemporaryFile(
216 dir=self.tmpdir.name, suffix=".h", delete=False
218 # Write out the header to be scanned.
219 h_file.write(h_contents.encode(encoding))
220 print(h_file.name + ":", h_contents)
223 # Run glib-mkenums with a template which outputs all substitutions.
224 result = self.runMkenumsWithAllSubstitutions(h_file.name)
226 # Known substitutions for generated filenames.
228 {"filename": h_file.name, "basename": os.path.basename(h_file.name)}
233 def assertSingleEnum(
249 """Assert that out (from runMkenumsWithHeader()) contains a single
250 enum and value matching the given arguments."""
253 "enum_name_camel": enum_name_camel,
254 "enum_name_lower": enum_name_lower,
255 "enum_name_upper": enum_name_upper,
256 "enum_name_short": enum_name_short,
257 "enum_prefix": enum_prefix,
258 "enum_since": enum_since,
259 "type_lower": type_lower,
260 "type_camel": type_camel,
261 "type_upper": type_upper,
262 "value_name": value_name,
263 "value_nick": value_nick,
264 "value_num": value_num,
272 comment: {standard_top_comment}
279 enumeration-production
280 EnumName: {enum_name_camel}
281 enum_name: {enum_name_lower}
282 ENUMNAME: {enum_name_upper}
283 ENUMSHORT: {enum_name_short}
284 ENUMPREFIX: {enum_prefix}
285 enumsince: {enum_since}
290 EnumName: {enum_name_camel}
291 enum_name: {enum_name_lower}
292 ENUMNAME: {enum_name_upper}
293 ENUMSHORT: {enum_name_short}
294 ENUMPREFIX: {enum_prefix}
295 enumsince: {enum_since}
300 VALUENAME: {value_name}
301 valuenick: {value_nick}
302 valuenum: {value_num}
307 EnumName: {enum_name_camel}
308 enum_name: {enum_name_lower}
309 ENUMNAME: {enum_name_upper}
310 ENUMSHORT: {enum_name_short}
311 ENUMPREFIX: {enum_prefix}
312 enumsince: {enum_since}
319 comment: {standard_bottom_comment}
327 """Test the --help argument."""
328 result = self.runMkenums("--help")
329 self.assertIn("usage: glib-mkenums", result.out)
331 def test_no_args(self):
332 """Test running with no arguments at all."""
333 result = self.runMkenums()
334 self.assertEqual("", result.err)
336 """/* {standard_top_comment} */
339 /* {standard_bottom_comment} */""".format(
345 def test_empty_template(self):
346 """Test running with an empty template and no header files."""
347 result = self.runMkenumsWithTemplate("")
348 self.assertEqual("", result.err)
350 """/* {standard_top_comment} */
353 /* {standard_bottom_comment} */""".format(
359 def test_no_headers(self):
360 """Test running with a complete template, but no header files."""
361 result = self.runMkenumsWithAllSubstitutions()
362 self.assertEqual("", result.err)
366 comment: {standard_top_comment}
373 comment: {standard_bottom_comment}
380 def test_empty_header(self):
381 """Test an empty header."""
382 result = self.runMkenumsWithHeader("")
383 self.assertEqual("", result.err)
387 comment: {standard_top_comment}
394 comment: {standard_bottom_comment}
401 def test_enum_name(self):
402 """Test typedefs with an enum and a typedef name. Bug #794506."""
404 typedef enum _SomeEnumIdentifier {
406 } SomeEnumIdentifier;
408 result = self.runMkenumsWithHeader(h_contents)
409 self.assertEqual("", result.err)
410 self.assertSingleEnum(
412 "SomeEnumIdentifier",
413 "some_enum_identifier",
414 "SOME_ENUM_IDENTIFIER",
426 def test_non_utf8_encoding(self):
427 """Test source files with non-UTF-8 encoding. Bug #785113."""
429 /* Copyright © La Peña */
432 } SomeEnumIdentifier;
434 result = self.runMkenumsWithHeader(h_contents, encoding="iso-8859-1")
435 self.assertIn("WARNING: UnicodeWarning: ", result.err)
436 self.assertSingleEnum(
438 "SomeEnumIdentifier",
439 "some_enum_identifier",
440 "SOME_ENUM_IDENTIFIER",
452 def test_reproducible(self):
453 """Test builds are reproducible regardless of file ordering.
455 template_contents = "template"
469 with tempfile.NamedTemporaryFile(
470 dir=self.tmpdir.name, suffix="1.h", delete=False
471 ) as h_file1, tempfile.NamedTemporaryFile(
472 dir=self.tmpdir.name, suffix="2.h", delete=False
474 # Write out the headers.
475 h_file1.write(h_contents1.encode("utf-8"))
476 h_file2.write(h_contents2.encode("utf-8"))
481 # Run glib-mkenums with the headers in one order, and then again
483 result1 = self.runMkenumsWithTemplate(
484 template_contents, h_file1.name, h_file2.name
486 self.assertEqual("", result1.err)
488 result2 = self.runMkenumsWithTemplate(
489 template_contents, h_file2.name, h_file1.name
491 self.assertEqual("", result2.err)
493 # The output should be the same.
494 self.assertEqual(result1.out, result2.out)
496 def test_no_nick(self):
497 """Test trigraphs with a desc but no nick. Issue #1360."""
500 GEGL_SAMPLER_NEAREST = 0, /*< desc="nearest" >*/
503 result = self.runMkenumsWithHeader(h_contents)
504 self.assertEqual("", result.err)
505 self.assertSingleEnum(
516 "GEGL_SAMPLER_NEAREST",
521 def test_filename_basename_in_fhead_ftail(self):
522 template_contents = """
523 /*** BEGIN file-header ***/
527 /*** END file-header ***/
529 /*** BEGIN comment ***/
532 /*** END comment ***/
534 /*** BEGIN file-tail ***/
538 /*** END file-tail ***/"""
539 result = self.runMkenumsWithTemplate(template_contents)
543 WARNING: @filename@ used in file-header section.
544 WARNING: @basename@ used in file-header section.
545 WARNING: @filename@ used in file-tail section.
546 WARNING: @basename@ used in file-tail section.
554 comment: {standard_top_comment}
565 comment: {standard_bottom_comment}
572 def test_since(self):
573 """Test user-provided 'since' version handling
574 https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1492"""
576 typedef enum { /*< since=1.0 >*/
577 QMI_WMS_MESSAGE_PROTOCOL_CDMA = 0,
578 } QmiWmsMessageProtocol;
580 result = self.runMkenumsWithHeader(h_contents)
581 self.assertEqual("", result.err)
582 self.assertSingleEnum(
584 "QmiWmsMessageProtocol",
585 "qmi_wms_message_protocol",
586 "QMI_WMS_MESSAGE_PROTOCOL",
587 "WMS_MESSAGE_PROTOCOL",
593 "QMI_WMS_MESSAGE_PROTOCOL_CDMA",
598 def test_enum_private_public(self):
599 """Test private/public enums. Bug #782162."""
617 result = self.runMkenumsWithHeader(h_contents1)
619 self.assertEqual("", result.err)
620 self.assertSingleEnum(
631 "ENUM_VALUE_PUBLIC1",
635 result = self.runMkenumsWithHeader(h_contents2)
636 self.assertEqual("", result.err)
637 self.assertSingleEnum(
648 "ENUM_VALUE_PUBLIC2",
653 def test_available_in(self):
654 """Test GLIB_AVAILABLE_ENUMERATOR_IN_2_68 handling
655 https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
658 G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_AVAILABLE_ENUMERATOR_IN_2_68 = (1<<2)
661 result = self.runMkenumsWithHeader(h_contents)
662 self.assertEqual("", result.err)
663 self.assertSingleEnum(
666 "g_dbus_server_flags",
667 "G_DBUS_SERVER_FLAGS",
674 "G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
679 def test_deprecated_in(self):
680 """Test GLIB_DEPRECATED_ENUMERATOR_IN_2_68 handling
681 https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
684 G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_DEPRECATED_ENUMERATOR_IN_2_68 = (1<<2)
687 result = self.runMkenumsWithHeader(h_contents)
688 self.assertEqual("", result.err)
689 self.assertSingleEnum(
692 "g_dbus_server_flags",
693 "G_DBUS_SERVER_FLAGS",
700 "G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
705 def test_deprecated_in_for(self):
706 """Test GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR() handling
707 https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
710 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)
713 result = self.runMkenumsWithHeader(h_contents)
714 self.assertEqual("", result.err)
715 self.assertSingleEnum(
718 "g_dbus_server_flags",
719 "G_DBUS_SERVER_FLAGS",
726 "G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
732 class TestRspMkenums(TestMkenums):
733 """Run all tests again in @rspfile mode"""
738 if __name__ == "__main__":
739 unittest.main(testRunner=taptestrunner.TAPTestRunner())