Esempio n. 1
0
    def test_temporary_file_assign_to_logtype(self, test_name):
        input_path, original_log_file, path, result_log_file, results_yaml_file = self._prepare_files_path(
            test_name)
        effect_line_offset, line_content = self._gather_effect_line_data(
            input_path, original_log_file)

        whylog_config = self._prepare_config(path)
        log_reader = LogReader(whylog_config)
        effect_line = FrontInput(
            effect_line_offset, line_content,
            LineSource(
                'localhost',
                os.path.join(path, self._get_starting_file_name(input_path))))

        node1_source = LineSource('localhost',
                                  os.path.join(path, 'node_1.log'))
        node2_source = LineSource('localhost',
                                  os.path.join(path, 'node_2.log'))
        node3_source = LineSource('localhost',
                                  os.path.join(path, 'node_3.log'))
        temp_assign = {AbstractConfig.DEFAULT_LOG_TYPE: [node1_source]}
        if test_name == "010_multiple_files":
            temp_assign = {
                AbstractConfig.DEFAULT_LOG_TYPE: [node1_source, node2_source]
            }
        if test_name == "011_different_entry":
            temp_assign = {
                AbstractConfig.DEFAULT_LOG_TYPE:
                [node1_source, node2_source, node3_source]
            }

        results = log_reader.get_causes(effect_line, temp_assign)
        expected_results = self._investigation_results_from_yaml(
            results_yaml_file, result_log_file)
        self._check_results(results, expected_results)
Esempio n. 2
0
    def test_constraints_or_without_constraints(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Pineapple', 44), 'Pineapple, 44 times', 3000,
                      line_source)
        clues_lists = [
            ([
                Clue(('Banana', 2), 'Banana, 2 times', 1050, line_source),
                Clue(('Milk', 1), 'Milk, 2 times', 1100, line_source)
            ], 1),
            ([
                Clue(('Chocolate', 2), 'Chocolate, 2 times', 100, line_source)
            ], 1)
        ]  # yapf: disable
        constraints = []

        causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                         ConstraintManager())
        assert len(causes) == 2

        assert all(cause.constraints_linkage == InvestigationResult.OR
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Banana', 2), 'Banana, 2 times', 1050, line_source)),
            FrontInput.from_clue(Clue(
                ('Chocolate', 2), 'Chocolate, 2 times', 100, line_source))
        ]  # yapf: disable
        assert causes[1].lines == [
            FrontInput.from_clue(Clue(
                ('Milk', 1), 'Milk, 2 times', 1100, line_source)),
            FrontInput.from_clue(Clue(
                ('Chocolate', 2), 'Chocolate, 2 times', 100, line_source))
        ]  # yapf: disable
        assert all(not cause.constraints for cause in causes)
Esempio n. 3
0
 def test_constraints_or_verification_failed(self):
     line_source = LineSource('localhost', 'node_0.log')
     effect = Clue(('Pineapple', 44), 'Pineapple, 44 times', 3000,
                   line_source)
     clues_lists = [
         ([
             Clue(('Milk', 3), 'Milk, 3 times', 50, line_source),
             Clue(('Chocolate', 2), 'Chocolate, 2 times', 100, line_source),
             Clue(('Banana', 1), 'Banana, 1 times', 150, line_source)
         ], 1), ([
             Clue(('Banana', 2), 'Banana, 2 times', 1050, line_source),
             Clue(('Milk', 1), 'Milk, 2 times', 1100, line_source)
         ], 1)
     ]  # yapf: disable
     constraints = [{
         'clues_groups': [[0, 1], [1, 1], [2, 1]],
         'name': 'identical',
         'params': {}
     }, {
         'clues_groups': [[0, 2], [1, 2], [2, 2]],
         'name': 'identical',
         'params': {}
     }]
     causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                      ConstraintManager())
     assert not causes
Esempio n. 4
0
    def test_constraints_or_two_time_constraints(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Foo occurred', datetime(2000, 6, 14, second=15)),
                      'Foo occurred, 2000 06 14 00:00:15', 500, line_source)
        clues_lists = [
            ([
                 Clue(('Bar occurred', datetime(2000, 6, 14, second=10)), 'Bar occurred, 2000 06 14 00:00:10', 250, line_source),
             ], 1)
        ]  # yapf: disable
        constraints = [{
            'clues_groups': [[1, 2], [0, 2]],
            'name': 'time_delta',
            'params': {
                'max_delta': 8.0
            }
        }, {
            'clues_groups': [[1, 2], [0, 2]],
            'name': 'time_delta',
            'params': {
                'max_delta': 3.0
            }
        }]
        causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                         ConstraintManager())
        assert len(causes) == 1

        assert all(cause.constraints_linkage == InvestigationResult.OR
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Bar occurred', datetime(2000, 6, 14, second=10)), 'Bar occurred, 2000 06 14 00:00:10', 250, line_source))
        ]  # yapf: disable
        assert len(causes[0].constraints) == 1
        assert causes[0].constraints[0] == constraints[0]
Esempio n. 5
0
    def setUpClass(cls):
        SettingsFactorySelector.WHYLOG_DIR = TestPaths.WHYLOG_DIR
        cls.config = SettingsFactorySelector.get_settings()['config']
        cls.whylog_dir = SettingsFactorySelector._attach_whylog_dir(os.getcwd())

        cause1_regex = '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) cause1 transaction number: (\d+) Host: (\w)$'
        cause1_line = '2016-04-12 23:39:43 cause1 transaction number: 10101 Host: db_host'
        convertions = {1: 'date'}
        cls.cause1 = RegexParser("cause1", cause1_line, cause1_regex, [1], 'database', convertions)

        cause2_regex = '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) cause2 moved resource id: (\d+) Host: (\w)$'
        cause2_line = '2016-04-12 23:40:43 cause2 moved resource id: 1234 Host: apache_host'
        convertions = {1: 'date'}
        cls.cause2 = RegexParser("cause2", cause2_line, cause2_regex, [1], 'apache', convertions)

        effect_regex = '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) effect internal server error Host: (\w)$'
        effect_line = '2016-04-12 23:54:43 effect internal server error Host: apache_host'
        convertions = {1: 'date'}
        cls.effect = RegexParser("effect", effect_line, effect_regex, [1], 'apache', convertions)

        line_source = LineSource('localhost', 'node_1.log')
        cls.effect_time = datetime(2016, 4, 12, 23, 54, 43)
        effect_line = '2016-04-12 23:54:43 effect internal server error Host: apache_host'
        cls.effect_clues = {
            'effect': Clue((cls.effect_time, 'apache_host'), effect_line, 40, line_source)
        }

        cls.earliest_date = datetime(1, 1, 1, 1, 1, 1)
        cls.ten_second_earlier = datetime(2016, 4, 12, 23, 54, 33)
        cls.one_hundred_second_earlier = datetime(2016, 4, 12, 23, 53, 3)
        cls.ten_second_later = datetime(2016, 4, 12, 23, 54, 53)
Esempio n. 6
0
def mocked_investigation_plan():
    super_parser = RegexSuperParser('^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d).*', [1], {1: 'date'})
    matcher = WildCardFilenameMatcher('localhost', 'node_1.log', 'default', super_parser)
    default_log_type = LogType('default', [matcher])
    cause = RegexParser(
        'cause', '2015-12-03 12:08:08 root cause',
        '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) root cause$', [1], 'default', {1: 'date'}
    )
    effect = RegexParser(
        'effect', '2015-12-03 12:08:09 visible effect',
        '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) visible effect$', [1], 'default', {1: 'date'}
    )
    concatenated = ConcatenatedRegexParser([cause])
    effect_time = datetime(2015, 12, 3, 12, 8, 9)
    search_range = {
        'default': {
            'date': {
                'left_bound': datetime(2015, 12, 3, 12, 8, 8),
                'right_bound': effect_time
            }
        }
    }
    default_investigation_step = InvestigationStep(concatenated, search_range)
    rule = Rule(
        [cause], effect, [
            {
                'clues_groups': [[1, 1], [0, 1]],
                'name': 'time',
                'params': {'max_delta': 1}
            }
        ], Rule.LINKAGE_AND
    )  # yapf: disable
    line_source = LineSource('localhost', 'node_1.log')
    effect_clues = {'effect': Clue((effect_time,), 'visible effect', 40, line_source)}
    return InvestigationPlan([rule], [(default_investigation_step, default_log_type)], effect_clues)
Esempio n. 7
0
    def test_constraints_and_basic(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Pear', 2), 'Pear, 2 times', 3000, line_source)
        clues_lists = [
            ([
                Clue(('Milk', 3), 'Milk, 3 times', 50, line_source),
                Clue(('Chocolate', 2), 'Chocolate, 2 times', 100, line_source),
                Clue(('Pear', 2), 'Pear, 2 times', 150, line_source)
            ], 1), ([
                Clue(('Pear', 2), 'Pear, 2 times', 1050, line_source),
                Clue(('Milk', 1), 'Milk, 1 times', 1100, line_source)
            ], 1)
        ]  # yapf: disable
        constraints = [{
            'clues_groups': [[0, 1], [1, 1], [2, 1]],
            'name': 'identical',
            'params': {}
        }, {
            'clues_groups': [[0, 2], [1, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]
        causes = Verifier.constraints_and(clues_lists, effect, constraints,
                                          ConstraintManager())
        assert len(causes) == 1

        assert all(cause.constraints_linkage == InvestigationResult.AND
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Pear', 2), 'Pear, 2 times', 150, line_source)),
            FrontInput.from_clue(Clue(
                ('Pear', 2), 'Pear, 2 times', 1050, line_source))
        ]  # yapf: disable
Esempio n. 8
0
    def test_single_constraint_not_basic(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Dinner', 44), 'Dinner, 44 times', 440, line_source)
        clues_lists = [
            ([
                Clue(('Car', 40), 'Car, 40 times', 400, line_source),
                Clue(('Car', 1), 'Car, 1 times', 100, line_source),
                Clue(('Car', 15), 'Car, 15 times', 150, line_source)
            ], 1),
            ([
                Clue(('Forest', 0), 'Forest, 0 times', 0, line_source),
                Clue(('Forest', 3), 'Forest, 3 times', 30, line_source),
                Clue(('Forest', 42), 'Forest, 42 times', 420, line_source)
            ], 1)
        ]  # yapf: disable
        constraint = {
            'clues_groups': [[0, 1], [1, 1], [2, 1]],
            'name': 'identical',
            'params': {}
        }
        causes = Verifier.single_constraint_not(clues_lists,
                                                effect, constraint,
                                                ConstraintManager())

        assert len(causes) == 1
        assert causes[0].constraints_linkage == InvestigationResult.NOT
        assert causes[0].constraints[0] == constraint
Esempio n. 9
0
    def tests_whylog_factory(self):
        log_reader, teacher_generator, config = whylog_factory()
        teacher = teacher_generator()

        front_input = FrontInput(1, 'line content', LineSource('host', 'path'))
        teacher.add_line(0, front_input, True)
        self.assertRaises(NoLogTypeError, log_reader.get_causes, front_input)
        config.get_log_type(front_input.line_source)
Esempio n. 10
0
    def setUp(self):
        """
        Creates teacher with sample Rule.
        """
        test_files_dir = 'empty_config_files'
        path = os.path.join(*path_test_files + [test_files_dir])
        parsers_path, rules_path, log_types_path = ConfigPathFactory.get_path_to_config_files(
            path, False
        )

        self.test_files = [parsers_path, rules_path, log_types_path]
        self._clean_test_files()

        yaml_config = YamlConfig(parsers_path, rules_path, log_types_path)
        regex_assistant = RegexAssistant()
        self.teacher = Teacher(yaml_config, regex_assistant)

        self.effect_id = 0
        self.effect_front_input = FrontInput(
            offset=42,
            line_content=r'2015-12-03 12:11:00 Error occurred in reading test',
            line_source=LineSource('sample_host', 'sample_path')
        )

        self.cause1_id = 1
        self.cause1_front_input = FrontInput(
            offset=30,
            line_content=r'2015-12-03 12:10:55 Data is missing on comp21',
            line_source=LineSource('sample_host1', 'sample_path1')
        )

        self.cause2_id = 2
        self.cause2_front_input = FrontInput(
            offset=21,
            line_content=r'2015-12-03 12:10:50 Data migration to comp21 failed in test 123',
            line_source=LineSource('sample_host2', 'sample_path2')
        )

        self.identical_groups = [(self.cause1_id, 2), (self.cause2_id, 2)]
        self.date_groups = [(self.effect_id, 1), (self.effect_id, 1)]

        self._add_rule()
Esempio n. 11
0
 def search(self, original_front_input):
     clues = defaultdict(list)
     left_bound, right_bound = self._find_offsets_range(original_front_input)
     for line, actual_offset in self._reverse_from_offset(right_bound):
         if actual_offset < left_bound:
             return clues
         # TODO: remove mock
         line_source = LineSource('localhost', self._file_path)
         clues_from_line = self._investigation_step.get_clues(line, actual_offset, line_source)
         self._merge_clues(clues, clues_from_line)
     return clues
Esempio n. 12
0
 def _investigation_results_from_yaml(self, yaml_file, real_log_file):
     file_content = yaml.load(open(yaml_file))
     results = []
     for result in file_content:
         causes = [
             FrontInput(
                 self._deduce_line_offset_by_unique_content(real_log_file, line_str), line_str,
                 LineSource("localhost", real_log_file)
             ) for line_str in result['causes']
         ]  # yapf: disable
         results.append(
             InvestigationResult(causes, result['constraints'],
                                 result['linkage']))
     return results
Esempio n. 13
0
 def search(self, original_front_input):
     clues = defaultdict(list)
     if original_front_input.line_source.path == self._file_path:
         # TODO checking if host is also the same
         offset = original_front_input.offset
     else:
         offset = self._deduce_offset()
     for line, actual_offset in self._reverse_from_offset(offset):
         # TODO: remove mock
         line_source = LineSource('localhost', self._file_path)
         clues_from_line = self._investigation_step.get_clues(
             line, actual_offset, line_source)
         self._merge_clues(clues, clues_from_line)
     return clues
Esempio n. 14
0
    def test_constraints_and_verification_failed_when_or_succeeded(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Banana', 44), 'Banana, 44 times', 3000, line_source)
        clues_lists = [
            ([
                Clue(('Milk', 3), 'Milk, 3 times', 50, line_source),
                Clue(('Chocolate', 4), 'Chocolate, 4 times', 100, line_source),
                Clue(('Pear', 2), 'Pear, 2 times', 150, line_source)                # <- should be found (parser 1)
            ], 1), ([
                Clue(('Pineapple', 2), 'Pineapple, 2 times', 1050, line_source),    # <- should be found (parser 2)
                Clue(('Milk', 1), 'Milk, 1 times', 1100, line_source)
            ], 1)
        ]  # yapf: disable
        constraints = [{
            'clues_groups': [[0, 1], [1, 1], [2, 1]],
            'name': 'identical',
            'params': {}
        }, {
            'clues_groups': [[1, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]

        # testing 'and'
        causes = Verifier.constraints_and(clues_lists, effect, constraints,
                                          ConstraintManager())
        assert not causes

        # testing 'or'
        causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                         ConstraintManager())
        assert len(causes) == 1

        assert all(cause.constraints_linkage == InvestigationResult.OR
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Pear', 2), 'Pear, 2 times', 150, line_source)),
            FrontInput.from_clue(Clue(
                ('Pineapple', 2), 'Pineapple, 2 times', 1050, line_source))
        ]  # yapf: disable
        assert causes[0].constraints == [{
            'clues_groups': [[1, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]
Esempio n. 15
0
    def test_one(self, test_name):
        input_path, original_log_file, path, result_log_file, results_yaml_file = self._prepare_files_path(
            test_name)
        effect_line_offset, line_content = self._gather_effect_line_data(
            input_path, original_log_file)

        # preparing Whylog structures, normally prepared by Front
        whylog_config = YamlConfig(
            *ConfigPathFactory.get_path_to_config_files(path))
        log_reader = LogReader(whylog_config)
        effect_line = FrontInput(
            effect_line_offset, line_content,
            LineSource(
                'localhost',
                os.path.join(path, self._get_starting_file_name(input_path))))

        results = log_reader.get_causes(effect_line)
        expected_results = self._investigation_results_from_yaml(
            results_yaml_file, result_log_file)
        self._check_results(results, expected_results)
Esempio n. 16
0
    def test_constraints_when_one_unmatched(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Banana', 2), 'Banana, 2 times', 3000, line_source)
        clues_lists = [
            ([
            ], 1), ([
                Clue(('Banana', 2), 'Banana, 2 times', 1050, line_source),
                Clue(('Milk', 1), 'Milk, 1 times', 1100, line_source)
            ], 1)
        ]  # yapf: disable
        constraints = [{
            'clues_groups': [[0, 1], [1, 1], [2, 1]],
            'name': 'identical',
            'params': {}
        }, {
            'clues_groups': [[0, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]

        # testing 'or'
        causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                         ConstraintManager())
        assert len(causes) == 1

        assert all(cause.constraints_linkage == InvestigationResult.OR
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Banana', 2), 'Banana, 2 times', 1050, line_source))
        ]  # yapf: disable
        assert causes[0].constraints == [{
            'clues_groups': [[0, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]

        # testing 'and'
        causes = Verifier.constraints_and(clues_lists, effect, constraints,
                                          ConstraintManager())
        assert not causes
Esempio n. 17
0
class TestBasic(TestCase):
    cause_a = RegexParser('cause_a', '31 carrots', '^(\d\d) carrots$', [1],
                          'default', {1: 'int'})
    cause_b = RegexParser('cause_b', '79 broccoli', '^(\d\d) broccoli$', [1],
                          'default', {1: 'int'})
    effect = RegexParser('effect', '53 dinners', '^(\d\d) dinners', [1],
                         'default', {1: 'int'})
    line_source = LineSource('localhost', 'node_1.log')

    def test_order_causes_list(self):
        cause_1 = RegexParser('cause_a', None, '', None, None, None)
        cause_2 = RegexParser('cause_b', None, '', None, None, None)
        cause_3 = RegexParser('cause_c', None, '', None, None, None)

        causes = [cause_2, cause_3, cause_2, cause_1]
        constraints = [{
            'clues_groups': [[0, 1], [1, 3], [2, 4], [3, 7], [4, 2]],
            'name': 'identical',
            'params': {}
        }, {
            'clues_groups': [[0, 1], [4, 2]],
            'name': 'identical',
            'params': {}
        }]

        sorted_causes, modified_constrains = RegexRuleFactory._order_causes_list(
            causes, constraints)

        option_1 = [{
            'clues_groups': [[0, 1], [2, 3], [4, 4], [3, 7], [1, 2]],
            'name': 'identical',
            'params': {}
        }, {
            'clues_groups': [[0, 1], [1, 2]],
            'name': 'identical',
            'params': {}
        }]
        option_2 = [{
            'clues_groups': [[0, 1], [3, 3], [4, 4], [2, 7], [1, 2]],
            'name': 'identical',
            'params': {}
        }, {
            'clues_groups': [[0, 1], [1, 2]],
            'name': 'identical',
            'params': {}
        }]
        assert all(sorted_causes[i].name <= sorted_causes[i + 1].name
                   for i in six.moves.range(len(sorted_causes) - 1))
        assert modified_constrains in [option_1, option_2]

    def test_constraints_check_basic(self):
        rule = Rule(
            [self.cause_a, self.cause_b], self.effect, [
                {
                    'clues_groups': [[0, 1], [1, 1], [2, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_AND
        )  # yapf: disable
        effect_clues_dict = {
            'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
        }
        clues = {  # it's dictionary of the same type as clues dict collected in SearchManager
            'cause_a': [
                Clue((40,), '40 carrots', 400, self.line_source),
                Clue((42,), '42 carrots', 420, self.line_source),
                Clue((44,), '44 carrots', 440, self.line_source)
            ],
            'cause_b': [
                Clue((32,), '32 broccoli', 100, self.line_source),
                Clue((42,), '42 broccoli', 120, self.line_source),
                Clue((52,), '52 broccoli', 140, self.line_source)
            ],
            'dummy': [
                Clue((42,), '42 foo bar', 980, self.line_source),
                Clue((84,), '84 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)

        assert len(results) == 1

        assert results[0].lines == [
            FrontInput.from_clue(
                Clue((42,), '42 carrots', 420, self.line_source)),
            FrontInput.from_clue(
                Clue((42,), '42 broccoli', 120, self.line_source))
        ]  # yapf: disable

    def test_constraints_check_same_cause_parser_as_effect(self):
        rule = Rule(
            [self.cause_a], self.cause_a, [
                {
                    'clues_groups': [[0, 1], [1, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_AND
        )  # yapf: disable
        effect_clues_dict = {
            'cause_a': Clue((42, ), '42 carrots', 1420, self.line_source)
        }
        clues = {  # it's dictionary of the same type as clues dict collected in SearchManager
            'cause_a': [
                Clue((40,), '40 carrots', 400, self.line_source),
                Clue((42,), '42 carrots', 420, self.line_source),
                Clue((44,), '44 carrots', 440, self.line_source)
            ],
            'dummy': [
                Clue((98,), '98 foo bar', 980, self.line_source),
                Clue((99,), '99 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)

        assert len(results) == 1

        assert results[0].lines == [
            FrontInput.from_clue(
                Clue((42,), '42 carrots', 420, self.line_source))
        ]  # yapf: disable

    def test_constraints_check_two_same_parsers(self):
        rule = Rule(
            [self.cause_a, self.cause_a], self.effect, [
                {
                    'clues_groups': [[0, 1], [1, 1], [2, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_AND
        )  # yapf: disable
        effect_clues_dict = {
            'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
        }
        clues = {  # it's dictionary of the same type as clues dict collected in SearchManager
            'cause_a': [
                Clue((40,), '40 carrots', 400, self.line_source),
                Clue((42,), '42 carrots', 420, self.line_source),
                Clue((44,), '44 carrots', 440, self.line_source),
                Clue((42,), '42 carrots', 460, self.line_source),
                Clue((40,), '40 carrots', 480, self.line_source),
            ],
            'dummy': [
                Clue((98,), '98 foo bar', 980, self.line_source),
                Clue((99,), '99 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)

        assert len(results) == 2

        assert results[0].lines == [
            FrontInput.from_clue(
                Clue((42,), '42 carrots', 420, self.line_source)),
            FrontInput.from_clue(
                Clue((42,), '42 carrots', 460, self.line_source))
        ]  # yapf: disable
        assert results[1].lines == [
            FrontInput.from_clue(
                Clue((42,), '42 carrots', 460, self.line_source)),
            FrontInput.from_clue(
                Clue((42,), '42 carrots', 420, self.line_source))
        ]  # yapf: disable

    def test_empty_clues_going_to_verify(self):
        rule = Rule(
            [self.cause_a, self.cause_a], self.effect, [
                {
                    'clues_groups': [[0, 1], [1, 1], [2, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_AND
        )  # yapf: disable
        effect_clues_dict = {
            'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
        }
        clues = {  # it's dictionary of the same type as clues dict collected in SearchManager
            'cause_a': [],
            'dummy': [
                Clue((98,), '98 foo bar', 980, self.line_source),
                Clue((99,), '99 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)
        assert not results

    def test_one_matched_line_when_two_occurrences_requested(self):
        rule = Rule(
            [self.cause_a, self.cause_a], self.effect, [
                {
                    'clues_groups': [[0, 1], [1, 1], [2, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_AND
        )  # yapf: disable
        effect_clues_dict = {
            'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
        }
        clues = {  # it's dictionary of the same type as clues dict collected in SearchManager
            'cause_a': [
                Clue((42,), '42 carrots', 420, self.line_source),
            ],
            'dummy': [
                Clue((98,), '98 foo bar', 980, self.line_source),
                Clue((99,), '99 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)
        assert effect_clues_dict['effect'].regex_parameters[0] == \
            clues['cause_a'][0].regex_parameters[0]
        assert not results

    def test_not_one_constraint_nothing_matched(self):
        rule = Rule(
            [self.cause_a], self.effect, [
                {
                    'clues_groups': [[0, 1], [1, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_NOT
        )  # yapf: disable
        effect_clues_dict = {
            'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
        }
        clues = {
            'dummy': [
                Clue((98,), '98 foo bar', 980, self.line_source),
                Clue((99,), '99 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)
        assert results
        assert len(results) == 1
        assert results[0].lines == []
        assert results[0].constraints_linkage == InvestigationResult.NOT

    def test_not_one_constraint_something_matched_but_constraint_rejected_it(
            self):
        rule = Rule(
            [self.cause_a], self.effect, [
                {
                    'clues_groups': [[0, 1], [1, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_NOT
        )  # yapf: disable
        effect_clues_dict = {
            'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
        }
        clues = {
            'cause_a': [
                Clue((13,), '13 carrots', 130, self.line_source),
            ],
            'dummy': [
                Clue((98,), '98 foo bar', 980, self.line_source),
                Clue((99,), '99 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)
        assert results
        assert len(results) == 1
        assert results[0].lines == []
        assert results[0].constraints_linkage == InvestigationResult.NOT

    def test_not_one_constraint_something_matched_but_constraint_approves(
            self):
        rule = Rule(
            [self.cause_a], self.effect, [
                {
                    'clues_groups': [[0, 1], [1, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_NOT
        )  # yapf: disable
        effect_clues_dict = {
            'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
        }
        clues = {
            'cause_a': [
                Clue((42,), '42 carrots', 420, self.line_source),
            ],
            'dummy': [
                Clue((98,), '98 foo bar', 980, self.line_source),
                Clue((99,), '99 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)
        assert not results