Repository: nanonyme/simplecpreprocessor

Issue:
Tokens should not be expanded inside strings
Current token expansion logic needs a rewrite. Quoted strings are considered single tokens. This is most likely not possible to implement by retaining the concept of yielding entire lines out of expander

- [x] Write failing tests
- [x] Fix tests

Discussion:

Patch:
diff --git a/simplecpreprocessor/__init__.py b/simplecpreprocessor/__init__.py
index 603177f..66e416e 100644
--- a/simplecpreprocessor/__init__.py
+++ b/simplecpreprocessor/__init__.py
@@ -4,6 +4,6 @@ try:
     __version__ = get_distribution(__name__).version
 except DistributionNotFound:
     from setuptools_scm import get_version
-    __version__ = get_version()                
+    __version__ = get_version()
 
 __all__ = ["preprocess"]
diff --git a/simplecpreprocessor/__main__.py b/simplecpreprocessor/__main__.py
index 9fe3b54..45e3f30 100644
--- a/simplecpreprocessor/__main__.py
+++ b/simplecpreprocessor/__main__.py
@@ -3,15 +3,15 @@ import argparse
 
 parser = argparse.ArgumentParser()
 parser.add_argument("--input-file", required=True,
-                                        help="Header file to parse. Can also be a shim header")
+                    help="Header file to parse. Can also be a shim header")
 parser.add_argument("--include-path", action="append",
-                                        help="Include paths", dest="include_paths",
-                                                            default=[])
+                    help="Include paths", dest="include_paths",
+                    default=[])
 parser.add_argument("--ignore-header", action="append",
-                                        help="Headers to ignore. Useful for eg CFFI",
-                                                            dest="ignore_headers", default=[])
+                    help="Headers to ignore. Useful for eg CFFI",
+                    dest="ignore_headers", default=[])
 parser.add_argument("--output-file", required=True,
-                                        help="Output file that contains preprocessed header(s)")
+                    help="Output file that contains preprocessed header(s)")
 
 
 def main(args=None):
@@ -21,5 +21,6 @@ def main(args=None):
             for line in preprocess(i, include_paths=args.include_paths,
                                    ignore_headers=args.ignore_headers):
                 o.write(line)
-                                            
+
+
 main()
diff --git a/simplecpreprocessor/core.py b/simplecpreprocessor/core.py
index 54c6061..8058220 100644
--- a/simplecpreprocessor/core.py
+++ b/simplecpreprocessor/core.py
@@ -96,27 +96,57 @@ IFDEF = "ifdef"
 IFNDEF = "ifndef"
 ELSE = "else"
 SKIP_FILE = object()
-TOKEN = re.compile(r"\b\w+\b")
+TOKEN = re.compile(r"\b\w+\b|\W")
+DOUBLE_QUOTE = '"'
+SINGLE_QUOTE = "'"
+CHAR = re.compile(r"^'\w'$")
 
+def _consume(tokens, buf, separator):
+    for token in tokens:
+        buf.append(token)
+        if token == separator:
+            return "".join(buf)
+
+def _handle_string(tokens):
+    buf = [DOUBLE_QUOTE]
+    return _consume(tokens, buf, DOUBLE_QUOTE)
+
+def _handle_char(tokens):
+    buf = [SINGLE_QUOTE]
+    return _consume(tokens, buf, SINGLE_QUOTE)
+
+def _tokenize(s):
+    for match in TOKEN.finditer(s):
+        yield match.group(0)
 
 class TokenExpander(object):
     def __init__(self, defines):
         self.defines = defines
 
     def expand_tokens(self, line, seen=()):
-        def helper(match):
-            return self._replace_tokens(match, seen)
-        return TOKEN.sub(helper, line)
-
-    def _replace_tokens(self, match, seen):
-        word = match.group(0)
-        new_word = self.defines.get(word, word)
-        if new_word == word or word in seen:
-            return word
-        else:
-            local_seen = {word}
-            local_seen.update(seen)
-            return self.expand_tokens(new_word, local_seen)
+        tokens = _tokenize(line)
+        for token in tokens:
+            if token in seen:
+                yield token
+                continue
+            elif token == DOUBLE_QUOTE:
+                token = _handle_string(tokens)
+                if token is None:
+                    raise ParseError("Unbalanced \"")
+            elif token == SINGLE_QUOTE:
+                token = _handle_char(tokens)
+                if token is None:
+                    raise ParseError("Unbalanced '")
+                elif not CHAR.search(token):
+                    raise ParseError("%s is not a char" % token)
+            if token not in self.defines:
+                yield token
+            else:
+                new_seen = {token}
+                new_seen.update(seen)
+                token = self.defines[token]
+                for token in self.expand_tokens(token, new_seen):
+                    yield token
 
 
 class Preprocessor(object):
@@ -243,8 +273,9 @@ class Preprocessor(object):
 
     def process_source_line(self, **kwargs):
         line = kwargs["line"]
-        line = self.token_expander.expand_tokens(line)
-        return line + self.line_ending
+        for chunk in self.token_expander.expand_tokens(line):
+            yield chunk
+        yield self.line_ending
 
     def skip_file(self, name):
         item = self.include_once.get(name)
@@ -318,7 +349,9 @@ class Preprocessor(object):
                         for line in ret:
                             yield line
             elif not self.ignore:
-                yield self.process_source_line(line=line, line_num=line_num)
+                for chunk in self.process_source_line(line=line,
+                                                      line_num=line_num):
+                    yield chunk
         self.check_fullfile_guard()
         self.header_stack.pop()
         if not self.header_stack and self.constraints:


Test patch:
diff --git a/simplecpreprocessor/tests.py b/simplecpreprocessor/tests.py
index 742252c..5b3cecf 100644
--- a/simplecpreprocessor/tests.py
+++ b/simplecpreprocessor/tests.py
@@ -62,75 +62,100 @@ class ProfilerMixin(object):
 
 class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
 
-    def run_case(self, input_list, expected_list):
-        output_list = list(simplecpreprocessor.preprocess(input_list))
-        self.assertEqual(output_list, expected_list)
+    def run_case(self, input_list, expected):
+        output = "".join(simplecpreprocessor.preprocess(input_list))
+        self.assertEqual(output, expected)
 
     def test_define(self):
         f_obj = FakeFile("header.h", ["#define FOO 1\n",
                                       "FOO"])
-        expected_list = ["1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n"
+        self.run_case(f_obj, expected)
+
+    def test_string_token_simple(self):
+        f_obj = FakeFile("header.h", ["#define FOO 1\n",
+                                      '"FOO"'])
+        expected = '"FOO"\n'
+        self.run_case(f_obj, expected)
+
+    def test_char_token_simple(self):
+        f_obj = FakeFile("header.h", ["#define F 1\n",
+                                      "'F'\n"])
+        expected = "'F'\n"
+        self.run_case(f_obj, expected)
+
+    def test_char_token_error(self):
+        f_obj = FakeFile("header.h", ["#define FOO 1\n",
+                                      "'FOO'\n"])
+        expected = "'FOO'\n"
+        with self.assertRaises(simplecpreprocessor.ParseError):
+            self.run_case(f_obj, expected)
+
+    def test_string_token_with_single_quote(self):
+        f_obj = FakeFile("header.h", ["#define FOO 1\n",
+                                      '"FOO\'"'])
+        expected = '"FOO\'"\n'
+        self.run_case(f_obj, expected)                                
 
     def test_multiline_define(self):
         f_obj = FakeFile("header.h", ["#define FOO \\\n",
                                       "\t1\n",
                                       "FOO\n"])
-        expected_list = ["1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n"
+        self.run_case(f_obj, expected)
 
     def test_define_simple_self_referential(self):
         f_obj = FakeFile("header.h", ["#define FOO FOO\n",
                                       "FOO\n"])
-        expected_list = ["FOO\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "FOO\n"
+        self.run_case(f_obj, expected)
 
     def test_expand_size_t(self):
         f_obj = FakeFile("header.h", ["__SIZE_TYPE__\n"])
-        expected_list = ["size_t\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "size_t\n"
+        self.run_case(f_obj, expected)
 
     def test_define_indirect_self_reference(self):
         f_obj = FakeFile("header.h", ["#define x (4 + y)\n",
                                       "#define y (2 * x)\n",
                                       "x\n", "y\n"])
-        expected_list = ["(4 + (2 * x))\n", "(2 * (4 + y))\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "(4 + (2 * x))\n(2 * (4 + y))\n"
+        self.run_case(f_obj, expected)
 
     def test_define_indirect_self_reference_multiple(self):
         f_obj = FakeFile("header.h", ["#define I 1\n",
                                       "#define J I + 2\n",
                                       "#define K I + J\n",
                                       "I\n", "J\n", "K\n"])
-        expected_list = ["1\n", "1 + 2\n", "1 + 1 + 2\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n1 + 2\n1 + 1 + 2\n"
+        self.run_case(f_obj, expected)
 
     def test_partial_match(self):
         f_obj = FakeFile("header.h", [
             "#define FOO\n",
             "FOOBAR\n"
         ])
-        expected_list = ["FOOBAR\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "FOOBAR\n"
+        self.run_case(f_obj, expected)
 
     def test_blank_define(self):
         f_obj = FakeFile("header.h", ["#define FOO\n",
                                       "FOO\n"])
-        expected_list = ["\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "\n"
+        self.run_case(f_obj, expected)
 
     def test_define_parens(self):
         f_obj = FakeFile("header.h", ["#define FOO (x)\n",
                                       "FOO\n"])
-        expected_list = ["(x)\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "(x)\n"
+        self.run_case(f_obj, expected)
 
     def test_define_undefine(self):
         f_obj = FakeFile("header.h", ["#define FOO 1\n",
                                       "#undef FOO\n",
                                       "FOO"])
-        expected_list = ["FOO\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "FOO\n"
+        self.run_case(f_obj, expected)
 
     def test_complex_ignore(self):
         f_obj = FakeFile("header.h",
@@ -145,8 +170,8 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
                             "#define X 4\n",
                             "#endif\n",
                             "X\n"])
-        expected_list = ["X\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "X\n"
+        self.run_case(f_obj, expected)
 
     def test_extra_endif_causes_error(self):
         input_list = ["#endif\n"]
@@ -172,27 +197,27 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
         f_obj = FakeFile("header.h", ["#define FOO\n", "#ifndef FOO\n",
                                       "#define BAR 1\n",
                                       "#endif\n", "BAR\n"])
-        expected_list = ["BAR\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "BAR\n"
+        self.run_case(f_obj, expected)
 
     def test_ifdef_unfulfilled_define_ignored(self):
         f_obj = FakeFile("header.h", ["#ifdef FOO\n", "#define BAR 1\n",
                                       "#endif\n", "BAR\n"])
-        expected_list = ["BAR\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "BAR\n"
+        self.run_case(f_obj, expected)
 
     def test_ifndef_fulfilled_define_allowed(self):
         f_obj = FakeFile("header.h", ["#ifndef FOO\n", "#define BAR 1\n",
                                       "#endif\n", "BAR\n"])
-        expected_list = ["1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n"
+        self.run_case(f_obj, expected)
 
     def test_fulfilled_ifdef_define_allowed(self):
         f_obj = FakeFile("header.h", ["#define FOO", "#ifdef FOO\n",
                                       "#define BAR 1\n",
                                       "#endif\n", "BAR\n"])
-        expected_list = ["1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n"
+        self.run_case(f_obj, expected)
 
     def test_define_inside_ifndef(self):
         f_obj = FakeFile("header.h", ["#ifndef MODULE\n",
@@ -203,8 +228,8 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
                                       "1\n",
                                       "#endif\n"])
 
-        expected_list = ["1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n"
+        self.run_case(f_obj, expected)
 
     def test_ifdef_else_undefined(self):
         f_obj = FakeFile("header.h", [
@@ -214,8 +239,8 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#define X 0\n",
             "#endif\n",
             "X\n"])
-        expected_list = ["0\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "0\n"
+        self.run_case(f_obj, expected)
 
     def test_ifdef_else_defined(self):
         f_obj = FakeFile("header.h", [
@@ -226,8 +251,8 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#define X 0\n",
             "#endif\n",
             "X\n"])
-        expected_list = ["1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n"
+        self.run_case(f_obj, expected)
 
     def test_ifndef_else_undefined(self):
         f_obj = FakeFile("header.h", [
@@ -237,8 +262,8 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#define X 0\n",
             "#endif\n",
             "X\n"])
-        expected_list = ["1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n"
+        self.run_case(f_obj, expected)
 
     def test_ifndef_else_defined(self):
         f_obj = FakeFile("header.h", [
@@ -249,20 +274,20 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#define X 0\n",
             "#endif\n",
             "X\n"])
-        expected_list = ["0\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "0\n"
+        self.run_case(f_obj, expected)
 
     def test_lines_normalized(self):
         f_obj = FakeFile("header.h", ["foo\r\n", "bar\r\n"])
-        expected_list = ["foo\n", "bar\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "foo\nbar\n"
+        self.run_case(f_obj, expected)
 
     def test_lines_normalize_custom(self):
         f_obj = FakeFile("header.h", ["foo\n", "bar\n"])
-        expected_list = ["foo\r\n", "bar\r\n"]
+        expected = "foo\r\nbar\r\n"
         ret = simplecpreprocessor.preprocess(f_obj,
                                              line_ending="\r\n")
-        self.assertEqual(list(ret), expected_list)
+        self.assertEqual("".join(ret), expected)
 
     def test_include_local_file_with_subdirectory(self):
         other_header = "somedirectory/other.h"
@@ -270,7 +295,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
         handler = FakeHandler({other_header: ["1\n"]})
         ret = simplecpreprocessor.preprocess(f_obj,
                                              header_handler=handler)
-        self.assertEqual(list(ret), ["1\n"])
+        self.assertEqual("".join(ret), "1\n")
 
     def test_include_local_precedence(self):
         other_header = "other.h"
@@ -281,7 +306,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
                               include_paths=[path])
         ret = simplecpreprocessor.preprocess(f_obj,
                                              header_handler=handler)
-        self.assertEqual(list(ret), ["1\n"])
+        self.assertEqual("".join(ret), "1\n")
 
     def test_include_local_fallback(self):
         other_header = "other.h"
@@ -291,7 +316,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
                               include_paths=[path])
         ret = simplecpreprocessor.preprocess(f_obj,
                                              header_handler=handler)
-        self.assertEqual(list(ret), ["2\n"])
+        self.assertEqual("".join(ret), "2\n")
 
     def test_ifdef_file_guard(self):
         other_header = "somedirectory/other.h"
@@ -300,14 +325,14 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
         handler = FakeHandler({other_header: ["1\n"]})
         ret = simplecpreprocessor.preprocess(f_obj,
                                              header_handler=handler)
-        self.assertEqual(list(ret), ["1\n"])
+        self.assertEqual("".join(ret), "1\n")
 
     def test_define_with_comment(self):
         f_obj = FakeFile("header.h", [
             "#define FOO 1 // comment\n",
             "FOO\n"])
-        expected_list = ["1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n"
+        self.run_case(f_obj, expected)
 
     def test_ifdef_with_comment(self):
         f_obj = FakeFile("header.h", [
@@ -315,8 +340,8 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#ifdef FOO // comment\n",
             "1\n",
             "#endif"])
-        expected_list = ["1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "1\n"
+        self.run_case(f_obj, expected)
 
     def test_include_with_path_list(self):
         f_obj = FakeFile("header.h", ['#include <other.h>\n'])
@@ -326,21 +351,21 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
         ret = simplecpreprocessor.preprocess(f_obj,
                                              include_paths=include_paths,
                                              header_handler=handler)
-        self.assertEqual(list(ret), ["1\n"])
+        self.assertEqual("".join(ret), "1\n")
 
     def test_tab_macro_indentation(self):
         f_obj = FakeFile("header.h", [
             "\t#define FOO 1\n",
             "\tFOO\n"])
-        expected_list = ["\t1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "\t1\n"
+        self.run_case(f_obj, expected)
 
     def test_space_macro_indentation(self):
         f_obj = FakeFile("header.h", [
             "    #define FOO 1\n",
             "    FOO\n"])
-        expected_list = ["    1\n"]
-        self.run_case(f_obj, expected_list)
+        expected = "    1\n"
+        self.run_case(f_obj, expected)
 
     def test_include_with_path_list_with_subdirectory(self):
         header_file = posixpath.join("nested", "other.h")
@@ -352,7 +377,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
         ret = simplecpreprocessor.preprocess(f_obj,
                                              include_paths=include_paths,
                                              header_handler=handler)
-        self.assertEqual(list(ret), ["1\n"])
+        self.assertEqual("".join(ret), "1\n")
 
     def test_include_missing_local_file(self):
         other_header = posixpath.join("somedirectory", "other.h")
@@ -361,7 +386,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
         with self.assertRaises(simplecpreprocessor.ParseError):
             ret = simplecpreprocessor.preprocess(f_obj,
                                                  header_handler=handler)
-            list(ret)
+            "".join(ret)
 
     def test_ignore_include_path(self):
         f_obj = FakeFile("header.h", ['#include <other.h>\n'])
@@ -373,7 +398,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
                                              include_paths=paths,
                                              header_handler=handler,
                                              ignore_headers=ignored)
-        self.assertEqual(list(ret), [])
+        self.assertEqual("".join(ret), "")
 
     def test_pragma_once(self):
         f_obj = FakeFile("header.h", ["""#include "other.h"\n""",
@@ -388,7 +413,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#endif\n"]})
         preprocessor = simplecpreprocessor.Preprocessor(header_handler=handler)
         ret = preprocessor.preprocess(f_obj)
-        self.assertEqual(list(ret), ["1\n"])
+        self.assertEqual("".join(ret), "1\n")
         self.assertTrue(preprocessor.skip_file("other.h"))
 
     def test_fullfile_guard_ifdef_skip(self):
@@ -399,7 +424,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#endif\n"]})
         preprocessor = simplecpreprocessor.Preprocessor(header_handler=handler)
         ret = preprocessor.preprocess(f_obj)
-        self.assertEqual(list(ret), ["1\n"])
+        self.assertEqual("".join(ret), "1\n")
         self.assertTrue(preprocessor.skip_file("other.h"),
                         "%s -> %s" % (preprocessor.include_once,
                                       preprocessor.defines))
@@ -413,7 +438,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#endif\n"]})
         preprocessor = simplecpreprocessor.Preprocessor(header_handler=handler)
         ret = preprocessor.preprocess(f_obj)
-        self.assertEqual(list(ret), ["1\n"])
+        self.assertEqual("".join(ret), "1\n")
         self.assertFalse(preprocessor.skip_file("other.h"),
                          "%s -> %s" % (preprocessor.include_once,
                                        preprocessor.defines))
@@ -427,7 +452,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#endif\n"]})
         preprocessor = simplecpreprocessor.Preprocessor(header_handler=handler)
         ret = preprocessor.preprocess(f_obj)
-        self.assertEqual(list(ret), ["done\n"])
+        self.assertEqual("".join(ret), "done\n")
         self.assertTrue(preprocessor.skip_file("other.h"),
                         "%s -> %s" % (preprocessor.include_once,
                                       preprocessor.defines))
@@ -440,7 +465,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "#endif\n"]})
         preprocessor = simplecpreprocessor.Preprocessor(header_handler=handler)
         ret = preprocessor.preprocess(f_obj)
-        self.assertEqual(list(ret), ["done\n"])
+        self.assertEqual("".join(ret), "done\n")
         self.assertFalse(preprocessor.skip_file("other.h"),
                          "%s -> %s" % (preprocessor.include_once,
                                        preprocessor.defines))
@@ -456,7 +481,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "foo\n"]})
         preprocessor = simplecpreprocessor.Preprocessor(header_handler=handler)
         ret = preprocessor.preprocess(f_obj)
-        self.assertEqual(list(ret), ["foo\n", "done\n"])
+        self.assertEqual("".join(ret), "foo\ndone\n")
         self.assertEqual({}, preprocessor.include_once)
         self.assertFalse(preprocessor.skip_file("other.h"),
                          "%s -> %s" % (preprocessor.include_once,
@@ -472,7 +497,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
             "foo\n"]})
         preprocessor = simplecpreprocessor.Preprocessor(header_handler=handler)
         ret = preprocessor.preprocess(f_obj)
-        self.assertEqual(list(ret), ["foo\n", "done\n"])
+        self.assertEqual("".join(ret), "foo\ndone\n")
         self.assertEqual({}, preprocessor.include_once)
         self.assertFalse(preprocessor.skip_file("other.h"),
                          "%s -> %s" % (preprocessor.include_once,
@@ -484,7 +509,7 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
         const = {"ODDPLATFORM": "ODDPLATFORM"}
         ret = simplecpreprocessor.preprocess(f_obj,
                                              platform_constants=const)
-        self.assertEqual(list(ret), ["ODDPLATFORM\n"])
+        self.assertEqual("".join(ret), "ODDPLATFORM\n")
 
     def test_handler_missing_file(self):
         handler = FakeHandler([])
@@ -503,4 +528,4 @@ class TestSimpleCPreprocessor(ProfilerMixin, unittest.TestCase):
         const = {"A": "value"}
         ret = simplecpreprocessor.preprocess(f_obj,
                                              platform_constants=const)
-        self.assertEqual(list(ret), ["value value\n"])
+        self.assertEqual("".join(ret), "value value\n")