xref: /llvm-project/llvm/utils/revert_checker_test.py (revision 0a53f43c0c7e33cde07b24169e8f45db7eba2fea)
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# ===----------------------------------------------------------------------===##
4#
5# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6# See https://llvm.org/LICENSE.txt for license information.
7# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8#
9# ===----------------------------------------------------------------------===##
10"""Tests for revert_checker.
11
12Note that these tests require having LLVM's git history available, since our
13repository has a few interesting instances of edge-cases.
14"""
15
16import os
17import logging
18import unittest
19from typing import List
20
21import revert_checker
22
23# pylint: disable=protected-access
24
25
26def get_llvm_project_path() -> str:
27    """Returns the path to llvm-project's root."""
28    my_dir = os.path.dirname(__file__)
29    return os.path.realpath(os.path.join(my_dir, "..", ".."))
30
31
32class _SilencingFilter(logging.Filter):
33    """Silences all log messages.
34
35    Also collects info about log messages that would've been emitted.
36    """
37
38    def __init__(self) -> None:
39        self.messages: List[str] = []
40
41    def filter(self, record: logging.LogRecord) -> bool:
42        self.messages.append(record.getMessage())
43        return False
44
45
46class Test(unittest.TestCase):
47    """Tests for revert_checker."""
48
49    def silence_logging(self) -> _SilencingFilter:
50        root = logging.getLogger()
51        filt = _SilencingFilter()
52        root.addFilter(filt)
53        self.addCleanup(root.removeFilter, filt)
54        return filt
55
56    def test_log_stream_with_known_sha_range(self) -> None:
57        start_sha = "e241573d5972d34a323fa5c64774c4207340beb3"
58        end_sha = "a7a37517751ffb0f5529011b4ba96e67fcb27510"
59        commits = [
60            revert_checker._LogEntry(
61                "e241573d5972d34a323fa5c64774c4207340beb3",
62                "\n".join(
63                    (
64                        "[mlir] NFC: remove IntegerValueSet / MutableIntegerSet",
65                        "",
66                        "Summary:",
67                        "- these are unused and really not needed now given flat "
68                        "affine",
69                        "  constraints",
70                        "",
71                        "Differential Revision: https://reviews.llvm.org/D75792",
72                    )
73                ),
74            ),
75            revert_checker._LogEntry(
76                "97572fa6e9daecd648873496fd11f7d1e25a55f0",
77                "[NFC] use hasAnyOperatorName and hasAnyOverloadedOperatorName "
78                "functions in clang-tidy matchers",
79            ),
80        ]
81
82        logs = list(
83            revert_checker._log_stream(
84                get_llvm_project_path(),
85                root_sha=start_sha,
86                end_at_sha=end_sha,
87            )
88        )
89        self.assertEqual(commits, logs)
90
91    def test_reverted_noncommit_object_is_a_nop(self) -> None:
92        log_filter = self.silence_logging()
93        # c9944df916e41b1014dff5f6f75d52297b48ecdc mentions reverting a non-commit
94        # object. It sits between the given base_ref and root.
95        reverts = revert_checker.find_reverts(
96            git_dir=get_llvm_project_path(),
97            across_ref="c9944df916e41b1014dff5f6f75d52297b48ecdc~",
98            root="c9944df916e41b1014dff5f6f75d52297b48ecdc",
99            max_pr_lookback=50,
100        )
101        self.assertEqual(reverts, [])
102
103        complaint = (
104            "Failed to resolve reverted object "
105            "edd18355be574122aaa9abf58c15d8c50fb085a1"
106        )
107        self.assertTrue(
108            any(x.startswith(complaint) for x in log_filter.messages),
109            log_filter.messages,
110        )
111
112    def test_known_reverts_across_arbitrary_llvm_rev(self) -> None:
113        reverts = revert_checker.find_reverts(
114            git_dir=get_llvm_project_path(),
115            across_ref="c47f971694be0159ffddfee8a75ae515eba91439",
116            root="9f981e9adf9c8d29bb80306daf08d2770263ade6",
117            max_pr_lookback=50,
118        )
119        self.assertEqual(
120            reverts,
121            [
122                revert_checker.Revert(
123                    sha="4e0fe038f438ae1679eae9e156e1f248595b2373",
124                    reverted_sha="65b21282c710afe9c275778820c6e3c1cf46734b",
125                ),
126                revert_checker.Revert(
127                    sha="9f981e9adf9c8d29bb80306daf08d2770263ade6",
128                    reverted_sha="4060016fce3e6a0b926ee9fc59e440a612d3a2ec",
129                ),
130            ],
131        )
132
133    def test_pr_based_revert_works(self) -> None:
134        reverts = revert_checker.find_reverts(
135            git_dir=get_llvm_project_path(),
136            # This SHA is a direct child of the reverted SHA expected below.
137            across_ref="2d5f3b0a61fb171617012a2c3ba05fd31fb3bb1d",
138            # This SHA is a direct child of the revert SHA listed below.
139            root="2c01b278580212914ec037bb5dd9b73702dfe7f1",
140            max_pr_lookback=50,
141        )
142        self.assertEqual(
143            reverts,
144            [
145                revert_checker.Revert(
146                    # This SHA is a `Reverts ${PR}` for #111004.
147                    sha="50866e84d1da8462aeb96607bf6d9e5bbd5869c5",
148                    # ...And this was the commit for #111004.
149                    reverted_sha="67160c5ab5f5b7fd5fa7851abcfde367c8a9f91b",
150                ),
151            ],
152        )
153
154
155if __name__ == "__main__":
156    unittest.main()
157