Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
import os
import shutil
import unittest
from tempfile import mkdtemp
from mozunit import MockedOpen, main
from six import StringIO
from mozbuild.preprocessor import Preprocessor
class TestPreprocessor(unittest.TestCase):
"""
Unit tests for the Context class
"""
def setUp(self):
self.pp = Preprocessor()
self.pp.out = StringIO()
def do_include_compare(self, content_lines, expected_lines):
content = "%s" % "\n".join(content_lines)
expected = "%s".rstrip() % "\n".join(expected_lines)
with MockedOpen({"dummy": content}):
self.pp.do_include("dummy")
self.assertEqual(self.pp.out.getvalue().rstrip("\n"), expected)
def do_include_pass(self, content_lines):
self.do_include_compare(content_lines, ["PASS"])
def test_conditional_if_0(self):
self.do_include_pass(
[
"#if 0",
"FAIL",
"#else",
"PASS",
"#endif",
]
)
def test_no_marker(self):
lines = [
"#if 0",
"PASS",
"#endif",
]
self.pp.setMarker(None)
self.do_include_compare(lines, lines)
def test_string_value(self):
self.do_include_compare(
[
"#define FOO STRING",
"#if FOO",
"string value is true",
"#else",
"string value is false",
"#endif",
],
["string value is false"],
)
def test_number_value(self):
self.do_include_compare(
[
"#define FOO 1",
"#if FOO",
"number value is true",
"#else",
"number value is false",
"#endif",
],
["number value is true"],
)
def test_conditional_if_0_elif_1(self):
self.do_include_pass(
[
"#if 0",
"#elif 1",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_conditional_if_1(self):
self.do_include_pass(
[
"#if 1",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_conditional_if_0_or_1(self):
self.do_include_pass(
[
"#if 0 || 1",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_conditional_if_1_elif_1_else(self):
self.do_include_pass(
[
"#if 1",
"PASS",
"#elif 1",
"FAIL",
"#else",
"FAIL",
"#endif",
]
)
def test_conditional_if_1_if_1(self):
self.do_include_pass(
[
"#if 1",
"#if 1",
"PASS",
"#else",
"FAIL",
"#endif",
"#else",
"FAIL",
"#endif",
]
)
def test_conditional_not_0(self):
self.do_include_pass(
[
"#if !0",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_conditional_not_0_and_1(self):
self.do_include_pass(
[
"#if !0 && !1",
"FAIL",
"#else",
"PASS",
"#endif",
]
)
def test_conditional_not_1(self):
self.do_include_pass(
[
"#if !1",
"FAIL",
"#else",
"PASS",
"#endif",
]
)
def test_conditional_not_emptyval(self):
self.do_include_compare(
[
"#define EMPTYVAL",
"#ifndef EMPTYVAL",
"FAIL",
"#else",
"PASS",
"#endif",
"#ifdef EMPTYVAL",
"PASS",
"#else",
"FAIL",
"#endif",
],
["PASS", "PASS"],
)
def test_conditional_not_nullval(self):
self.do_include_pass(
[
"#define NULLVAL 0",
"#if !NULLVAL",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_indentation(self):
self.do_include_pass(
[
" #define NULLVAL 0",
" #if !NULLVAL",
"PASS",
" #else",
"FAIL",
" #endif",
]
)
def test_expand(self):
self.do_include_pass(
[
"#define ASVAR AS",
"#expand P__ASVAR__S",
]
)
def test_undef_defined(self):
self.do_include_compare(
[
"#define BAR",
"#undef BAR",
"BAR",
],
["BAR"],
)
def test_undef_undefined(self):
self.do_include_compare(
[
"#undef BAR",
],
[],
)
def test_filter_attemptSubstitution(self):
self.do_include_compare(
[
"#filter attemptSubstitution",
"@PASS@",
"#unfilter attemptSubstitution",
],
["@PASS@"],
)
def test_filter_emptyLines(self):
self.do_include_compare(
[
"lines with a",
"",
"blank line",
"#filter emptyLines",
"lines with",
"",
"no blank lines",
"#unfilter emptyLines",
"yet more lines with",
"",
"blank lines",
],
[
"lines with a",
"",
"blank line",
"lines with",
"no blank lines",
"yet more lines with",
"",
"blank lines",
],
)
def test_filter_dumbComments(self):
self.do_include_compare(
[
"#filter dumbComments",
"PASS//PASS // PASS",
" //FAIL",
"// FAIL",
"PASS //",
"PASS // FAIL",
"//",
"",
"#unfilter dumbComments",
"// PASS",
],
[
"PASS//PASS // PASS",
"",
"",
"PASS //",
"PASS // FAIL",
"",
"",
"// PASS",
],
)
def test_filter_dumbComments_and_emptyLines(self):
self.do_include_compare(
[
"#filter dumbComments emptyLines",
"PASS//PASS // PASS",
" //FAIL",
"// FAIL",
"PASS //",
"PASS // FAIL",
"//",
"",
"#unfilter dumbComments emptyLines",
"",
"// PASS",
],
[
"PASS//PASS // PASS",
"PASS //",
"PASS // FAIL",
"",
"// PASS",
],
)
def test_filter_substitution(self):
self.do_include_pass(
[
"#define VAR ASS",
"#filter substitution",
"P@VAR@",
"#unfilter substitution",
]
)
def test_error(self):
with MockedOpen({"f": "#error spit this message out\n"}):
with self.assertRaises(Preprocessor.Error) as e:
self.pp.do_include("f")
self.assertEqual(e.args[0][-1], "spit this message out")
def test_ambigous_command(self):
comment = "# if I tell you a joke\n"
with MockedOpen({"f": comment}):
with self.assertRaises(Preprocessor.Error) as e:
self.pp.do_include("f")
the_exception = e.exception
self.assertEqual(the_exception.args[0][-1], comment)
def test_javascript_line(self):
# The preprocessor is reading the filename from somewhere not caught
# by MockedOpen.
tmpdir = mkdtemp()
try:
full = os.path.join(tmpdir, "javascript_line.js.in")
with open(full, "w") as fh:
fh.write(
"\n".join(
[
"// Line 1",
"#if 0",
"// line 3",
"#endif",
"// line 5",
"# comment",
"// line 7",
"// line 8",
"// line 9",
"# another comment",
"// line 11",
"#define LINE 1",
"// line 13, given line number overwritten with 2",
"",
]
)
)
self.pp.do_include(full)
out = "\n".join(
[
"// Line 1",
'//@line 5 "CWDjavascript_line.js.in"',
"// line 5",
'//@line 7 "CWDjavascript_line.js.in"',
"// line 7",
"// line 8",
"// line 9",
'//@line 11 "CWDjavascript_line.js.in"',
"// line 11",
'//@line 2 "CWDjavascript_line.js.in"',
"// line 13, given line number overwritten with 2",
"",
]
)
out = out.replace("CWD", tmpdir + os.path.sep)
self.assertEqual(self.pp.out.getvalue(), out)
finally:
shutil.rmtree(tmpdir)
def test_literal(self):
self.do_include_pass(
[
"#literal PASS",
]
)
def test_var_directory(self):
self.do_include_pass(
[
"#ifdef DIRECTORY",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_var_file(self):
self.do_include_pass(
[
"#ifdef FILE",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_var_if_0(self):
self.do_include_pass(
[
"#define VAR 0",
"#if VAR",
"FAIL",
"#else",
"PASS",
"#endif",
]
)
def test_var_if_0_elifdef(self):
self.do_include_pass(
[
"#if 0",
"#elifdef FILE",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_var_if_0_elifndef(self):
self.do_include_pass(
[
"#if 0",
"#elifndef VAR",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_var_ifdef_0(self):
self.do_include_pass(
[
"#define VAR 0",
"#ifdef VAR",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_var_ifdef_1_or_undef(self):
self.do_include_pass(
[
"#define FOO 1",
"#if defined(FOO) || defined(BAR)",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_var_ifdef_undef(self):
self.do_include_pass(
[
"#define VAR 0",
"#undef VAR",
"#ifdef VAR",
"FAIL",
"#else",
"PASS",
"#endif",
]
)
def test_var_ifndef_0(self):
self.do_include_pass(
[
"#define VAR 0",
"#ifndef VAR",
"FAIL",
"#else",
"PASS",
"#endif",
]
)
def test_var_ifndef_0_and_undef(self):
self.do_include_pass(
[
"#define FOO 0",
"#if !defined(FOO) && !defined(BAR)",
"FAIL",
"#else",
"PASS",
"#endif",
]
)
def test_var_ifndef_undef(self):
self.do_include_pass(
[
"#define VAR 0",
"#undef VAR",
"#ifndef VAR",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_var_line(self):
self.do_include_pass(
[
"#ifdef LINE",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_filterDefine(self):
self.do_include_pass(
[
"#filter substitution",
"#define VAR AS",
"#define VAR2 P@VAR@",
"@VAR2@S",
]
)
def test_number_value_equals(self):
self.do_include_pass(
[
"#define FOO 1000",
"#if FOO == 1000",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_default_defines(self):
self.pp.handleCommandLine(["-DFOO"])
self.do_include_pass(
[
"#if FOO == 1",
"PASS",
"#else",
"FAIL",
]
)
def test_number_value_equals_defines(self):
self.pp.handleCommandLine(["-DFOO=1000"])
self.do_include_pass(
[
"#if FOO == 1000",
"PASS",
"#else",
"FAIL",
]
)
def test_octal_value_equals(self):
self.do_include_pass(
[
"#define FOO 0100",
"#if FOO == 0100",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_octal_value_equals_defines(self):
self.pp.handleCommandLine(["-DFOO=0100"])
self.do_include_pass(
[
"#if FOO == 0100",
"PASS",
"#else",
"FAIL",
"#endif",
]
)
def test_value_quoted_expansion(self):
"""
Quoted values on the commandline don't currently have quotes stripped.
Pike says this is for compat reasons.
"""
self.pp.handleCommandLine(['-DFOO="ABCD"'])
self.do_include_compare(
[
"#filter substitution",
"@FOO@",
],
['"ABCD"'],
)
def test_octal_value_quoted_expansion(self):
self.pp.handleCommandLine(['-DFOO="0100"'])
self.do_include_compare(
[
"#filter substitution",
"@FOO@",
],
['"0100"'],
)
def test_number_value_not_equals_quoted_defines(self):
self.pp.handleCommandLine(['-DFOO="1000"'])
self.do_include_pass(
[
"#if FOO == 1000",
"FAIL",
"#else",
"PASS",
"#endif",
]
)
def test_octal_value_not_equals_quoted_defines(self):
self.pp.handleCommandLine(['-DFOO="0100"'])
self.do_include_pass(
[
"#if FOO == 0100",
"FAIL",
"#else",
"PASS",
"#endif",
]
)
def test_undefined_variable(self):
with MockedOpen({"f": "#filter substitution\n@foo@"}):
with self.assertRaises(Preprocessor.Error) as e:
self.pp.do_include("f")
self.assertEqual(e.key, "UNDEFINED_VAR")
def test_include(self):
files = {
"foo/test": "\n".join(
[
"#define foo foobarbaz",
"#include @inc@",
"@bar@",
"",
]
),
"bar": "\n".join(
[
"#define bar barfoobaz",
"@foo@",
"",
]
),
"f": "\n".join(
[
"#filter substitution",
"#define inc ../bar",
"#include foo/test",
"",
]
),
}
with MockedOpen(files):
self.pp.do_include("f")
self.assertEqual(self.pp.out.getvalue(), "foobarbaz\nbarfoobaz\n")
def test_include_line(self):
files = {
"srcdir/test.js": "\n".join(
[
"#define foo foobarbaz",
"#include @inc@",
"@bar@",
"",
]
),
"srcdir/bar.js": "\n".join(
[
"#define bar barfoobaz",
"@foo@",
"",
]
),
"srcdir/foo.js": "\n".join(
[
"bazfoobar",
"#include bar.js",
"bazbarfoo",
"",
]
),
"objdir/baz.js": "baz\n",
"srcdir/f.js": "\n".join(
[
"#include foo.js",
"#filter substitution",
"#define inc bar.js",
"#include test.js",
"#include ../objdir/baz.js",
"fin",
"",
]
),
}
preprocessed = (
'//@line 1 "$SRCDIR/foo.js"\n'
"bazfoobar\n"
'//@line 2 "$SRCDIR/bar.js"\n'
"@foo@\n"
'//@line 3 "$SRCDIR/foo.js"\n'
"bazbarfoo\n"
'//@line 2 "$SRCDIR/bar.js"\n'
"foobarbaz\n"
'//@line 3 "$SRCDIR/test.js"\n'
"barfoobaz\n"
'//@line 1 "$OBJDIR/baz.js"\n'
"baz\n"
'//@line 6 "$SRCDIR/f.js"\n'
"fin\n"
)
# Try with separate srcdir/objdir
with MockedOpen(files):
self.pp.topsrcdir = os.path.abspath("srcdir")
self.pp.topobjdir = os.path.abspath("objdir")
self.pp.do_include("srcdir/f.js")
self.assertEqual(self.pp.out.getvalue(), preprocessed)
# Try again with relative objdir
self.setUp()
files["srcdir/objdir/baz.js"] = files["objdir/baz.js"]
del files["objdir/baz.js"]
files["srcdir/f.js"] = files["srcdir/f.js"].replace("../", "")
with MockedOpen(files):
self.pp.topsrcdir = os.path.abspath("srcdir")
self.pp.topobjdir = os.path.abspath("srcdir/objdir")
self.pp.do_include("srcdir/f.js")
self.assertEqual(self.pp.out.getvalue(), preprocessed)
def test_include_missing_file(self):
with MockedOpen({"f": "#include foo\n"}):
with self.assertRaises(Preprocessor.Error) as e:
self.pp.do_include("f")
self.assertEqual(e.exception.key, "FILE_NOT_FOUND")
def test_include_undefined_variable(self):
with MockedOpen({"f": "#filter substitution\n#include @foo@\n"}):
with self.assertRaises(Preprocessor.Error) as e:
self.pp.do_include("f")
self.assertEqual(e.exception.key, "UNDEFINED_VAR")
def test_include_literal_at(self):
files = {
"@foo@": "#define foo foobarbaz\n",
"f": "#include @foo@\n#filter substitution\n@foo@\n",
}
with MockedOpen(files):
self.pp.do_include("f")
self.assertEqual(self.pp.out.getvalue(), "foobarbaz\n")
def test_command_line_literal_at(self):
with MockedOpen({"@foo@.in": "@foo@\n"}):
self.pp.handleCommandLine(["-Fsubstitution", "-Dfoo=foobarbaz", "@foo@.in"])
self.assertEqual(self.pp.out.getvalue(), "foobarbaz\n")
def test_invalid_ifdef(self):
with MockedOpen({"dummy": "#ifdef FOO == BAR\nPASS\n#endif"}):
with self.assertRaises(Preprocessor.Error) as e:
self.pp.do_include("dummy")
self.assertEqual(e.exception.key, "INVALID_VAR")
with MockedOpen({"dummy": "#ifndef FOO == BAR\nPASS\n#endif"}):
with self.assertRaises(Preprocessor.Error) as e:
self.pp.do_include("dummy")
self.assertEqual(e.exception.key, "INVALID_VAR")
# Trailing whitespaces, while not nice, shouldn't be an error.
self.do_include_pass(
[
"#ifndef FOO ",
"PASS",
"#endif",
]
)
if __name__ == "__main__":
main()