1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 from tempfile
import mkdtemp
10 from mozunit
import MockedOpen
, main
11 from six
import StringIO
13 from mozbuild
.preprocessor
import Preprocessor
16 class TestPreprocessor(unittest
.TestCase
):
18 Unit tests for the Context class
22 self
.pp
= Preprocessor()
23 self
.pp
.out
= StringIO()
25 def do_include_compare(self
, content_lines
, expected_lines
):
26 content
= "%s" % "\n".join(content_lines
)
27 expected
= "%s".rstrip() % "\n".join(expected_lines
)
29 with
MockedOpen({"dummy": content
}):
30 self
.pp
.do_include("dummy")
31 self
.assertEqual(self
.pp
.out
.getvalue().rstrip("\n"), expected
)
33 def do_include_pass(self
, content_lines
):
34 self
.do_include_compare(content_lines
, ["PASS"])
36 def test_conditional_if_0(self
):
47 def test_no_marker(self
):
53 self
.pp
.setMarker(None)
54 self
.do_include_compare(lines
, lines
)
56 def test_string_value(self
):
57 self
.do_include_compare(
61 "string value is true",
63 "string value is false",
66 ["string value is false"],
69 def test_number_value(self
):
70 self
.do_include_compare(
74 "number value is true",
76 "number value is false",
79 ["number value is true"],
82 def test_conditional_if_0_elif_1(self
):
94 def test_conditional_if_1(self
):
105 def test_conditional_if_0_or_1(self
):
106 self
.do_include_pass(
116 def test_conditional_if_1_elif_1_else(self
):
117 self
.do_include_pass(
129 def test_conditional_if_1_if_1(self
):
130 self
.do_include_pass(
144 def test_conditional_not_0(self
):
145 self
.do_include_pass(
155 def test_conditional_not_0_and_1(self
):
156 self
.do_include_pass(
166 def test_conditional_not_1(self
):
167 self
.do_include_pass(
177 def test_conditional_not_emptyval(self
):
178 self
.do_include_compare(
195 def test_conditional_not_nullval(self
):
196 self
.do_include_pass(
207 def test_indentation(self
):
208 self
.do_include_pass(
210 " #define NULLVAL 0",
219 def test_expand(self
):
220 self
.do_include_pass(
223 "#expand P__ASVAR__S",
227 def test_undef_defined(self
):
228 self
.do_include_compare(
237 def test_undef_undefined(self
):
238 self
.do_include_compare(
245 def test_filter_attemptSubstitution(self
):
246 self
.do_include_compare(
248 "#filter attemptSubstitution",
250 "#unfilter attemptSubstitution",
255 def test_filter_emptyLines(self
):
256 self
.do_include_compare(
261 "#filter emptyLines",
265 "#unfilter emptyLines",
266 "yet more lines with",
276 "yet more lines with",
282 def test_filter_dumbComments(self
):
283 self
.do_include_compare(
285 "#filter dumbComments",
286 "PASS//PASS // PASS",
293 "#unfilter dumbComments",
297 "PASS//PASS // PASS",
308 def test_filter_dumbComments_and_emptyLines(self
):
309 self
.do_include_compare(
311 "#filter dumbComments emptyLines",
312 "PASS//PASS // PASS",
319 "#unfilter dumbComments emptyLines",
324 "PASS//PASS // PASS",
332 def test_filter_substitution(self
):
333 self
.do_include_pass(
336 "#filter substitution",
338 "#unfilter substitution",
342 def test_error(self
):
343 with
MockedOpen({"f": "#error spit this message out\n"}):
344 with self
.assertRaises(Preprocessor
.Error
) as e
:
345 self
.pp
.do_include("f")
346 self
.assertEqual(e
.args
[0][-1], "spit this message out")
348 def test_ambigous_command(self
):
349 comment
= "# if I tell you a joke\n"
350 with
MockedOpen({"f": comment
}):
351 with self
.assertRaises(Preprocessor
.Error
) as e
:
352 self
.pp
.do_include("f")
353 the_exception
= e
.exception
354 self
.assertEqual(the_exception
.args
[0][-1], comment
)
356 def test_javascript_line(self
):
357 # The preprocessor is reading the filename from somewhere not caught
361 full
= os
.path
.join(tmpdir
, "javascript_line.js.in")
362 with
open(full
, "w") as fh
:
378 "// line 13, given line number overwritten with 2",
384 self
.pp
.do_include(full
)
388 '//@line 5 "CWDjavascript_line.js.in"',
390 '//@line 7 "CWDjavascript_line.js.in"',
394 '//@line 11 "CWDjavascript_line.js.in"',
396 '//@line 2 "CWDjavascript_line.js.in"',
397 "// line 13, given line number overwritten with 2",
401 out
= out
.replace("CWD", tmpdir
+ os
.path
.sep
)
402 self
.assertEqual(self
.pp
.out
.getvalue(), out
)
404 shutil
.rmtree(tmpdir
)
406 def test_literal(self
):
407 self
.do_include_pass(
413 def test_var_directory(self
):
414 self
.do_include_pass(
424 def test_var_file(self
):
425 self
.do_include_pass(
435 def test_var_if_0(self
):
436 self
.do_include_pass(
447 def test_var_if_0_elifdef(self
):
448 self
.do_include_pass(
459 def test_var_if_0_elifndef(self
):
460 self
.do_include_pass(
471 def test_var_ifdef_0(self
):
472 self
.do_include_pass(
483 def test_var_ifdef_1_or_undef(self
):
484 self
.do_include_pass(
487 "#if defined(FOO) || defined(BAR)",
495 def test_var_ifdef_undef(self
):
496 self
.do_include_pass(
508 def test_var_ifndef_0(self
):
509 self
.do_include_pass(
520 def test_var_ifndef_0_and_undef(self
):
521 self
.do_include_pass(
524 "#if !defined(FOO) && !defined(BAR)",
532 def test_var_ifndef_undef(self
):
533 self
.do_include_pass(
545 def test_var_line(self
):
546 self
.do_include_pass(
556 def test_filterDefine(self
):
557 self
.do_include_pass(
559 "#filter substitution",
561 "#define VAR2 P@VAR@",
566 def test_number_value_equals(self
):
567 self
.do_include_pass(
578 def test_default_defines(self
):
579 self
.pp
.handleCommandLine(["-DFOO"])
580 self
.do_include_pass(
589 def test_number_value_equals_defines(self
):
590 self
.pp
.handleCommandLine(["-DFOO=1000"])
591 self
.do_include_pass(
600 def test_octal_value_equals(self
):
601 self
.do_include_pass(
612 def test_octal_value_equals_defines(self
):
613 self
.pp
.handleCommandLine(["-DFOO=0100"])
614 self
.do_include_pass(
624 def test_value_quoted_expansion(self
):
626 Quoted values on the commandline don't currently have quotes stripped.
627 Pike says this is for compat reasons.
629 self
.pp
.handleCommandLine(['-DFOO="ABCD"'])
630 self
.do_include_compare(
632 "#filter substitution",
638 def test_octal_value_quoted_expansion(self
):
639 self
.pp
.handleCommandLine(['-DFOO="0100"'])
640 self
.do_include_compare(
642 "#filter substitution",
648 def test_number_value_not_equals_quoted_defines(self
):
649 self
.pp
.handleCommandLine(['-DFOO="1000"'])
650 self
.do_include_pass(
660 def test_octal_value_not_equals_quoted_defines(self
):
661 self
.pp
.handleCommandLine(['-DFOO="0100"'])
662 self
.do_include_pass(
672 def test_undefined_variable(self
):
673 with
MockedOpen({"f": "#filter substitution\n@foo@"}):
674 with self
.assertRaises(Preprocessor
.Error
) as e
:
675 self
.pp
.do_include("f")
676 self
.assertEqual(e
.key
, "UNDEFINED_VAR")
678 def test_include(self
):
680 "foo/test": "\n".join(
682 "#define foo foobarbaz",
690 "#define bar barfoobaz",
697 "#filter substitution",
698 "#define inc ../bar",
705 with
MockedOpen(files
):
706 self
.pp
.do_include("f")
707 self
.assertEqual(self
.pp
.out
.getvalue(), "foobarbaz\nbarfoobaz\n")
709 def test_include_line(self
):
711 "srcdir/test.js": "\n".join(
713 "#define foo foobarbaz",
719 "srcdir/bar.js": "\n".join(
721 "#define bar barfoobaz",
726 "srcdir/foo.js": "\n".join(
734 "objdir/baz.js": "baz\n",
735 "srcdir/f.js": "\n".join(
738 "#filter substitution",
739 "#define inc bar.js",
741 "#include ../objdir/baz.js",
749 '//@line 1 "$SRCDIR/foo.js"\n'
751 '//@line 2 "$SRCDIR/bar.js"\n'
753 '//@line 3 "$SRCDIR/foo.js"\n'
755 '//@line 2 "$SRCDIR/bar.js"\n'
757 '//@line 3 "$SRCDIR/test.js"\n'
759 '//@line 1 "$OBJDIR/baz.js"\n'
761 '//@line 6 "$SRCDIR/f.js"\n'
765 # Try with separate srcdir/objdir
766 with
MockedOpen(files
):
767 self
.pp
.topsrcdir
= os
.path
.abspath("srcdir")
768 self
.pp
.topobjdir
= os
.path
.abspath("objdir")
769 self
.pp
.do_include("srcdir/f.js")
770 self
.assertEqual(self
.pp
.out
.getvalue(), preprocessed
)
772 # Try again with relative objdir
774 files
["srcdir/objdir/baz.js"] = files
["objdir/baz.js"]
775 del files
["objdir/baz.js"]
776 files
["srcdir/f.js"] = files
["srcdir/f.js"].replace("../", "")
777 with
MockedOpen(files
):
778 self
.pp
.topsrcdir
= os
.path
.abspath("srcdir")
779 self
.pp
.topobjdir
= os
.path
.abspath("srcdir/objdir")
780 self
.pp
.do_include("srcdir/f.js")
781 self
.assertEqual(self
.pp
.out
.getvalue(), preprocessed
)
783 def test_include_missing_file(self
):
784 with
MockedOpen({"f": "#include foo\n"}):
785 with self
.assertRaises(Preprocessor
.Error
) as e
:
786 self
.pp
.do_include("f")
787 self
.assertEqual(e
.exception
.key
, "FILE_NOT_FOUND")
789 def test_include_undefined_variable(self
):
790 with
MockedOpen({"f": "#filter substitution\n#include @foo@\n"}):
791 with self
.assertRaises(Preprocessor
.Error
) as e
:
792 self
.pp
.do_include("f")
793 self
.assertEqual(e
.exception
.key
, "UNDEFINED_VAR")
795 def test_include_literal_at(self
):
797 "@foo@": "#define foo foobarbaz\n",
798 "f": "#include @foo@\n#filter substitution\n@foo@\n",
801 with
MockedOpen(files
):
802 self
.pp
.do_include("f")
803 self
.assertEqual(self
.pp
.out
.getvalue(), "foobarbaz\n")
805 def test_command_line_literal_at(self
):
806 with
MockedOpen({"@foo@.in": "@foo@\n"}):
807 self
.pp
.handleCommandLine(["-Fsubstitution", "-Dfoo=foobarbaz", "@foo@.in"])
808 self
.assertEqual(self
.pp
.out
.getvalue(), "foobarbaz\n")
810 def test_invalid_ifdef(self
):
811 with
MockedOpen({"dummy": "#ifdef FOO == BAR\nPASS\n#endif"}):
812 with self
.assertRaises(Preprocessor
.Error
) as e
:
813 self
.pp
.do_include("dummy")
814 self
.assertEqual(e
.exception
.key
, "INVALID_VAR")
816 with
MockedOpen({"dummy": "#ifndef FOO == BAR\nPASS\n#endif"}):
817 with self
.assertRaises(Preprocessor
.Error
) as e
:
818 self
.pp
.do_include("dummy")
819 self
.assertEqual(e
.exception
.key
, "INVALID_VAR")
821 # Trailing whitespaces, while not nice, shouldn't be an error.
822 self
.do_include_pass(
831 if __name__
== "__main__":