Completed
Pull Request — master (#2558)
by Lasse
01:50
created

RootDirTestLinter.process_output()   A

Complexity

Conditions 2

Size

Total Lines 2

Duplication

Lines 2
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
dl 2
loc 2
rs 10
1
import platform
2
import os
3
import re
4
import sys
5
import unittest
6
from unittest.mock import ANY, Mock
7
8
from coalib.bearlib.abstractions.Linter import linter
9
from coalib.results.Diff import Diff
10
from coalib.results.Result import Result
11
from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY
12
from coalib.results.SourceRange import SourceRange
13
from coalib.settings.Section import Section
14
from unittest.case import skipIf
15
16
17
def get_testfile_name(name):
18
    """
19
    Gets the full path to a testfile inside ``linter_test_files`` directory.
20
21
    :param name: The filename of the testfile to get the full path for.
22
    :return:     The full path to given testfile name.
23
    """
24
    return os.path.join(os.path.dirname(os.path.realpath(__file__)),
25
                        "linter_test_files",
26
                        name)
27
28
29
class LinterComponentTest(unittest.TestCase):
30
31
    # Using `object` instead of an empty class results in inheritance problems
32
    # inside the linter decorator.
33
    class EmptyTestLinter:
34
        pass
35
36
    class RootDirTestLinter:
37
38
        def create_arguments(self, *args, **kwargs):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
39
            return tuple()
40
41
        def get_config_dir(self):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
42 View Code Duplication
            return '/'
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
43
44
        def process_output(self, output, *args, **kwargs):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
45
            assert output == '/\n', ("The linter doesn't run the command in "
46
                                     "the right directory!")
47
48
    class ManualProcessingTestLinter:
49
50
        def process_output(self, *args, **kwargs):
51
            pass
52
53
    def setUp(self):
54
        self.section = Section("TEST_SECTION")
55
56
    def test_decorator_invalid_parameters(self):
57
        with self.assertRaises(ValueError) as cm:
58
            linter("some-executable", invalid_arg=88, ABC=2000)
59
        self.assertEqual(
60
            str(cm.exception),
61
            "Invalid keyword arguments provided: 'ABC', 'invalid_arg'")
62
63
        with self.assertRaises(ValueError) as cm:
64
            linter("some-executable", diff_severity=RESULT_SEVERITY.MAJOR)
65
        self.assertEqual(str(cm.exception),
66
                         "Invalid keyword arguments provided: 'diff_severity'")
67
68
        with self.assertRaises(ValueError) as cm:
69
            linter("some-executable", result_message="Custom message")
70
        self.assertEqual(str(cm.exception),
71
                         "Invalid keyword arguments provided: "
72
                         "'result_message'")
73
74
        with self.assertRaises(ValueError) as cm:
75
            linter("some-executable",
76
                   output_format="corrected",
77
                   output_regex=".*")
78
        self.assertEqual(str(cm.exception),
79
                         "Invalid keyword arguments provided: 'output_regex'")
80
81 View Code Duplication
        with self.assertRaises(ValueError) as cm:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
82
            linter("some-executable",
83
                   output_format="corrected",
84
                   severity_map={})
85
        self.assertEqual(str(cm.exception),
86
                         "Invalid keyword arguments provided: 'severity_map'")
87
88
        with self.assertRaises(ValueError) as cm:
89
            linter("some-executable",
90
                   prerequisite_check_fail_message="some_message")
91
        self.assertEqual(str(cm.exception),
92
                         "Invalid keyword arguments provided: "
93
                         "'prerequisite_check_fail_message'")
94
95
    def test_decorator_invalid_states(self):
96
        with self.assertRaises(ValueError) as cm:
97
            linter("some-executable", use_stdout=False, use_stderr=False)
98
        self.assertEqual(str(cm.exception),
99
                         "No output streams provided at all.")
100
101
        with self.assertRaises(ValueError) as cm:
102
            linter("some-executable", output_format="INVALID")
103
        self.assertEqual(str(cm.exception),
104
                         "Invalid `output_format` specified.")
105
106
        with self.assertRaises(ValueError) as cm:
107
            linter("some-executable", output_format="regex")
108
        self.assertEqual(
109
            str(cm.exception),
110
            "`output_regex` needed when specified output-format 'regex'.")
111
112
        with self.assertRaises(ValueError) as cm:
113
            linter("some-executable",
114
                   output_format="regex",
115
                   output_regex="",
116
                   severity_map={})
117
        self.assertEqual(
118
            str(cm.exception),
119
            "Provided `severity_map` but named group `severity` is not used "
120
            "in `output_regex`.")
121
122
        with self.assertRaises(ValueError) as cm:
123
            linter("some-executable")(object)
124
        self.assertEqual(
125
            str(cm.exception),
126
            "`process_output` not provided by given class 'object'.")
127
128
        with self.assertRaises(ValueError) as cm:
129
            (linter("some-executable", output_format="regex", output_regex="")
130
             (self.ManualProcessingTestLinter))
131
        self.assertEqual(
132
            str(cm.exception),
133
            "Found `process_output` already defined by class "
134
            "'ManualProcessingTestLinter', but 'regex' output-format is "
135
            "specified.")
136
137
    def test_decorator_generated_default_interface(self):
138
        uut = linter("some-executable")(self.ManualProcessingTestLinter)
139
        with self.assertRaises(NotImplementedError):
140
            uut.create_arguments("filename", "content", None)
141
142
    def test_decorator_invalid_parameter_types(self):
143
        # Provide some invalid severity maps.
144
        with self.assertRaises(TypeError):
145
            linter("some-executable",
146
                   output_format="regex",
147
                   output_regex="(?P<severity>)",
148
                   severity_map=list())
149
150
        with self.assertRaises(TypeError):
151
            linter("some-executable",
152
                   output_format="regex",
153
                   output_regex="(?P<severity>)",
154
                   severity_map={3: 0})
155
156
        with self.assertRaises(TypeError) as cm:
157
            linter("some-executable",
158
                   output_format="regex",
159
                   output_regex="(?P<severity>)",
160
                   severity_map={"critical": "invalid"})
161
        self.assertEqual(str(cm.exception),
162
                         "The value 'invalid' for key 'critical' inside given "
163
                         "severity-map is no valid severity value.")
164
165
        with self.assertRaises(TypeError) as cm:
166
            linter("some-executable",
167
                   output_format="regex",
168
                   output_regex="(?P<severity>)",
169
                   severity_map={"critical-error": 389274234})
170
        self.assertEqual(str(cm.exception),
171
                         "Invalid severity value 389274234 for key "
172
                         "'critical-error' inside given severity-map.")
173
174
        # Other type-error test cases.
175
176
        with self.assertRaises(TypeError):
177
            linter("some-executable",
178
                   output_format="regex",
179
                   output_regex="(?P<message>)",
180
                   result_message=None)
181
182
        with self.assertRaises(TypeError):
183
            linter("some-executable",
184
                   output_format="corrected",
185
                   result_message=list())
186
187
        with self.assertRaises(TypeError) as cm:
188
            linter("some-executable",
189
                   output_format="corrected",
190
                   diff_severity=999888777)
191
        self.assertEqual(str(cm.exception),
192
                         "Invalid value for `diff_severity`: 999888777")
193
194
        with self.assertRaises(TypeError):
195
            linter("some-executable",
196
                   prerequisite_check_command=("command",),
197
                   prerequisite_check_fail_message=382983)
198
199
    def test_get_executable(self):
200
        uut = linter("some-executable")(self.ManualProcessingTestLinter)
201
        self.assertEqual(uut.get_executable(), "some-executable")
202
203
    def test_check_prerequisites(self):
204
        uut = linter(sys.executable)(self.ManualProcessingTestLinter)
205
        self.assertTrue(uut.check_prerequisites())
206
207
        uut = (linter("invalid_nonexisting_programv412")
208
               (self.ManualProcessingTestLinter))
209
        self.assertEqual(uut.check_prerequisites(),
210
                         "'invalid_nonexisting_programv412' is not installed.")
211
212
        uut = (linter("invalid_nonexisting_programv412",
213
                      executable_check_fail_info="You can't install it.")
214
               (self.ManualProcessingTestLinter))
215
        self.assertEqual(uut.check_prerequisites(),
216
                         "'invalid_nonexisting_programv412' is not installed. "
217
                         "You can't install it.")
218
219
        uut = (linter(sys.executable,
220
                      prerequisite_check_command=(sys.executable, "--version"))
221
               (self.ManualProcessingTestLinter))
222
        self.assertTrue(uut.check_prerequisites())
223
224
        uut = (linter(sys.executable,
225
                      prerequisite_check_command=("invalid_programv413",))
226
               (self.ManualProcessingTestLinter))
227
        self.assertEqual(uut.check_prerequisites(),
228
                         "Prerequisite check failed.")
229
230
        uut = (linter(sys.executable,
231
                      prerequisite_check_command=("invalid_programv413",),
232
                      prerequisite_check_fail_message="NOPE")
233
               (self.ManualProcessingTestLinter))
234
        self.assertEqual(uut.check_prerequisites(), "NOPE")
235
236
    def test_output_stream(self):
237
        process_output_mock = Mock()
238
239
        class TestLinter:
240
241
            @staticmethod
242
            def process_output(output, filename, file):
243
                process_output_mock(output, filename, file)
244
245
            @staticmethod
246
            def create_arguments(filename, file, config_file):
247
                code = "\n".join(["import sys",
248
                                  "print('hello stdout')",
249
                                  "print('hello stderr', file=sys.stderr)"])
250
                return "-c", code
251
252
        uut = (linter(sys.executable, use_stdout=True)
0 ignored issues
show
Bug introduced by
linter(sys.executable, u...tdout=True)(TestLinter) does not seem to be callable.
Loading history...
253
               (TestLinter)
254
               (self.section, None))
255
        uut.run("", [])
256
257
        process_output_mock.assert_called_once_with("hello stdout\n", "", [])
258
        process_output_mock.reset_mock()
259
260
        uut = (linter(sys.executable, use_stdout=False, use_stderr=True)
0 ignored issues
show
Bug introduced by
linter(sys.executable, u...tderr=True)(TestLinter) does not seem to be callable.
Loading history...
261
               (TestLinter)
262
               (self.section, None))
263
        uut.run("", [])
264
265
        process_output_mock.assert_called_once_with("hello stderr\n", "", [])
266
        process_output_mock.reset_mock()
267
268
        uut = (linter(sys.executable, use_stdout=True, use_stderr=True)
0 ignored issues
show
Bug introduced by
linter(sys.executable, u...tderr=True)(TestLinter) does not seem to be callable.
Loading history...
269
               (TestLinter)
270
               (self.section, None))
271
272
        uut.run("", [])
273
274
        process_output_mock.assert_called_once_with(("hello stdout\n",
275
                                                     "hello stderr\n"), "", [])
276
277
    def test_process_output_corrected(self):
278
        uut = (linter(sys.executable, output_format="corrected")
0 ignored issues
show
Bug introduced by
linter(sys.executable, o...)(self.EmptyTestLinter) does not seem to be callable.
Loading history...
279
               (self.EmptyTestLinter)
280
               (self.section, None))
281
282
        original = ["void main()  {\n", "return 09;\n", "}\n"]
283
        fixed = ["void main()\n", "{\n", "return 9;\n", "}\n"]
284
        fixed_string = "".join(fixed)
285
286
        results = list(uut.process_output(fixed_string,
287
                                          "some-file.c",
288
                                          original))
289
290
        diffs = list(Diff.from_string_arrays(original, fixed).split_diff())
291
        expected = [Result.from_values(uut,
292
                                       "Inconsistency found.",
293
                                       "some-file.c",
294
                                       1, None, 2, None,
295
                                       RESULT_SEVERITY.NORMAL,
296
                                       diffs={"some-file.c": diffs[0]})]
297
298
        self.assertEqual(results, expected)
299
300
        # Test when providing a sequence as output.
301
302
        results = list(uut.process_output([fixed_string, fixed_string],
303
                                          "some-file.c",
304
                                          original))
305
        self.assertEqual(results, 2 * expected)
306
307
        # Test diff_distance
308
309
        uut = (linter(sys.executable,
0 ignored issues
show
Bug introduced by
linter(sys.executable, o...)(self.EmptyTestLinter) does not seem to be callable.
Loading history...
310
                      output_format="corrected",
311
                      diff_distance=-1)
312
               (self.EmptyTestLinter)
313
               (self.section, None))
314
315
        results = list(uut.process_output(fixed_string,
316
                                          "some-file.c",
317
                                          original))
318
        self.assertEqual(len(results), 2)
319
320
    def test_process_output_regex(self):
321
        # Also test the case when an unknown severity is matched.
322
        test_output = ("12:4-14:0-Serious issue (error) -> ORIGIN=X -> D\n"
323
                       "0:0-0:1-This is a warning (warning) -> ORIGIN=Y -> A\n"
324
                       "813:77-1024:32-Just a note (info) -> ORIGIN=Z -> C\n"
325
                       "0:0-0:0-Some unknown sev (???) -> ORIGIN=W -> B\n")
326
        regex = (r"(?P<line>\d+):(?P<column>\d+)-"
327
                 r"(?P<end_line>\d+):(?P<end_column>\d+)-"
328
                 r"(?P<message>.*) \((?P<severity>.*)\) -> "
329
                 r"ORIGIN=(?P<origin>.*) -> (?P<additional_info>.*)")
330
331
        uut = (linter(sys.executable,
0 ignored issues
show
Bug introduced by
linter(sys.executable, o...)(self.EmptyTestLinter) does not seem to be callable.
Loading history...
332
                      output_format="regex",
333
                      output_regex=regex)
334
               (self.EmptyTestLinter)
335
               (self.section, None))
336
        uut.warn = Mock()
337
338
        sample_file = "some-file.xtx"
339
        results = list(uut.process_output(test_output, sample_file, [""]))
340
        expected = [Result.from_values("EmptyTestLinter (X)",
341
                                       "Serious issue",
342
                                       sample_file,
343
                                       12, 4, 14, 0,
344
                                       RESULT_SEVERITY.MAJOR,
345
                                       additional_info="D"),
346
                    Result.from_values("EmptyTestLinter (Y)",
347
                                       "This is a warning",
348
                                       sample_file,
349
                                       0, 0, 0, 1,
350
                                       RESULT_SEVERITY.NORMAL,
351
                                       additional_info="A"),
352
                    Result.from_values("EmptyTestLinter (Z)",
353
                                       "Just a note",
354
                                       sample_file,
355
                                       813, 77, 1024, 32,
356
                                       RESULT_SEVERITY.INFO,
357
                                       additional_info="C"),
358
                    Result.from_values("EmptyTestLinter (W)",
359
                                       "Some unknown sev",
360
                                       sample_file,
361
                                       0, 0, 0, 0,
362
                                       RESULT_SEVERITY.NORMAL,
363
                                       additional_info="B")]
364
365
        self.assertEqual(results, expected)
366
        uut.warn.assert_called_once_with(
367
            "'???' not found in severity-map. Assuming "
368
            "`RESULT_SEVERITY.NORMAL`.")
369
370
        # Test when providing a sequence as output.
371
        test_output = ["",
372
                       "12:4-14:0-Serious issue (error) -> ORIGIN=X -> XYZ\n"]
373
        results = list(uut.process_output(test_output, sample_file, [""]))
374
        expected = [Result.from_values("EmptyTestLinter (X)",
375
                                       "Serious issue",
376
                                       sample_file,
377
                                       12, 4, 14, 0,
378
                                       RESULT_SEVERITY.MAJOR,
379
                                       additional_info="XYZ")]
380
381
        self.assertEqual(results, expected)
382
383
        # Test with using `result_message` parameter.
384
        uut = (linter(sys.executable,
0 ignored issues
show
Bug introduced by
linter(sys.executable, o...)(self.EmptyTestLinter) does not seem to be callable.
Loading history...
385
                      output_format="regex",
386
                      output_regex=regex,
387
                      result_message="Hello world")
388
               (self.EmptyTestLinter)
389
               (self.section, None))
390
391
        results = list(uut.process_output(test_output, sample_file, [""]))
392
        expected = [Result.from_values("EmptyTestLinter (X)",
393
                                       "Hello world",
394
                                       sample_file,
395
                                       12, 4, 14, 0,
396
                                       RESULT_SEVERITY.MAJOR,
397
                                       additional_info="XYZ")]
398
399
        self.assertEqual(results, expected)
400
401
    def test_minimal_regex(self):
402
        uut = (linter(sys.executable,
0 ignored issues
show
Bug introduced by
linter(sys.executable, o...)(self.EmptyTestLinter) does not seem to be callable.
Loading history...
403
                      output_format="regex",
404
                      output_regex="an_issue")
405
               (self.EmptyTestLinter)
406
               (self.section, None))
407
408
        results = list(uut.process_output(['not an issue'], 'file', [""]))
409
        self.assertEqual(results, [])
410
411
        results = list(uut.process_output(['an_issue'], 'file', [""]))
412
        self.assertEqual(results, [Result.from_values("EmptyTestLinter", "",
413
                                                      file="file")])
414
415
    def test_get_non_optional_settings(self):
416
        class Handler(self.ManualProcessingTestLinter):
417
418
            @staticmethod
419
            def create_arguments(filename, file, config_file, param_x: int):
420
                pass
421
422
            @staticmethod
423
            def generate_config(filename, file, superparam):
424
                """
425
                :param superparam: A superparam!
426
                """
427
                return None
428
429
        uut = linter(sys.executable)(Handler)
430
431
        self.assertEqual(uut.get_non_optional_settings(),
432
                         {"param_x": ("No description given.", int),
433
                          "superparam": ("A superparam!", None)})
434
435
    def test_process_output_metadata_omits_on_builtin_formats(self):
436
        uut = (linter(executable='', output_format='corrected')
437
               (self.EmptyTestLinter))
438
        # diff_severity and result_message should now not occur inside the
439
        # metadata definition.
440
        self.assertNotIn("diff_severity", uut.get_metadata().optional_params)
441
        self.assertNotIn("result_message", uut.get_metadata().optional_params)
442
        self.assertNotIn("diff_severity",
443
                         uut.get_metadata().non_optional_params)
444
        self.assertNotIn("result_message",
445
                         uut.get_metadata().non_optional_params)
446
447
        # But every parameter manually defined in process_output shall appear
448
        # inside the metadata signature.
449
        class Handler:
450
451
            @staticmethod
452
            def create_arguments(filename, file, config_file):
453
                pass
454
455
            @staticmethod
456
            def process_output(output, filename, file, diff_severity):
457
                pass
458
459
        uut = linter(executable='')(Handler)
460
        self.assertIn("diff_severity", uut.get_metadata().non_optional_params)
461
462
    def test_section_settings_forwarding(self):
463
        create_arguments_mock = Mock()
464
        generate_config_mock = Mock()
465
        process_output_mock = Mock()
466
467
        class Handler(self.ManualProcessingTestLinter):
468
469
            @staticmethod
470
            def create_arguments(filename, file, config_file, my_param: int):
471
                create_arguments_mock(filename, file, config_file, my_param)
472
                # Execute python and do nothing.
473
                return "-c", "print('coala!')"
474
475
            @staticmethod
476
            def generate_config(filename, file, my_config_param: int):
477
                generate_config_mock(filename, file, my_config_param)
478
                return None
479
480
            def process_output(self, output, filename, file, makman2: str):
481
                process_output_mock(output, filename, file, makman2)
482
483
        self.section["my_param"] = "109"
484
        self.section["my_config_param"] = "88"
485
        self.section["makman2"] = "is cool"
486
487
        uut = linter(sys.executable)(Handler)(self.section, None)
488
489
        self.assertIsNotNone(list(uut.execute(filename="some_file.cs",
490
                                              file=[])))
491
        create_arguments_mock.assert_called_once_with(
492
            "some_file.cs", [], None, 109)
493
        generate_config_mock.assert_called_once_with("some_file.cs", [], 88)
494
        process_output_mock.assert_called_once_with(
495
            "coala!\n", "some_file.cs", [], "is cool")
496
497
    def test_section_settings_defaults_forwarding(self):
498
        create_arguments_mock = Mock()
499
        generate_config_mock = Mock()
500
        process_output_mock = Mock()
501
502
        class Handler:
503
504
            @staticmethod
505
            def generate_config(filename, file, some_default: str="x"):
506
                generate_config_mock(filename, file, some_default)
507
                return None
508
509
            @staticmethod
510
            def create_arguments(filename, file, config_file, default: int=3):
511
                create_arguments_mock(
512
                    filename, file, config_file, default)
513
                return "-c", "print('hello')"
514
515
            @staticmethod
516
            def process_output(output, filename, file, xxx: int=64):
517
                process_output_mock(output, filename, file, xxx)
518
519
        uut = linter(sys.executable)(Handler)(self.section, None)
520
521
        self.assertIsNotNone(list(uut.execute(filename="abc.py", file=[])))
522
        create_arguments_mock.assert_called_once_with("abc.py", [], None, 3)
523
        generate_config_mock.assert_called_once_with("abc.py", [], "x")
524
        process_output_mock.assert_called_once_with(
525
            "hello\n", "abc.py", [], 64)
526
527
        create_arguments_mock.reset_mock()
528
        generate_config_mock.reset_mock()
529
        process_output_mock.reset_mock()
530
531
        self.section["default"] = "1000"
532
        self.section["some_default"] = "xyz"
533
        self.section["xxx"] = "-50"
534
        self.assertIsNotNone(list(uut.execute(filename="def.py", file=[])))
535
        create_arguments_mock.assert_called_once_with("def.py", [], None, 1000)
536
        generate_config_mock.assert_called_once_with("def.py", [], "xyz")
537
        process_output_mock.assert_called_once_with(
538
            "hello\n", "def.py", [], -50)
539
540
    def test_invalid_arguments(self):
541
542
        class InvalidArgumentsLinter(self.ManualProcessingTestLinter):
543
544
            @staticmethod
545
            def create_arguments(filename, file, config_file):
546
                return None
547
548
        uut = (linter(sys.executable)(InvalidArgumentsLinter)
0 ignored issues
show
Bug introduced by
linter(sys.executable)(InvalidArgumentsLinter) does not seem to be callable.
Loading history...
549
               (self.section, None))
550
        self.assertEqual(uut.run("", []), None)
551
552
    def test_generate_config(self):
553
        uut = linter("")(self.ManualProcessingTestLinter)
554
        with uut._create_config("filename", []) as config_file:
555
            self.assertIsNone(config_file)
556
557
        class ConfigurationTestLinter(self.ManualProcessingTestLinter):
558
559
            @staticmethod
560
            def generate_config(filename, file, val):
561
                return "config_value = " + str(val)
562
563
        uut = linter("", config_suffix=".xml")(ConfigurationTestLinter)
564
        with uut._create_config("filename", [], val=88) as config_file:
565
            self.assertTrue(os.path.isfile(config_file))
566
            self.assertEqual(config_file[-4:], ".xml")
567
            with open(config_file, mode="r") as fl:
568
                self.assertEqual(fl.read(), "config_value = 88")
569
        self.assertFalse(os.path.isfile(config_file))
570
571
    def test_metaclass_repr(self):
572
        uut = linter("my-tool")(self.ManualProcessingTestLinter)
573
        self.assertEqual(
574
            repr(uut),
575
            "<ManualProcessingTestLinter linter class (wrapping 'my-tool')>")
576
577
        # Test also whether derivatives change the class name accordingly.
578
        class DerivedLinter(uut):
579
            pass
580
        self.assertEqual(repr(DerivedLinter),
581
                         "<DerivedLinter linter class (wrapping 'my-tool')>")
582
583
    def test_repr(self):
584
        uut = (linter(sys.executable)
0 ignored issues
show
Bug introduced by
linter(sys.executable)(s...alProcessingTestLinter) does not seem to be callable.
Loading history...
585
               (self.ManualProcessingTestLinter)
586
               (self.section, None))
587
588
        self.assertRegex(
589
            repr(uut),
590
            "<ManualProcessingTestLinter linter object \\(wrapping " +
591
            re.escape(repr(sys.executable)) + "\\) at 0x[a-fA-F0-9]+>")
592
593
    @skipIf(platform.system() == "Windows",
594
            "Nobody can sanely test things on windows")
595
    def test_process_directory(self):
596
        """
597
        The linter shall run the process in the right directory so tools can
598
        use the current working directory to resolve import like things.
599
        """
600
        uut = (linter("pwd")
0 ignored issues
show
Bug introduced by
linter('pwd')(self.RootDirTestLinter) does not seem to be callable.
Loading history...
601
               (self.RootDirTestLinter)
602
               (self.section, None))
603
        uut.run('', [])  # Does an assert in the output processing
604
605
606
class LinterReallifeTest(unittest.TestCase):
607
608
    def setUp(self):
609
        self.section = Section("REALLIFE_TEST_SECTION")
610
611
        self.test_program_path = get_testfile_name("test_linter.py")
612
        self.test_program_regex = (
613
            r"L(?P<line>\d+)C(?P<column>\d+)-"
614
            r"L(?P<end_line>\d+)C(?P<end_column>\d+):"
615
            r" (?P<message>.*) \| (?P<severity>.+) SEVERITY")
616
        self.test_program_severity_map = {"MAJOR": RESULT_SEVERITY.MAJOR}
617
618
        self.testfile_path = get_testfile_name("test_file.txt")
619
        with open(self.testfile_path, mode="r") as fl:
620
            self.testfile_content = fl.read().splitlines(keepends=True)
621
622
        self.testfile2_path = get_testfile_name("test_file2.txt")
623
        with open(self.testfile2_path, mode="r") as fl:
624
            self.testfile2_content = fl.read().splitlines(keepends=True)
625
626
    def test_nostdin_nostderr_noconfig_nocorrection(self):
627
        create_arguments_mock = Mock()
628
629
        class Handler:
630
631
            @staticmethod
632
            def create_arguments(filename, file, config_file):
633
                create_arguments_mock(filename, file, config_file)
634
                return self.test_program_path, filename
635
636
        uut = (linter(sys.executable,
0 ignored issues
show
Bug introduced by
linter(sys.executable, o..._severity_map)(Handler) does not seem to be callable.
Loading history...
637
                      output_format="regex",
638
                      output_regex=self.test_program_regex,
639
                      severity_map=self.test_program_severity_map)
640
               (Handler)
641
               (self.section, None))
642
643
        results = list(uut.run(self.testfile_path, self.testfile_content))
644
        expected = [Result.from_values(uut,
645
                                       "Invalid char ('0')",
646
                                       self.testfile_path,
647
                                       3, 0, 3, 1,
648
                                       RESULT_SEVERITY.MAJOR),
649
                    Result.from_values(uut,
650
                                       "Invalid char ('.')",
651
                                       self.testfile_path,
652
                                       5, 0, 5, 1,
653
                                       RESULT_SEVERITY.MAJOR),
654
                    Result.from_values(uut,
655
                                       "Invalid char ('p')",
656
                                       self.testfile_path,
657
                                       9, 0, 9, 1,
658
                                       RESULT_SEVERITY.MAJOR)]
659
660
        self.assertEqual(results, expected)
661
        create_arguments_mock.assert_called_once_with(
662
            self.testfile_path, self.testfile_content, None)
663
664
    def test_stdin_stderr_noconfig_nocorrection(self):
665
        create_arguments_mock = Mock()
666
667
        class Handler:
668
669
            @staticmethod
670
            def create_arguments(filename, file, config_file):
671
                create_arguments_mock(filename, file, config_file)
672
                return (self.test_program_path,
673
                        "--use_stderr",
674
                        "--use_stdin",
675
                        filename)
676
677
        uut = (linter(sys.executable,
0 ignored issues
show
Bug introduced by
linter(sys.executable, u..._severity_map)(Handler) does not seem to be callable.
Loading history...
678
                      use_stdin=True,
679
                      use_stdout=False,
680
                      use_stderr=True,
681
                      output_format="regex",
682
                      output_regex=self.test_program_regex,
683
                      severity_map=self.test_program_severity_map)
684
               (Handler)
685
               (self.section, None))
686
687
        results = list(uut.run(self.testfile2_path, self.testfile2_content))
688
        expected = [Result.from_values(uut,
689
                                       "Invalid char ('X')",
690
                                       self.testfile2_path,
691
                                       0, 0, 0, 1,
692
                                       RESULT_SEVERITY.MAJOR),
693
                    Result.from_values(uut,
694
                                       "Invalid char ('i')",
695
                                       self.testfile2_path,
696
                                       4, 0, 4, 1,
697
                                       RESULT_SEVERITY.MAJOR)]
698
699
        self.assertEqual(results, expected)
700
        create_arguments_mock.assert_called_once_with(
701
            self.testfile2_path, self.testfile2_content, None)
702
703
    def test_nostdin_nostderr_noconfig_correction(self):
704
        create_arguments_mock = Mock()
705
706
        class Handler:
707
708
            @staticmethod
709
            def create_arguments(filename, file, config_file):
710
                create_arguments_mock(filename, file, config_file)
711
                return self.test_program_path, "--correct", filename
712
713
        uut = (linter(sys.executable,
0 ignored issues
show
Bug introduced by
linter(sys.executable, o...stom message')(Handler) does not seem to be callable.
Loading history...
714
                      output_format="corrected",
715
                      diff_severity=RESULT_SEVERITY.INFO,
716
                      result_message="Custom message")
717
               (Handler)
718
               (self.section, None))
719
720
        results = list(uut.run(self.testfile_path, self.testfile_content))
721
722
        expected_correction = [s + "\n"
723
                               for s in ["+", "-", "*", "++", "-", "-", "+"]]
724
725
        diffs = list(Diff.from_string_arrays(
726
            self.testfile_content,
727
            expected_correction).split_diff())
728
729
        expected = [Result(uut, "Custom message",
730
                           affected_code=(
731
                               SourceRange.from_values(self.testfile_path, 4),
732
                               SourceRange.from_values(self.testfile_path, 6)),
733
                           severity=RESULT_SEVERITY.INFO,
734
                           diffs={self.testfile_path: diffs[0]}),
735
                    Result.from_values(uut,
736
                                       "Custom message",
737
                                       self.testfile_path,
738
                                       10, None, 10, None,
739
                                       RESULT_SEVERITY.INFO,
740
                                       diffs={self.testfile_path: diffs[1]})]
741
742
        self.assertEqual(results, expected)
743
        create_arguments_mock.assert_called_once_with(
744
            self.testfile_path, self.testfile_content, None)
745
746
    def test_stdin_stdout_stderr_config_nocorrection(self):
747
        create_arguments_mock = Mock()
748
        generate_config_mock = Mock()
749
750
        class Handler:
751
752
            @staticmethod
753
            def generate_config(filename, file, some_val):
754
                # some_val shall only test the argument delegation from run().
755
                generate_config_mock(filename, file, some_val)
756
                return "\n".join(["use_stdin", "use_stderr"])
757
758
            @staticmethod
759
            def create_arguments(filename, file, config_file, some_val):
760
                create_arguments_mock(filename, file, config_file, some_val)
761
                return self.test_program_path, "--config", config_file
762
763
        uut = (linter(sys.executable,
0 ignored issues
show
Bug introduced by
linter(sys.executable, u...ar provided!')(Handler) does not seem to be callable.
Loading history...
764
                      use_stdin=True,
765
                      use_stderr=True,
766
                      output_format="regex",
767
                      output_regex=self.test_program_regex,
768
                      severity_map=self.test_program_severity_map,
769
                      result_message="Invalid char provided!")
770
               (Handler)
771
               (self.section, None))
772
773
        results = list(uut.run(self.testfile_path,
774
                               self.testfile_content,
775
                               some_val=33))
776
        expected = [Result.from_values(uut,
777
                                       "Invalid char provided!",
778
                                       self.testfile_path,
779
                                       3, 0, 3, 1,
780
                                       RESULT_SEVERITY.MAJOR),
781
                    Result.from_values(uut,
782
                                       "Invalid char provided!",
783
                                       self.testfile_path,
784
                                       5, 0, 5, 1,
785
                                       RESULT_SEVERITY.MAJOR),
786
                    Result.from_values(uut,
787
                                       "Invalid char provided!",
788
                                       self.testfile_path,
789
                                       9, 0, 9, 1,
790
                                       RESULT_SEVERITY.MAJOR)]
791
792
        self.assertEqual(results, expected)
793
        create_arguments_mock.assert_called_once_with(
794
            self.testfile_path, self.testfile_content, ANY, 33)
795
        self.assertIsNotNone(create_arguments_mock.call_args[0][2])
796
        generate_config_mock.assert_called_once_with(
797
            self.testfile_path, self.testfile_content, 33)
798
799
    def test_stdin_stderr_config_correction(self):
800
        create_arguments_mock = Mock()
801
        generate_config_mock = Mock()
802
803
        # `some_value_A` and `some_value_B` are used to test the different
804
        # delegation to `generate_config()` and `create_arguments()`
805
        # accordingly.
806
        class Handler:
807
808
            @staticmethod
809
            def generate_config(filename, file, some_value_A):
810
                generate_config_mock(filename, file, some_value_A)
811
                return "\n".join(["use_stdin", "use_stderr", "correct"])
812
813
            @staticmethod
814
            def create_arguments(filename, file, config_file, some_value_B):
815
                create_arguments_mock(filename, file, config_file,
816
                                      some_value_B)
817
                return self.test_program_path, "--config", config_file
818
819
        uut = (linter(sys.executable,
0 ignored issues
show
Bug introduced by
linter(sys.executable, u...uffix='.conf')(Handler) does not seem to be callable.
Loading history...
820
                      use_stdin=True,
821
                      use_stdout=False,
822
                      use_stderr=True,
823
                      output_format="corrected",
824
                      config_suffix=".conf")
825
               (Handler)
826
               (self.section, None))
827
828
        results = list(uut.run(self.testfile2_path,
829
                               self.testfile2_content,
830
                               some_value_A=124,
831
                               some_value_B=-78))
832
833
        expected_correction = [s + "\n" for s in ["+", "/", "/", "-"]]
834
835
        diffs = list(Diff.from_string_arrays(
836
            self.testfile2_content,
837
            expected_correction).split_diff())
838
839
        expected = [Result.from_values(uut,
840
                                       "Inconsistency found.",
841
                                       self.testfile2_path,
842
                                       1, None, 1, None,
843
                                       RESULT_SEVERITY.NORMAL,
844
                                       diffs={self.testfile2_path: diffs[0]}),
845
                    Result.from_values(uut,
846
                                       "Inconsistency found.",
847
                                       self.testfile2_path,
848
                                       5, None, 5, None,
849
                                       RESULT_SEVERITY.NORMAL,
850
                                       diffs={self.testfile2_path: diffs[1]})]
851
852
        self.assertEqual(results, expected)
853
        create_arguments_mock.assert_called_once_with(
854
            self.testfile2_path, self.testfile2_content, ANY, -78)
855
        self.assertEqual(create_arguments_mock.call_args[0][2][-5:], ".conf")
856
        generate_config_mock.assert_called_once_with(
857
            self.testfile2_path, self.testfile2_content, 124)
858