Completed
Pull Request — master (#2558)
by Lasse
03:16 queued 01:23
created

RootDirTestLinter.get_config_dir()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 1
Ratio 50 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 1
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 "
46
                                         "in 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(
258
            ("hello stdout\n", ), "", [])
259
        process_output_mock.reset_mock()
260
261
        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...
262
               (TestLinter)
263
               (self.section, None))
264
        uut.run("", [])
265
266
        process_output_mock.assert_called_once_with(
267
            ("hello stderr\n", ), "", [])
268
        process_output_mock.reset_mock()
269
270
        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...
271
               (TestLinter)
272
               (self.section, None))
273
274
        uut.run("", [])
275
276
        process_output_mock.assert_called_once_with(("hello stdout\n",
277
                                                     "hello stderr\n"), "", [])
278
279
    def test_process_output_corrected(self):
280
        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...
281
               (self.EmptyTestLinter)
282
               (self.section, None))
283
284
        original = ["void main()  {\n", "return 09;\n", "}\n"]
285
        fixed = ["void main()\n", "{\n", "return 9;\n", "}\n"]
286
        fixed_string = "".join(fixed)
287
288
        results = list(uut.process_output((fixed_string, ),
289
                                          "some-file.c",
290
                                          original))
291
292
        diffs = list(Diff.from_string_arrays(original, fixed).split_diff())
293
        expected = [Result.from_values(uut,
294
                                       "Inconsistency found.",
295
                                       "some-file.c",
296
                                       1, None, 2, None,
297
                                       RESULT_SEVERITY.NORMAL,
298
                                       diffs={"some-file.c": diffs[0]})]
299
300
        self.assertEqual(results, expected)
301
302
        # Test when providing a sequence as output.
303
304
        results = list(uut.process_output([fixed_string, fixed_string],
305
                                          "some-file.c",
306
                                          original))
307
        self.assertEqual(results, 2 * expected)
308
309
        # Test diff_distance
310
311
        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...
312
                      output_format="corrected",
313
                      diff_distance=-1)
314
               (self.EmptyTestLinter)
315
               (self.section, None))
316
317
        results = list(uut.process_output((fixed_string, ),
318
                                          "some-file.c",
319
                                          original))
320
        self.assertEqual(len(results), 2)
321
322
    def test_process_output_regex(self):
323
        # Also test the case when an unknown severity is matched.
324
        test_output = ("12:4-14:0-Serious issue (error) -> ORIGIN=X -> D\n"
325
                       "0:0-0:1-This is a warning (warning) -> ORIGIN=Y -> A\n"
326
                       "813:77-1024:32-Just a note (info) -> ORIGIN=Z -> C\n"
327
                       "0:0-0:0-Some unknown sev (???) -> ORIGIN=W -> B\n", )
328
        regex = (r"(?P<line>\d+):(?P<column>\d+)-"
329
                 r"(?P<end_line>\d+):(?P<end_column>\d+)-"
330
                 r"(?P<message>.*) \((?P<severity>.*)\) -> "
331
                 r"ORIGIN=(?P<origin>.*) -> (?P<additional_info>.*)")
332
333
        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...
334
                      output_format="regex",
335
                      output_regex=regex)
336
               (self.EmptyTestLinter)
337
               (self.section, None))
338
        uut.warn = Mock()
339
340
        sample_file = "some-file.xtx"
341
        results = list(uut.process_output(test_output, sample_file, [""]))
342
        expected = [Result.from_values("EmptyTestLinter (X)",
343
                                       "Serious issue",
344
                                       sample_file,
345
                                       12, 4, 14, 0,
346
                                       RESULT_SEVERITY.MAJOR,
347
                                       additional_info="D"),
348
                    Result.from_values("EmptyTestLinter (Y)",
349
                                       "This is a warning",
350
                                       sample_file,
351
                                       0, 0, 0, 1,
352
                                       RESULT_SEVERITY.NORMAL,
353
                                       additional_info="A"),
354
                    Result.from_values("EmptyTestLinter (Z)",
355
                                       "Just a note",
356
                                       sample_file,
357
                                       813, 77, 1024, 32,
358
                                       RESULT_SEVERITY.INFO,
359
                                       additional_info="C"),
360
                    Result.from_values("EmptyTestLinter (W)",
361
                                       "Some unknown sev",
362
                                       sample_file,
363
                                       0, 0, 0, 0,
364
                                       RESULT_SEVERITY.NORMAL,
365
                                       additional_info="B")]
366
367
        self.assertEqual(results, expected)
368
        uut.warn.assert_called_once_with(
369
            "'???' not found in severity-map. Assuming "
370
            "`RESULT_SEVERITY.NORMAL`.")
371
372
        # Test when providing a sequence as output.
373
        test_output = ["",
374
                       "12:4-14:0-Serious issue (error) -> ORIGIN=X -> XYZ\n"]
375
        results = list(uut.process_output(test_output, sample_file, [""]))
376
        expected = [Result.from_values("EmptyTestLinter (X)",
377
                                       "Serious issue",
378
                                       sample_file,
379
                                       12, 4, 14, 0,
380
                                       RESULT_SEVERITY.MAJOR,
381
                                       additional_info="XYZ")]
382
383
        self.assertEqual(results, expected)
384
385
        # Test with using `result_message` parameter.
386
        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...
387
                      output_format="regex",
388
                      output_regex=regex,
389
                      result_message="Hello world")
390
               (self.EmptyTestLinter)
391
               (self.section, None))
392
393
        results = list(uut.process_output(test_output, sample_file, [""]))
394
        expected = [Result.from_values("EmptyTestLinter (X)",
395
                                       "Hello world",
396
                                       sample_file,
397
                                       12, 4, 14, 0,
398
                                       RESULT_SEVERITY.MAJOR,
399
                                       additional_info="XYZ")]
400
401
        self.assertEqual(results, expected)
402
403
    def test_minimal_regex(self):
404
        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...
405
                      output_format="regex",
406
                      output_regex="an_issue")
407
               (self.EmptyTestLinter)
408
               (self.section, None))
409
410
        results = list(uut.process_output(['not an issue'], 'file', [""]))
411
        self.assertEqual(results, [])
412
413
        results = list(uut.process_output(['an_issue'], 'file', [""]))
414
        self.assertEqual(results, [Result.from_values("EmptyTestLinter", "",
415
                                                      file="file")])
416
417
    def test_get_non_optional_settings(self):
418
        class Handler(self.ManualProcessingTestLinter):
419
420
            @staticmethod
421
            def create_arguments(filename, file, config_file, param_x: int):
422
                pass
423
424
            @staticmethod
425
            def generate_config(filename, file, superparam):
426
                """
427
                :param superparam: A superparam!
428
                """
429
                return None
430
431
        uut = linter(sys.executable)(Handler)
432
433
        self.assertEqual(uut.get_non_optional_settings(),
434
                         {"param_x": ("No description given.", int),
435
                          "superparam": ("A superparam!", None)})
436
437
    def test_process_output_metadata_omits_on_builtin_formats(self):
438
        uut = (linter(executable='', output_format='corrected')
439
               (self.EmptyTestLinter))
440
        # diff_severity and result_message should now not occur inside the
441
        # metadata definition.
442
        self.assertNotIn("diff_severity", uut.get_metadata().optional_params)
443
        self.assertNotIn("result_message", uut.get_metadata().optional_params)
444
        self.assertNotIn("diff_severity",
445
                         uut.get_metadata().non_optional_params)
446
        self.assertNotIn("result_message",
447
                         uut.get_metadata().non_optional_params)
448
449
        # But every parameter manually defined in process_output shall appear
450
        # inside the metadata signature.
451
        class Handler:
452
453
            @staticmethod
454
            def create_arguments(filename, file, config_file):
455
                pass
456
457
            @staticmethod
458
            def process_output(output, filename, file, diff_severity):
459
                pass
460
461
        uut = linter(executable='')(Handler)
462
        self.assertIn("diff_severity", uut.get_metadata().non_optional_params)
463
464
    def test_section_settings_forwarding(self):
465
        create_arguments_mock = Mock()
466
        generate_config_mock = Mock()
467
        process_output_mock = Mock()
468
469
        class Handler(self.ManualProcessingTestLinter):
470
471
            @staticmethod
472
            def create_arguments(filename, file, config_file, my_param: int):
473
                create_arguments_mock(filename, file, config_file, my_param)
474
                # Execute python and do nothing.
475
                return "-c", "print('coala!')"
476
477
            @staticmethod
478
            def generate_config(filename, file, my_config_param: int):
479
                generate_config_mock(filename, file, my_config_param)
480
                return None
481
482
            def process_output(self, output, filename, file, makman2: str):
483
                process_output_mock(output, filename, file, makman2)
484
485
        self.section["my_param"] = "109"
486
        self.section["my_config_param"] = "88"
487
        self.section["makman2"] = "is cool"
488
489
        uut = linter(sys.executable)(Handler)(self.section, None)
490
491
        self.assertIsNotNone(list(uut.execute(filename="some_file.cs",
492
                                              file=[])))
493
        create_arguments_mock.assert_called_once_with(
494
            "some_file.cs", [], None, 109)
495
        generate_config_mock.assert_called_once_with("some_file.cs", [], 88)
496
        process_output_mock.assert_called_once_with(
497
            ("coala!\n", ), "some_file.cs", [], "is cool")
498
499
    def test_section_settings_defaults_forwarding(self):
500
        create_arguments_mock = Mock()
501
        generate_config_mock = Mock()
502
        process_output_mock = Mock()
503
504
        class Handler:
505
506
            @staticmethod
507
            def generate_config(filename, file, some_default: str="x"):
508
                generate_config_mock(filename, file, some_default)
509
                return None
510
511
            @staticmethod
512
            def create_arguments(filename, file, config_file, default: int=3):
513
                create_arguments_mock(
514
                    filename, file, config_file, default)
515
                return "-c", "print('hello')"
516
517
            @staticmethod
518
            def process_output(output, filename, file, xxx: int=64):
519
                process_output_mock(output, filename, file, xxx)
520
521
        uut = linter(sys.executable)(Handler)(self.section, None)
522
523
        self.assertIsNotNone(list(uut.execute(filename="abc.py", file=[])))
524
        create_arguments_mock.assert_called_once_with("abc.py", [], None, 3)
525
        generate_config_mock.assert_called_once_with("abc.py", [], "x")
526
        process_output_mock.assert_called_once_with(
527
            ("hello\n", ), "abc.py", [], 64)
528
529
        create_arguments_mock.reset_mock()
530
        generate_config_mock.reset_mock()
531
        process_output_mock.reset_mock()
532
533
        self.section["default"] = "1000"
534
        self.section["some_default"] = "xyz"
535
        self.section["xxx"] = "-50"
536
        self.assertIsNotNone(list(uut.execute(filename="def.py", file=[])))
537
        create_arguments_mock.assert_called_once_with("def.py", [], None, 1000)
538
        generate_config_mock.assert_called_once_with("def.py", [], "xyz")
539
        process_output_mock.assert_called_once_with(
540
            ("hello\n", ), "def.py", [], -50)
541
542
    def test_invalid_arguments(self):
543
544
        class InvalidArgumentsLinter(self.ManualProcessingTestLinter):
545
546
            @staticmethod
547
            def create_arguments(filename, file, config_file):
548
                return None
549
550
        uut = (linter(sys.executable)(InvalidArgumentsLinter)
0 ignored issues
show
Bug introduced by
linter(sys.executable)(InvalidArgumentsLinter) does not seem to be callable.
Loading history...
551
               (self.section, None))
552
        self.assertEqual(uut.run("", []), None)
553
554
    def test_generate_config(self):
555
        uut = linter("")(self.ManualProcessingTestLinter)
556
        with uut._create_config("filename", []) as config_file:
557
            self.assertIsNone(config_file)
558
559
        class ConfigurationTestLinter(self.ManualProcessingTestLinter):
560
561
            @staticmethod
562
            def generate_config(filename, file, val):
563
                return "config_value = " + str(val)
564
565
        uut = linter("", config_suffix=".xml")(ConfigurationTestLinter)
566
        with uut._create_config("filename", [], val=88) as config_file:
567
            self.assertTrue(os.path.isfile(config_file))
568
            self.assertEqual(config_file[-4:], ".xml")
569
            with open(config_file, mode="r") as fl:
570
                self.assertEqual(fl.read(), "config_value = 88")
571
        self.assertFalse(os.path.isfile(config_file))
572
573
    def test_metaclass_repr(self):
574
        uut = linter("my-tool")(self.ManualProcessingTestLinter)
575
        self.assertEqual(
576
            repr(uut),
577
            "<ManualProcessingTestLinter linter class (wrapping 'my-tool')>")
578
579
        # Test also whether derivatives change the class name accordingly.
580
        class DerivedLinter(uut):
581
            pass
582
        self.assertEqual(repr(DerivedLinter),
583
                         "<DerivedLinter linter class (wrapping 'my-tool')>")
584
585
    def test_repr(self):
586
        uut = (linter(sys.executable)
0 ignored issues
show
Bug introduced by
linter(sys.executable)(s...alProcessingTestLinter) does not seem to be callable.
Loading history...
587
               (self.ManualProcessingTestLinter)
588
               (self.section, None))
589
590
        self.assertRegex(
591
            repr(uut),
592
            "<ManualProcessingTestLinter linter object \\(wrapping " +
593
            re.escape(repr(sys.executable)) + "\\) at 0x[a-fA-F0-9]+>")
594
595
    @skipIf(platform.system() == "Windows",
596
            "Nobody can sanely test things on windows")
597
    def test_process_directory(self):
598
        """
599
        The linter shall run the process in the right directory so tools can
600
        use the current working directory to resolve import like things.
601
        """
602
        uut = (linter("pwd")
0 ignored issues
show
Bug introduced by
linter('pwd')(self.RootDirTestLinter) does not seem to be callable.
Loading history...
603
               (self.RootDirTestLinter)
604
               (self.section, None))
605
        uut.run('', [])  # Does an assert in the output processing
606
607
608
class LinterReallifeTest(unittest.TestCase):
609
610
    def setUp(self):
611
        self.section = Section("REALLIFE_TEST_SECTION")
612
613
        self.test_program_path = get_testfile_name("test_linter.py")
614
        self.test_program_regex = (
615
            r"L(?P<line>\d+)C(?P<column>\d+)-"
616
            r"L(?P<end_line>\d+)C(?P<end_column>\d+):"
617
            r" (?P<message>.*) \| (?P<severity>.+) SEVERITY")
618
        self.test_program_severity_map = {"MAJOR": RESULT_SEVERITY.MAJOR}
619
620
        self.testfile_path = get_testfile_name("test_file.txt")
621
        with open(self.testfile_path, mode="r") as fl:
622
            self.testfile_content = fl.read().splitlines(keepends=True)
623
624
        self.testfile2_path = get_testfile_name("test_file2.txt")
625
        with open(self.testfile2_path, mode="r") as fl:
626
            self.testfile2_content = fl.read().splitlines(keepends=True)
627
628
    def test_nostdin_nostderr_noconfig_nocorrection(self):
629
        create_arguments_mock = Mock()
630
631
        class Handler:
632
633
            @staticmethod
634
            def create_arguments(filename, file, config_file):
635
                create_arguments_mock(filename, file, config_file)
636
                return self.test_program_path, filename
637
638
        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...
639
                      output_format="regex",
640
                      output_regex=self.test_program_regex,
641
                      severity_map=self.test_program_severity_map)
642
               (Handler)
643
               (self.section, None))
644
645
        results = list(uut.run(self.testfile_path, self.testfile_content))
646
        expected = [Result.from_values(uut,
647
                                       "Invalid char ('0')",
648
                                       self.testfile_path,
649
                                       3, 0, 3, 1,
650
                                       RESULT_SEVERITY.MAJOR),
651
                    Result.from_values(uut,
652
                                       "Invalid char ('.')",
653
                                       self.testfile_path,
654
                                       5, 0, 5, 1,
655
                                       RESULT_SEVERITY.MAJOR),
656
                    Result.from_values(uut,
657
                                       "Invalid char ('p')",
658
                                       self.testfile_path,
659
                                       9, 0, 9, 1,
660
                                       RESULT_SEVERITY.MAJOR)]
661
662
        self.assertEqual(results, expected)
663
        create_arguments_mock.assert_called_once_with(
664
            self.testfile_path, self.testfile_content, None)
665
666
    def test_stdin_stderr_noconfig_nocorrection(self):
667
        create_arguments_mock = Mock()
668
669
        class Handler:
670
671
            @staticmethod
672
            def create_arguments(filename, file, config_file):
673
                create_arguments_mock(filename, file, config_file)
674
                return (self.test_program_path,
675
                        "--use_stderr",
676
                        "--use_stdin",
677
                        filename)
678
679
        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...
680
                      use_stdin=True,
681
                      use_stdout=False,
682
                      use_stderr=True,
683
                      output_format="regex",
684
                      output_regex=self.test_program_regex,
685
                      severity_map=self.test_program_severity_map)
686
               (Handler)
687
               (self.section, None))
688
689
        results = list(uut.run(self.testfile2_path, self.testfile2_content))
690
        expected = [Result.from_values(uut,
691
                                       "Invalid char ('X')",
692
                                       self.testfile2_path,
693
                                       0, 0, 0, 1,
694
                                       RESULT_SEVERITY.MAJOR),
695
                    Result.from_values(uut,
696
                                       "Invalid char ('i')",
697
                                       self.testfile2_path,
698
                                       4, 0, 4, 1,
699
                                       RESULT_SEVERITY.MAJOR)]
700
701
        self.assertEqual(results, expected)
702
        create_arguments_mock.assert_called_once_with(
703
            self.testfile2_path, self.testfile2_content, None)
704
705
    def test_nostdin_nostderr_noconfig_correction(self):
706
        create_arguments_mock = Mock()
707
708
        class Handler:
709
710
            @staticmethod
711
            def create_arguments(filename, file, config_file):
712
                create_arguments_mock(filename, file, config_file)
713
                return self.test_program_path, "--correct", filename
714
715
        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...
716
                      output_format="corrected",
717
                      diff_severity=RESULT_SEVERITY.INFO,
718
                      result_message="Custom message")
719
               (Handler)
720
               (self.section, None))
721
722
        results = list(uut.run(self.testfile_path, self.testfile_content))
723
724
        expected_correction = [s + "\n"
725
                               for s in ["+", "-", "*", "++", "-", "-", "+"]]
726
727
        diffs = list(Diff.from_string_arrays(
728
            self.testfile_content,
729
            expected_correction).split_diff())
730
731
        expected = [Result(uut, "Custom message",
732
                           affected_code=(
733
                               SourceRange.from_values(self.testfile_path, 4),
734
                               SourceRange.from_values(self.testfile_path, 6)),
735
                           severity=RESULT_SEVERITY.INFO,
736
                           diffs={self.testfile_path: diffs[0]}),
737
                    Result.from_values(uut,
738
                                       "Custom message",
739
                                       self.testfile_path,
740
                                       10, None, 10, None,
741
                                       RESULT_SEVERITY.INFO,
742
                                       diffs={self.testfile_path: diffs[1]})]
743
744
        self.assertEqual(results, expected)
745
        create_arguments_mock.assert_called_once_with(
746
            self.testfile_path, self.testfile_content, None)
747
748
    def test_stdin_stdout_stderr_config_nocorrection(self):
749
        create_arguments_mock = Mock()
750
        generate_config_mock = Mock()
751
752
        class Handler:
753
754
            @staticmethod
755
            def generate_config(filename, file, some_val):
756
                # some_val shall only test the argument delegation from run().
757
                generate_config_mock(filename, file, some_val)
758
                return "\n".join(["use_stdin", "use_stderr"])
759
760
            @staticmethod
761
            def create_arguments(filename, file, config_file, some_val):
762
                create_arguments_mock(filename, file, config_file, some_val)
763
                return self.test_program_path, "--config", config_file
764
765
        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...
766
                      use_stdin=True,
767
                      use_stderr=True,
768
                      output_format="regex",
769
                      output_regex=self.test_program_regex,
770
                      severity_map=self.test_program_severity_map,
771
                      result_message="Invalid char provided!")
772
               (Handler)
773
               (self.section, None))
774
775
        results = list(uut.run(self.testfile_path,
776
                               self.testfile_content,
777
                               some_val=33))
778
        expected = [Result.from_values(uut,
779
                                       "Invalid char provided!",
780
                                       self.testfile_path,
781
                                       3, 0, 3, 1,
782
                                       RESULT_SEVERITY.MAJOR),
783
                    Result.from_values(uut,
784
                                       "Invalid char provided!",
785
                                       self.testfile_path,
786
                                       5, 0, 5, 1,
787
                                       RESULT_SEVERITY.MAJOR),
788
                    Result.from_values(uut,
789
                                       "Invalid char provided!",
790
                                       self.testfile_path,
791
                                       9, 0, 9, 1,
792
                                       RESULT_SEVERITY.MAJOR)]
793
794
        self.assertEqual(results, expected)
795
        create_arguments_mock.assert_called_once_with(
796
            self.testfile_path, self.testfile_content, ANY, 33)
797
        self.assertIsNotNone(create_arguments_mock.call_args[0][2])
798
        generate_config_mock.assert_called_once_with(
799
            self.testfile_path, self.testfile_content, 33)
800
801
    def test_stdin_stderr_config_correction(self):
802
        create_arguments_mock = Mock()
803
        generate_config_mock = Mock()
804
805
        # `some_value_A` and `some_value_B` are used to test the different
806
        # delegation to `generate_config()` and `create_arguments()`
807
        # accordingly.
808
        class Handler:
809
810
            @staticmethod
811
            def generate_config(filename, file, some_value_A):
812
                generate_config_mock(filename, file, some_value_A)
813
                return "\n".join(["use_stdin", "use_stderr", "correct"])
814
815
            @staticmethod
816
            def create_arguments(filename, file, config_file, some_value_B):
817
                create_arguments_mock(filename, file, config_file,
818
                                      some_value_B)
819
                return self.test_program_path, "--config", config_file
820
821
        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...
822
                      use_stdin=True,
823
                      use_stdout=False,
824
                      use_stderr=True,
825
                      output_format="corrected",
826
                      config_suffix=".conf")
827
               (Handler)
828
               (self.section, None))
829
830
        results = list(uut.run(self.testfile2_path,
831
                               self.testfile2_content,
832
                               some_value_A=124,
833
                               some_value_B=-78))
834
835
        expected_correction = [s + "\n" for s in ["+", "/", "/", "-"]]
836
837
        diffs = list(Diff.from_string_arrays(
838
            self.testfile2_content,
839
            expected_correction).split_diff())
840
841
        expected = [Result.from_values(uut,
842
                                       "Inconsistency found.",
843
                                       self.testfile2_path,
844
                                       1, None, 1, None,
845
                                       RESULT_SEVERITY.NORMAL,
846
                                       diffs={self.testfile2_path: diffs[0]}),
847
                    Result.from_values(uut,
848
                                       "Inconsistency found.",
849
                                       self.testfile2_path,
850
                                       5, None, 5, None,
851
                                       RESULT_SEVERITY.NORMAL,
852
                                       diffs={self.testfile2_path: diffs[1]})]
853
854
        self.assertEqual(results, expected)
855
        create_arguments_mock.assert_called_once_with(
856
            self.testfile2_path, self.testfile2_content, ANY, -78)
857
        self.assertEqual(create_arguments_mock.call_args[0][2][-5:], ".conf")
858
        generate_config_mock.assert_called_once_with(
859
            self.testfile2_path, self.testfile2_content, 124)
860