Completed
Pull Request — master (#1189)
by Lasse
07:35
created

test_print_bears()   A

Complexity

Conditions 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 17
rs 9.4286
1
import tempfile
2
import unittest
3
import sys
4
import os
5
from collections import OrderedDict
6
from pyprint.NullPrinter import NullPrinter
7
from pyprint.ConsolePrinter import ConsolePrinter
8
9
sys.path.insert(0, ".")
10
from coalib.results.result_actions.ResultAction import ResultAction
11
from coalib.results.Result import Result
12
from coalib.results.SourceRange import SourceRange
13
from coalib.results.Diff import Diff
14
from coalib.settings.Section import Section
15
from coalib.settings.Setting import Setting
16
from coalib.misc.ContextManagers import (simulate_console_inputs,
17
                                         retrieve_stdout)
18
from coalib.output.ConsoleInteraction import (nothing_done,
19
                                              acquire_settings,
20
                                              print_bears,
21
                                              get_action_info,
22
                                              print_result,
23
                                              print_section_beginning,
24
                                              print_results,
25
                                              show_bears,
26
                                              print_results_formatted)
27
from coalib.output.printers.LogPrinter import LogPrinter
28
from coalib.output.printers.StringPrinter import StringPrinter
29
from coalib.results.result_actions.ApplyPatchAction import ApplyPatchAction
30
from coalib.results.result_actions.OpenEditorAction import OpenEditorAction
31
from coalib.bears.Bear import Bear
32
from bears.general.KeywordBear import KeywordBear
33
from bears.general.LineLengthBear import LineLengthBear
34
35
36
STR_GET_VAL_FOR_SETTING = ("Please enter a value for the setting \"{}\" ({}) "
37
                           "needed by {}: ")
38
STR_LINE_DOESNT_EXIST = ("The line belonging to the following result "
39
                         "cannot be printed because it refers to a line "
40
                         "that doesn't seem to exist in the given file.")
41
STR_PROJECT_WIDE = "Project wide:"
42
43
44
class TestAction(ResultAction):
45
    def apply(self, result, original_file_dict, file_diff_dict, param):
46
        pass
47
48
49
class TestBear(Bear):
50
    def run(self, setting1, setting2: int=None):
51
        """
52
        Test bear Description.
53
54
        :param setting1: Required Setting.
55
        :param setting2: Optional Setting.
56
        """
57
        return None
58
59
60
class TestBear2(Bear):
61
    def run(self, setting1):
62
        """
63
        Test bear 2 description.
64
65
        :param setting1: Required Setting.
66
        """
67
        return None
68
69
class SomeBear(Bear):
70
    def run(self):
71
        """
72
        Some Description.
73
        """
74
        return None
75
76
77
class SomeOtherBear(Bear):
78
    def run(self, setting: int=None):
79
        """
80
        This is a Bear.
81
        :param setting: This is an optional setting.
82
        """
83
        setting = 1
84
        return None
85
86
87
class SomeglobalBear(Bear):
88
    def run(self):
89
        """
90
        Some Description.
91
        """
92
        return None
93
94
95
class ConsoleInteractionTest(unittest.TestCase):
96
    def setUp(self):
97
        self.log_printer = LogPrinter(ConsolePrinter(print_colored=False))
98
        self.console_printer = ConsolePrinter(print_colored=False)
99
        self.file_diff_dict = {}
100
        self.local_bears = OrderedDict([("default", [KeywordBear]),
101
                                        ("test", [LineLengthBear,
102
                                                  KeywordBear])])
103
        self.global_bears = OrderedDict([("default", [SomeglobalBear]),
104
                                         ("test", [SomeglobalBear])])
105
106
        self.old_open_editor_applicable = OpenEditorAction.is_applicable
107
        OpenEditorAction.is_applicable = staticmethod(lambda *args: False)
108
109
        self.old_apply_patch_applicable = ApplyPatchAction.is_applicable
110
        ApplyPatchAction.is_applicable = staticmethod(lambda *args: False)
111
112
    def tearDown(self):
113
        OpenEditorAction.is_applicable = self.old_open_editor_applicable
114
        ApplyPatchAction.is_applicable = self.old_apply_patch_applicable
115
116
    def test_require_settings(self):
117
        self.assertRaises(TypeError, acquire_settings, self.log_printer, 0)
118
        self.assertEqual(acquire_settings(self.log_printer, {0: 0}), {})
119
120
        with simulate_console_inputs(0, 1, 2) as generator:
121
            self.assertEqual(acquire_settings(self.log_printer,
122
                                              {"setting": ["help text",
123
                                                           "SomeBear"]}),
124
                             {"setting": 0})
125
126
            self.assertEqual(acquire_settings(self.log_printer,
127
                                              {"setting": ["help text",
128
                                                           "SomeBear",
129
                                                           "AnotherBear"]}),
130
                             {"setting": 1})
131
132
            self.assertEqual(acquire_settings(self.log_printer,
133
                                              {"setting": ["help text",
134
                                                           "SomeBear",
135
                                                           "AnotherBear",
136
                                                           "YetAnotherBear"]}),
137
                             {"setting": 2})
138
139
            self.assertEqual(generator.last_input, 2)
140
141
    def test_print_result(self):
142
        print_result(self.console_printer,
143
                     self.log_printer,
144
                     None,
145
                     self.file_diff_dict,
146
                     "illegal value",
147
                     {})
148
149
        with simulate_console_inputs(0):
150
            print_result(self.console_printer,
151
                         self.log_printer,
152
                         None,
153
                         self.file_diff_dict,
154
                         Result("origin", "msg", diffs={}),
155
                         {})
156
157
        (testfile, testfile_path) = tempfile.mkstemp()
158
        os.close(testfile)
159
        file_dict = {
160
            testfile_path: ["1\n", "2\n", "3\n"],
161
            "f_b": ["1", "2", "3"]
162
        }
163
        diff = Diff(file_dict[testfile_path])
164
        diff.delete_line(2)
165
        diff.change_line(3, "3\n", "3_changed\n")
166
167
        with simulate_console_inputs(1), self.assertRaises(ValueError):
168
            ApplyPatchAction.is_applicable = staticmethod(lambda *args: True)
169
            print_result(self.console_printer,
170
                         self.log_printer,
171
                         None,
172
                         self.file_diff_dict,
173
                         Result("origin", "msg", diffs={testfile_path: diff}),
174
                         file_dict)
175
176
        # Interaction must be closed by the user with `0` if it's not a param
177
        with simulate_console_inputs("INVALID",
178
                                     -1,
179
                                     1,
180
                                     0,
181
                                     3) as input_generator:
182
            curr_section = Section("")
183
            print_section_beginning(self.console_printer, curr_section)
184
            print_result(self.console_printer,
185
                         self.log_printer,
186
                         curr_section,
187
                         self.file_diff_dict,
188
                         Result("origin", "msg", diffs={testfile_path: diff}),
189
                         file_dict)
190
            self.assertEqual(input_generator.last_input, 3)
191
192
            self.file_diff_dict.clear()
193
194
            with open(testfile_path) as f:
195
                self.assertEqual(f.readlines(), ["1\n", "3_changed\n"])
196
197
            os.remove(testfile_path)
198
            os.remove(testfile_path + ".orig")
199
200
            name, section = get_action_info(curr_section,
201
                                            TestAction().get_metadata())
202
            self.assertEqual(input_generator.last_input, 4)
203
            self.assertEqual(str(section), " {param : '3'}")
204
            self.assertEqual(name, "TestAction")
205
206
        # Check if the user is asked for the parameter only the first time.
207
        # Use OpenEditorAction that needs this parameter (editor command).
208
        with simulate_console_inputs(1, "test_editor", 0, 1, 0) as generator:
209
            OpenEditorAction.is_applicable = staticmethod(lambda *args: True)
210
211
            patch_result = Result("origin", "msg", diffs={testfile_path: diff})
212
            patch_result.file = "f_b"
213
214
            print_result(self.console_printer,
215
                         self.log_printer,
216
                         curr_section,
217
                         self.file_diff_dict,
218
                         patch_result,
219
                         file_dict)
220
            # choose action, choose editor, choose no action (-1 -> 2)
221
            self.assertEqual(generator.last_input, 2)
222
223
            # It shoudn't ask for parameter again
224
            print_result(self.console_printer,
225
                         self.log_printer,
226
                         curr_section,
227
                         self.file_diff_dict,
228
                         patch_result,
229
                         file_dict)
230
            self.assertEqual(generator.last_input, 4)
231
232
    def test_print_section_beginning(self):
233
        with retrieve_stdout() as stdout:
234
            print_section_beginning(self.console_printer, Section("name"))
235
            self.assertEqual(stdout.getvalue(), "Executing section name...\n")
236
237
    def test_nothing_done(self):
238
        with retrieve_stdout() as stdout:
239
            nothing_done(self.log_printer)
240
            self.assertIn("No existent section was targeted or enabled. "
241
                          "Nothing to do.\n",
242
                          stdout.getvalue())
243
244
    def test_print_results_empty(self):
245
        with retrieve_stdout() as stdout:
246
            print_results(self.log_printer, Section(""), [], {}, {})
247
            self.assertEqual(stdout.getvalue(), "")
248
249
    def test_print_results_project_wide(self):
250
        with retrieve_stdout() as stdout:
251
            print_results(self.log_printer,
252
                          Section(""),
253
                          [Result("origin", "message")],
254
                          {},
255
                          {},
256
                          color=False)
257
            self.assertEqual(
258
                "\n{}\n|    | [NORMAL] origin:\n|    | message"
259
                "\n".format(STR_PROJECT_WIDE),
260
                stdout.getvalue())
261
262
    def test_print_results_for_file(self):
263
        with retrieve_stdout() as stdout:
264
            print_results(
265
                self.log_printer,
266
                Section(""),
267
                [Result.from_values("SpaceConsistencyBear",
268
                                    "Trailing whitespace found",
269
                                    file="proj/white",
270
                                    line=2)],
271
                {"proj/white": ["test line\n", "line 2\n", "line 3\n"]},
272
                {},
273
                color=False)
274
            self.assertEqual("""\nproj/white
275
|   2| line 2
276
|    | [NORMAL] SpaceConsistencyBear:
277
|    | Trailing whitespace found
278
""",
279
                         stdout.getvalue())
280
281
        with retrieve_stdout() as stdout:
282
            print_results(
283
                self.log_printer,
284
                Section(""),
285
                [Result.from_values("SpaceConsistencyBear",
286
                                    "Trailing whitespace found",
287
                                    file="proj/white",
288
                                    line=5)],
289
                {"proj/white": ["test line\n",
290
                                "line 2\n",
291
                                "line 3\n",
292
                                "line 4\n",
293
                                "line 5\n"]},
294
                {},
295
                color=False)
296
            self.assertEqual("""\nproj/white
297
|   5| line 5
298
|    | [NORMAL] SpaceConsistencyBear:
299
|    | Trailing whitespace found
300
""",
301
                             stdout.getvalue())
302
303
    def test_print_results_sorting(self):
304
        with retrieve_stdout() as stdout:
305
            print_results(self.log_printer,
306
                          Section(""),
307
                          [Result.from_values("SpaceConsistencyBear",
308
                                              "Trailing whitespace found",
309
                                              file="file",
310
                                              line=5),
311
                           Result.from_values("SpaceConsistencyBear",
312
                                              "Trailing whitespace found",
313
                                              file="file",
314
                                              line=2)],
315
                          {"file": ["test line\n",
316
                                    "line 2\n",
317
                                    "line 3\n",
318
                                    "line 4\n",
319
                                    "line 5\n"]},
320
                          {},
321
                          color=False)
322
323
            self.assertEqual("""
324
file
325
|   2| line 2
326
|    | [NORMAL] SpaceConsistencyBear:
327
|    | Trailing whitespace found
328
329
file
330
|   5| line 5
331
|    | [NORMAL] SpaceConsistencyBear:
332
|    | Trailing whitespace found
333
""",
334
                             stdout.getvalue())
335
336
    def test_print_results_multiple_ranges(self):
337
        affected_code = (SourceRange.from_values("some_file", 5, end_line=7),
338
                         SourceRange.from_values("another_file", 1, 3, 1, 5),
339
                         SourceRange.from_values("another_file", 3, 3, 3, 5))
340
        with retrieve_stdout() as stdout:
341
            print_results(
342
                self.log_printer,
343
                Section(""),
344
                [Result("ClangCloneDetectionBear",
345
                        "Clone Found",
346
                        affected_code)],
347
                {"some_file": ["line "+str(i+1)+"\n" for i in range(10)],
348
                 "another_file": ["line "+str(i+1)+"\n" for i in range(10)]},
349
                {},
350
                color=False)
351
            self.assertEqual("""
352
another_file
353
|   1| line 1
354
355
another_file
356
|   3| line 3
357
358
some_file
359
|   5| line 5
360
|   6| line 6
361
|   7| line 7
362
|    | [NORMAL] ClangCloneDetectionBear:
363
|    | Clone Found
364
""",
365
                         stdout.getvalue())
366
367
    def test_print_results_missing_file(self):
368
        self.log_printer = LogPrinter(NullPrinter())
369
        with retrieve_stdout() as stdout:
370
            print_results(
371
                self.log_printer,
372
                Section(""),
373
                [Result("t", "msg"),
374
                 Result.from_values("t", "msg", file="file", line=5)],
375
                {},
376
                {},
377
                color=False)
378
            self.assertEqual("\n" + STR_PROJECT_WIDE + "\n"
379
                             "|    | [NORMAL] t:\n"
380
                             "|    | msg\n"
381
                             # Second results file isn't there, no context is
382
                             # printed, only a warning log message which we
383
                             # don't catch
384
                             "|    | [NORMAL] t:\n"
385
                             "|    | msg\n", stdout.getvalue())
386
387
    def test_print_results_missing_line(self):
388
        with retrieve_stdout() as stdout:
389
            print_results(
390
                self.log_printer,
391
                Section(""),
392
                [Result.from_values("t", "msg", file="file", line=5),
393
                 Result.from_values("t", "msg", file="file", line=6)],
394
                {"file": ["line " + str(i+1) for i in range(5)]},
395
                {},
396
                color=False)
397
            self.assertEqual("\n"
398
                             "file\n"
399
                             "|   5| line 5\n"
400
                             "|    | [NORMAL] t:\n"
401
                             "|    | msg\n"
402
                             "\n"
403
                             "file\n"
404
                             "|    | {}\n"
405
                             "|    | [NORMAL] t:\n"
406
                             "|    | msg\n".format(STR_LINE_DOESNT_EXIST),
407
                             stdout.getvalue())
408
409
    def test_print_results_without_line(self):
410
        with retrieve_stdout() as stdout:
411
            print_results(
412
                self.log_printer,
413
                Section(""),
414
                [Result.from_values("t", "msg", file="file")],
415
                {"file": []},
416
                {},
417
                color=False)
418
            self.assertEqual(
419
                "\nfile\n"
420
                "|    | [NORMAL] t:\n"
421
                "|    | msg\n",
422
                stdout.getvalue())
423
424
    def test_print_bears_empty(self):
425
        with retrieve_stdout() as stdout:
426
            bears = {}
427
            print_bears(self.log_printer.printer, bears, True)
428
            self.assertEqual("No bears to show.\n", stdout.getvalue())
429
        with retrieve_stdout() as stdout:
430
            bears = {}
431
            print_bears(self.log_printer.printer, bears, False)
432
            self.assertEqual("No bears to show.\n", stdout.getvalue())
433
434
    def test_print_bears(self):
435
        with retrieve_stdout() as stdout:
436
            bears = {TestBear: ["default", "docs"]}
437
            print_bears(self.log_printer.printer, bears, False)
438
            expected_string = "TestBear:\n"
439
            expected_string += "  Test bear Description.\n\n"
440
            expected_string += "  Used in:\n"
441
            expected_string += "   * default\n"
442
            expected_string += "   * docs\n\n"
443
            expected_string += "  Needed Settings:\n"
444
            expected_string += "   * setting1: Required Setting.\n\n"
445
            expected_string += "  Optional Settings:\n"
446
            expected_string += "   * setting2: Optional Setting. ("
447
            expected_string += "Optional, defaults to 'None'."
448
            expected_string += ")\n\n"
449
450
            self.assertEqual(expected_string, stdout.getvalue())
451
452
    def test_print_bears_no_settings(self):
453
        with retrieve_stdout() as stdout:
454
            bears = {SomeBear: ["default"]}
455
            print_bears(self.log_printer.printer, bears, False)
456
            expected_string = "SomeBear:\n"
457
            expected_string += "  Some Description.\n\n"
458
            expected_string += "  Used in:\n"
459
            expected_string += "   * default\n\n"
460
            expected_string += "  No needed settings.\n\n"
461
            expected_string += "  No optional settings.\n\n"
462
463
            self.assertEqual(expected_string, stdout.getvalue())
464
465
    def test_print_bears_no_needed_settings(self):
466
        with retrieve_stdout() as stdout:
467
            bears = {SomeOtherBear: ["test"]}
468
            print_bears(self.log_printer.printer, bears, False)
469
            expected_string = "SomeOtherBear:\n"
470
            expected_string += "  This is a Bear.\n\n"
471
            expected_string += "  Used in:\n"
472
            expected_string += "   * test\n\n"
473
            expected_string += "  No needed settings.\n\n"
474
            expected_string += "  Optional Settings:\n"
475
            expected_string += "   * setting: This is an optional setting. ("
476
            expected_string += "Optional, defaults to 'None'."
477
            expected_string += ")\n\n"
478
479
            self.assertEqual(expected_string, stdout.getvalue())
480
481
    def test_print_bears_no_optional_settings(self):
482
        with retrieve_stdout() as stdout:
483
            bears = {TestBear2: ["test"]}
484
            print_bears(self.log_printer.printer, bears, False)
485
            expected_string = "TestBear2:\n"
486
            expected_string += "  Test bear 2 description.\n\n"
487
            expected_string += "  Used in:\n"
488
            expected_string += "   * test\n\n"
489
            expected_string += "  Needed Settings:\n"
490
            expected_string += "   * setting1: Required Setting.\n\n"
491
            expected_string += "  No optional settings.\n\n"
492
493
            self.assertEqual(expected_string, stdout.getvalue())
494
495
    def test_print_bears_no_sections(self):
496
        with retrieve_stdout() as stdout:
497
            bears = {SomeBear: []}
498
            print_bears(self.log_printer.printer, bears, False)
499
            expected_string = "SomeBear:\n"
500
            expected_string += "  Some Description.\n\n"
501
            expected_string += "  No sections.\n\n"
502
            expected_string += "  No needed settings.\n\n"
503
            expected_string += "  No optional settings.\n\n"
504
505
            self.assertEqual(expected_string, stdout.getvalue())
506
507
    def test_show_bears(self):
508
        with retrieve_stdout() as stdout:
509
            bears = {KeywordBear: ['default', 'test'],
510
                     LineLengthBear: ['test'],
511
                     SomeglobalBear: ['default', 'test']}
512
            print_bears(self.log_printer.printer, bears, False)
513
            expected_string = stdout.getvalue()
514
        self.maxDiff = None
515
        with retrieve_stdout() as stdout:
516
            show_bears(self.local_bears,
517
                       self.global_bears,
518
                       False,
519
                       self.log_printer.printer)
520
            self.assertEqual(expected_string, stdout.getvalue())
521
522
        with retrieve_stdout() as stdout:
523
            show_bears(self.local_bears,
524
                       self.global_bears,
525
                       True,
526
                       self.log_printer.printer)
527
            self.assertEqual(" * KeywordBear\n"
528
                             " * LineLengthBear\n"
529
                             " * SomeglobalBear\n", stdout.getvalue())
530
531
532
# Own test because this is easy and not tied to the rest
533
class PrintFormattedResultsTest(unittest.TestCase):
534
    def setUp(self):
535
        self.printer = StringPrinter()
536
        self.logger = LogPrinter(self.printer)
537
        self.section = Section("t")
538
539
    def test_default_format(self):
540
        expected_string = ("id:-?[0-9]+:origin:1:file:None:from_line:None:"
541
                           "from_column:None:to_line:None:to_column:None:"
542
                           "severity:1:msg:2\n")
543
        with retrieve_stdout() as stdout:
544
            print_results_formatted(self.logger,
545
                                    self.section,
546
                                    [Result("1", "2")],
547
                                    None,
548
                                    None)
549
            self.assertRegex(stdout.getvalue(), expected_string)
550
551
    def test_multiple_ranges(self):
552
        expected_string = (
553
            "id:-?[0-9]+:origin:1:file:another_file:from_line:5:"
554
            "from_column:3:to_line:5:to_column:5:"
555
            "severity:1:msg:2\n"
556
            "id:-?[0-9]+:origin:1:file:some_file:from_line:5:"
557
            "from_column:None:to_line:7:to_column:None:"
558
            "severity:1:msg:2\n")
559
        affected_code = (SourceRange.from_values("some_file", 5, end_line=7),
560
                         SourceRange.from_values("another_file", 5, 3, 5, 5))
561
        with retrieve_stdout() as stdout:
562
            print_results_formatted(self.logger,
563
                                    self.section,
564
                                    [Result("1", "2", affected_code)],
565
                                    None,
566
                                    None)
567
            self.assertRegex(stdout.getvalue(), expected_string)
568
569
    def test_bad_format(self):
570
        self.section.append(Setting("format_str", "{nonexistant}"))
571
        print_results_formatted(self.logger,
572
                                self.section,
573
                                [Result("1", "2")],
574
                                None,
575
                                None)
576
        self.assertRegex(self.printer.string, ".*Unable to print.*")
577
578
    def test_good_format(self):
579
        self.section.append(Setting("format_str", "{origin}"))
580
        with retrieve_stdout() as stdout:
581
            print_results_formatted(self.logger,
582
                                    self.section,
583
                                    [Result("1", "2")],
584
                                    None,
585
                                    None)
586
            self.assertEqual(stdout.getvalue(), "1\n")
587
588
    def test_empty_list(self):
589
        self.section.append(Setting("format_str", "{origin}"))
590
        # Shouldn't attempt to format the string None and will fail badly if
591
        # its done wrong.
592
        print_results_formatted(None,
593
                                self.section,
594
                                [],
595
                                None,
596
                                None,
597
                                None)
598
599
600
if __name__ == '__main__':
601
    unittest.main(verbosity=2)
602