1#!/usr/bin/env python 2# Copyright 2015 the V8 project authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6from collections import namedtuple 7from os import path 8import search_related_commits 9import shutil 10from subprocess import Popen, PIPE, check_call 11import unittest 12 13 14TEST_CONFIG = { 15 "GIT_REPO": "/tmp/test-v8-search-related-commits", 16} 17 18class TestSearchRelatedCommits(unittest.TestCase): 19 20 base_dir = TEST_CONFIG["GIT_REPO"] 21 22 def _execute_git(self, git_args): 23 24 fullCommand = ["git", "-C", self.base_dir] + git_args 25 p = Popen(args=fullCommand, stdin=PIPE, 26 stdout=PIPE, stderr=PIPE) 27 output, err = p.communicate() 28 rc = p.returncode 29 if rc != 0: 30 raise Exception(err) 31 return output 32 33 def setUp(self): 34 if path.exists(self.base_dir): 35 shutil.rmtree(self.base_dir) 36 37 check_call(["git", "init", self.base_dir]) 38 39 # Initial commit 40 message = """[turbofan] Sanitize language mode for javascript operators. 41 42 R=mstarzinger@chromium.org 43 44 Review URL: https://codereview.chromium.org/1084243005 45 46 Cr-Commit-Position: refs/heads/master@{#28059}""" 47 self._make_empty_commit(message) 48 49 message = """[crankshaft] Do some stuff 50 51 R=hablich@chromium.org 52 53 Review URL: https://codereview.chromium.org/1084243007 54 55 Cr-Commit-Position: refs/heads/master@{#28030}""" 56 57 self._make_empty_commit(message) 58 59 def tearDown(self): 60 if path.exists(self.base_dir): 61 shutil.rmtree(self.base_dir) 62 63 def _assert_correct_standard_result( 64 self, result, all_commits, hash_of_first_commit): 65 self.assertEqual(len(result), 1, "Master commit not found") 66 self.assertTrue( 67 result.get(hash_of_first_commit), 68 "Master commit is wrong") 69 70 self.assertEqual( 71 len(result[hash_of_first_commit]), 72 1, 73 "Child commit not found") 74 self.assertEqual( 75 all_commits[2], 76 result[hash_of_first_commit][0], 77 "Child commit wrong") 78 79 def _get_commits(self): 80 commits = self._execute_git( 81 ["log", "--format=%H", "--reverse"]).splitlines() 82 return commits 83 84 def _make_empty_commit(self, message): 85 self._execute_git(["commit", "--allow-empty", "-m", message]) 86 87 def testSearchByCommitPosition(self): 88 message = """Revert of some stuff. 89 > Cr-Commit-Position: refs/heads/master@{#28059} 90 R=mstarzinger@chromium.org 91 92 Review URL: https://codereview.chromium.org/1084243005 93 94 Cr-Commit-Position: refs/heads/master@{#28088}""" 95 96 self._make_empty_commit(message) 97 98 commits = self._get_commits() 99 hash_of_first_commit = commits[0] 100 101 result = search_related_commits.search_all_related_commits( 102 self.base_dir, hash_of_first_commit, "HEAD", None) 103 104 self._assert_correct_standard_result(result, commits, hash_of_first_commit) 105 106 def testSearchByTitle(self): 107 message = """Revert of some stuff. 108 > [turbofan] Sanitize language mode for javascript operators. 109 > Cr-Commit-Position: refs/heads/master@{#289} 110 R=mstarzinger@chromium.org 111 112 Review URL: https://codereview.chromium.org/1084243005 113 114 Cr-Commit-Position: refs/heads/master@{#28088}""" 115 116 self._make_empty_commit(message) 117 118 commits = self._get_commits() 119 hash_of_first_commit = commits[0] 120 121 result = search_related_commits.search_all_related_commits( 122 self.base_dir, hash_of_first_commit, "HEAD", None) 123 124 self._assert_correct_standard_result(result, commits, hash_of_first_commit) 125 126 def testSearchByHash(self): 127 commits = self._get_commits() 128 hash_of_first_commit = commits[0] 129 130 message = """Revert of some stuff. 131 > [turbofan] Sanitize language mode for javascript operators. 132 > Reverting """ + hash_of_first_commit + """ 133 > R=mstarzinger@chromium.org 134 135 Review URL: https://codereview.chromium.org/1084243005 136 137 Cr-Commit-Position: refs/heads/master@{#28088}""" 138 139 self._make_empty_commit(message) 140 141 #Fetch again for an update 142 commits = self._get_commits() 143 hash_of_first_commit = commits[0] 144 145 result = search_related_commits.search_all_related_commits( 146 self.base_dir, 147 hash_of_first_commit, 148 "HEAD", 149 None) 150 151 self._assert_correct_standard_result(result, commits, hash_of_first_commit) 152 153 def testConsiderSeparator(self): 154 commits = self._get_commits() 155 hash_of_first_commit = commits[0] 156 157 # Related commits happen before separator so it is not a hit 158 message = """Revert of some stuff: Not a hit 159 > [turbofan] Sanitize language mode for javascript operators. 160 > Reverting """ + hash_of_first_commit + """ 161 > R=mstarzinger@chromium.org 162 163 Review URL: https://codereview.chromium.org/1084243005 164 165 Cr-Commit-Position: refs/heads/master@{#28088}""" 166 self._make_empty_commit(message) 167 168 # Related commits happen before and after separator so it is a hit 169 commit_pos_of_master = "27088" 170 message = """Implement awesome feature: Master commit 171 172 Review URL: https://codereview.chromium.org/1084243235 173 174 Cr-Commit-Position: refs/heads/master@{#""" + commit_pos_of_master + "}" 175 self._make_empty_commit(message) 176 177 # Separator commit 178 message = """Commit which is the origin of the branch 179 180 Review URL: https://codereview.chromium.org/1084243456 181 182 Cr-Commit-Position: refs/heads/master@{#28173}""" 183 self._make_empty_commit(message) 184 185 # Filler commit 186 message = "Some unrelated commit: Not a hit" 187 self._make_empty_commit(message) 188 189 # Related commit after separator: a hit 190 message = "Patch r" + commit_pos_of_master +""" done 191 192 Review URL: https://codereview.chromium.org/1084243235 193 194 Cr-Commit-Position: refs/heads/master@{#29567}""" 195 self._make_empty_commit(message) 196 197 #Fetch again for an update 198 commits = self._get_commits() 199 hash_of_first_commit = commits[0] 200 hash_of_hit = commits[3] 201 hash_of_separator = commits[4] 202 hash_of_child_hit = commits[6] 203 204 result = search_related_commits.search_all_related_commits( 205 self.base_dir, 206 hash_of_first_commit, 207 "HEAD", 208 hash_of_separator) 209 210 self.assertTrue(result.get(hash_of_hit), "Hit not found") 211 self.assertEqual(len(result), 1, "More than one hit found") 212 self.assertEqual( 213 len(result.get(hash_of_hit)), 214 1, 215 "More than one child hit found") 216 self.assertEqual( 217 result.get(hash_of_hit)[0], 218 hash_of_child_hit, 219 "Wrong commit found") 220 221 def testPrettyPrint(self): 222 message = """Revert of some stuff. 223 > [turbofan] Sanitize language mode for javascript operators. 224 > Cr-Commit-Position: refs/heads/master@{#289} 225 R=mstarzinger@chromium.org 226 227 Review URL: https://codereview.chromium.org/1084243005 228 229 Cr-Commit-Position: refs/heads/master@{#28088}""" 230 231 self._make_empty_commit(message) 232 233 commits = self._get_commits() 234 hash_of_first_commit = commits[0] 235 OptionsStruct = namedtuple( 236 "OptionsStruct", 237 "git_dir of until all prettyprint separator verbose") 238 options = OptionsStruct( 239 git_dir= self.base_dir, 240 of= [hash_of_first_commit], 241 until= [commits[2]], 242 all= True, 243 prettyprint= True, 244 separator = None, 245 verbose=False) 246 output = [] 247 for current_line in search_related_commits.main(options): 248 output.append(current_line) 249 250 self.assertIs(len(output), 2, "Not exactly two entries written") 251 self.assertTrue(output[0].startswith("+"), "Master entry not marked with +") 252 self.assertTrue(output[1].startswith("| "), "Child entry not marked with |") 253 254 def testNothingFound(self): 255 commits = self._get_commits() 256 257 self._execute_git(["commit", "--allow-empty", "-m", "A"]) 258 self._execute_git(["commit", "--allow-empty", "-m", "B"]) 259 self._execute_git(["commit", "--allow-empty", "-m", "C"]) 260 self._execute_git(["commit", "--allow-empty", "-m", "D"]) 261 262 hash_of_first_commit = commits[0] 263 result = search_related_commits.search_all_related_commits( 264 self.base_dir, 265 hash_of_first_commit, 266 "HEAD", 267 None) 268 269 self.assertEqual(len(result), 0, "Results found where none should be.") 270 271 272if __name__ == "__main__": 273 #import sys;sys.argv = ['', 'Test.testName'] 274 unittest.main() 275