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
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
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()