File: expect_tests.py

package info (click to toggle)
cmake-format 0.6.13-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,436 kB
  • sloc: python: 16,990; makefile: 14
file content (248 lines) | stat: -rw-r--r-- 7,492 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# -*- coding: utf-8 -*-
# pylint: disable=R1708
from __future__ import unicode_literals

import io
import logging
import os
import re
import sys
import unittest

from cmakelang import configuration
from cmakelang import lex

from cmakelang.lint import __main__
from cmakelang.lint import lint_util
from cmakelang.lint.test import genfiles


def overzip(iterable_a, iterable_b):
  """
  Like itertools.izip but instead if the two lists have different sizes then
  the resulting generator will yield a number of pairs equal to the larger of
  the two inputs (rathe than the smaller). The empty list will be padded with
  None elements.
  """
  iter_a = iter(iterable_a)
  iter_b = iter(iterable_b)

  item_a = next(iter_a, None)
  item_b = next(iter_b, None)

  # NOTE(josh): this only matters when overzipping a parse tree. It's not
  # meaningful for overzipping a layout tree, but it doesn't hurt since
  # lexer tokens don't show up in the layout tree
  while isinstance(item_a, lex.Token):
    item_a = next(iter_a, None)

  while item_a is not None and item_b is not None:
    yield(item_a, item_b)
    item_a = next(iter_a, None)
    while isinstance(item_a, lex.Token):
      item_a = next(iter_a, None)
    item_b = next(iter_b, None)

  while item_a is not None:
    yield(item_a, None)
    item_a = next(iter_a, None)
    while isinstance(item_a, lex.Token):
      item_a = next(iter_a, None)

  while item_b is not None:
    yield(None, item_b)
    item_b = next(iter_b, None)


def camel_to_snake_callback(match):
  """
  Substituion callback for camel_to_snake
  """
  chars = match.group(0)
  return "{}_{}".format(chars[0], chars[1])


def camel_to_snake(camelstr):
  """
  Translate a camelCaseString into a snake_case_string
  """
  return re.sub("[a-z][A-Z]", camel_to_snake_callback, camelstr).lower()


def snake_to_camel_callback(match):
  """
  Substituion callback for camel_to_snake
  """
  snake_chars = match.group(0)
  return snake_chars[0] + snake_chars[-1].upper()


def snake_to_camel(camelstr, upper=False):
  """
  Translate a camelCaseString into a snake_case_string
  """
  lower_camel = re.sub("([^_]?_+[^_]?)", snake_to_camel_callback, camelstr)
  if upper:
    return lower_camel[0].upper() + lower_camel[1:]
  return lower_camel


class TestBase(unittest.TestCase):
  def __init__(self, *args):
    super(TestBase, self).__init__(*args)
    self.config = configuration.Configuration()

  def execute_test(self, test_name, test_body, expect_list):
    outfile = io.StringIO()
    global_ctx = lint_util.GlobalContext(outfile)
    local_ctx = global_ctx.get_file_ctx(test_name, self.config)
    __main__.process_file(self.config, local_ctx, test_body)
    for actual, expected in overzip(local_ctx.get_lint(), expect_list):
      if expected is None:
        raise AssertionError(
            "More lint than expected, starting with {}".format(actual))

      if "@" in expected:
        expect_id, expect_locstr = expected.split("@")
        expect_loc = tuple(int(val) for val in expect_locstr.split(":"))
      else:
        expect_id = expected
        expect_loc = ()

      if actual is None:
        if expect_loc:
          raise AssertionError(
              "Missing expected lint {} at {}".format(expect_id, expect_loc))

        raise AssertionError(
            "Missing expected lint {}".format(expect_id))

      if expect_id != actual.spec.idstr:
        raise AssertionError(
            "Expected lint {} but got lint {}".format(expect_id, actual))

      actual_loc = actual.location
      if actual_loc is None:
        actual_loc = ()
      for expect_val, actual_val in zip(expect_loc, actual_loc):
        if expect_val != actual_val:
          raise AssertionError(
              "Expected lint {}@{} but got it at {}".format(
                  expect_id, ":".join(str(x) for x in expect_loc),
                  actual_loc))


EXCLUSIONS = [
    "expect_lint.cmake"
]


def iter_testfiles():
  thisdir = os.path.dirname(os.path.realpath(__file__))
  base_override = os.getenv("PYBUILD_TEST_BASE_OVERRIDE")
  if base_override is not None:
    thisdir = thisdir.replace(os.environ['PWD'], base_override)
  for dirpath, _dirnames, filenames in os.walk(thisdir):
    for filename in filenames:
      if filename in EXCLUSIONS:
        continue
      if filename.endswith(".cmake"):
        yield os.path.join(dirpath, filename)


def iter_tests_from_file(filepath):
  with io.open(filepath, "r", newline='') as infile:
    # Split lines but if the final newline is missing then preserve that fact
    items = re.split("(\n)", infile.read())
    lines = []
    for item in items:
      if item == "\n":
        lines[-1] += item
      else:
        lines.append(item)

  # Simple state-machine parser
  test_name = None
  line_buffer = []
  expect_str = None
  lineiter = enumerate(lines)
  for lineno, line in lineiter:
    if line.startswith("# test: "):
      if line_buffer:
        if test_name is None:
          raise ValueError(
              "Malformed sidecar {}:{}".format(filepath, lineno))
        yield (test_name, "".join(line_buffer), expect_str)
      test_name = line[len("# test: "):].rstrip()
      line_buffer = []
      expect_str = None
    elif line.startswith("# expect:"):
      expect_str = line[len("# expect:"):].strip()
    elif line.endswith("# end-test"):
      if test_name is None:
        raise ValueError(
            "Malformed sidecar {}:{}".format(filepath, lineno))
      yield (test_name, "".join(line_buffer), expect_str)
      test_name = None
      line_buffer = []
      expect_str = None
    else:
      line_buffer.append(line)

  if test_name and line_buffer:
    yield (test_name, "".join(line_buffer), expect_str)


def make_test_fun(test_name, test_body, expect_list):
  def test_fun(self):
    self.execute_test(test_name, test_body, expect_list)
  if sys.version_info < (3, 0, 0):
    # In python 2.7 test_name is a unicode object. We need to convert it to
    # a string.
    test_name = test_name.encode("utf-8")
  test_fun.__name__ = test_name
  test_fun.__doc__ = " ".join(test_name.split("_")[1:])
  return test_fun


def gen_test_classes():
  genfiles.rewrite_lint_tests()
  for filepath in iter_testfiles():
    basename = os.path.splitext(os.path.basename(filepath))[0]
    classname = snake_to_camel(basename, upper=True)
    defn = {}
    for test_name, test_body, expect_str in iter_tests_from_file(filepath):
      method_name = "test_" + test_name.replace("-", "_")
      expect_list = expect_str.rstrip().split(",")
      while expect_list and not expect_list[0]:
        expect_list.pop(0)
      defn[method_name] = make_test_fun(method_name, test_body, expect_list)

    yield type(classname, (TestBase,), defn)


class ConfigTestCase(TestBase):
  """
  Test that config options function correctly
  """

  def test_disabled_codes(self):
    self.config.lint.disabled_codes = ["E1120"]
    test_body = "set(VARNAME varvalue CACHE STRING)\n"
    self.execute_test("test_disabled_code", test_body, [])

  def test_tabpolicy_tabs(self):
    self.config.format.use_tabchars = True
    self.config.format.fractional_tab_policy = "round-up"
    test_body = "if(TRUE)\n\t message(\"hello\")\nendif()\n"
    self.execute_test("test_tabpolicy_tabs", test_body, ["C0306", "C0307"])


classobj = None
for classobj in gen_test_classes():
  globals()[classobj.__name__] = classobj
del classobj

if __name__ == "__main__":
  logging.basicConfig(level=logging.INFO)
  unittest.main()