blob: 3df4ebeaa00e207537434e1046f52e646a95ed23 [file] [log] [blame]
Emmanuel Debanne52160412014-09-19 09:20:50 +02001#!/usr/bin/env ruby
2# Tests for the automerge plugin.
3#
4# Required local configuration:
5# - gerrit running on 0.0.0.0:29418
6# - 2 cloned projects
Emmanuel Debanne0cf82d72015-08-20 16:49:14 +02007# - local user must be an owner of these repos
Emmanuel Debanne52160412014-09-19 09:20:50 +02008
9gem "minitest"
10require 'minitest/autorun'
11require 'json'
12require 'open3'
13
14PROJECTS_DIR="~/"
15PROJECT1="project1"
16PROJECT2="project2"
Emmanuel Debanneac1b10a2017-09-05 11:34:06 +020017USER="admin"
Emmanuel Debanne52160412014-09-19 09:20:50 +020018
19class TestAutomerge < MiniTest::Test
20 HOST = "0.0.0.0"
21 PORT = 29418
Emmanuel Debanneac1b10a2017-09-05 11:34:06 +020022 GERRIT_SSH = "ssh -l #{USER} -p #{PORT} #{HOST}"
Emmanuel Debanne52160412014-09-19 09:20:50 +020023
24 def setup
25 clean_local_repo(PROJECT1)
26 clean_local_repo(PROJECT2)
27 clean_gerrit([PROJECT1, PROJECT2])
28 end
29
30 def test_no_topic
31 commit_id = create_review(PROJECT1, "review0 on #{PROJECT1}")
32 approve_review(commit_id)
33 check_status(commit_id, 'MERGED')
34 end
35
36 def test_normal_topic_1_repo
37 commit_id = create_review(PROJECT1, "review0 on #{PROJECT1}", "topic1")
38 check_status(commit_id, 'NEW')
39 approve_review(commit_id)
40 check_status(commit_id, 'MERGED')
41 end
42
43 def test_crossrepo_topic_1_repo
44 commit_id = create_review(PROJECT1, "review0 on #{PROJECT1}", "crossrepo/topic1")
45 approve_review(commit_id)
46 check_status(commit_id, 'MERGED')
47 end
48
49 def test_crossrepo_topic_1_repo_over_not_merged_commit
50 commit0 = create_review(PROJECT1, "review0 on #{PROJECT1}")
51 commit0b = create_review(PROJECT1, "review0b on #{PROJECT1}", "crossrepo/topic1")
Emmanuel Debannef92d6b02017-09-06 11:24:06 +020052
Emmanuel Debannedf78b8c2017-09-05 17:20:44 +020053 check_last_message_contains(commit0b, "This cross-repo review depends on a not merged commit")
Emmanuel Debanne52160412014-09-19 09:20:50 +020054 end
55
56 def test_normal_topic_2_repos
57 commit1 = create_review(PROJECT1, "review1 on #{PROJECT1}", "topic2")
58 commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "topic2")
59 approve_review(commit1)
60 check_status(commit1, 'MERGED')
61 check_status(commit2, 'NEW')
62 approve_review(commit2)
63 check_status(commit2, 'MERGED')
64 end
65
66 def test_crossrepo_topic_2_repos
67 commit1 = create_review(PROJECT1, "review1 on #{PROJECT1}", "crossrepo/topic2")
68 commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
69 approve_review(commit1)
70 check_status(commit1, 'NEW')
71 check_status(commit2, 'NEW')
Emmanuel Debannecea7a4e2017-09-04 16:31:01 +020072
Emmanuel Debanne52160412014-09-19 09:20:50 +020073 approve_review(commit2)
Emmanuel Debannecea7a4e2017-09-04 16:31:01 +020074
Emmanuel Debanne52160412014-09-19 09:20:50 +020075 check_status(commit1, 'MERGED')
76 check_status(commit2, 'MERGED')
77 end
78
Emmanuel Debannecea7a4e2017-09-04 16:31:01 +020079 def test_crossrepo_topic_2_repos_above_non_mergeable_commit
80 commit1a = create_review(PROJECT1, "review1a on #{PROJECT1}")
81 commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}", "crossrepo/topic2")
82 commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
83 approve_review(commit1b)
84 check_status(commit1a, 'NEW')
85 check_status(commit1b, 'NEW')
86 check_status(commit2, 'NEW')
87
88 approve_review(commit2)
89
Emmanuel Debanne5e8df612017-09-05 17:24:45 +020090 check_last_message_contains(commit2, "blocked by a non merged commit below")
Emmanuel Debannecea7a4e2017-09-04 16:31:01 +020091 check_status(commit1a, 'NEW')
92 check_status(commit1b, 'NEW')
93 check_status(commit2, 'NEW')
94 end
95
Emmanuel Debanneae7802c2017-09-05 11:53:20 +020096 def test_crossrepo_topic_2_repos_below_not_merged_commit
97 commit1 = create_review(PROJECT1, "review1 on #{PROJECT1}", "crossrepo/topic2")
98 commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}")
99 commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
100 approve_review(commit1)
101 check_status(commit1, 'NEW')
102 check_status(commit2, 'NEW')
103
104 approve_review(commit2)
105
106 check_status(commit1, 'MERGED')
107 check_status(commit2, 'MERGED')
108 end
109
Emmanuel Debanneac248a22017-09-04 15:01:30 +0200110 def test_refupdatedevent_merge_upper_commit
111 commit1a = create_review(PROJECT1, "review1a on #{PROJECT1}")
112 commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}")
113 approve_review(commit1b)
114 check_status(commit1a, 'NEW')
115 check_status(commit1b, 'NEW')
116
117 approve_review(commit1a)
118
119 check_status(commit1a, 'MERGED')
120 check_status(commit1b, 'MERGED')
121 end
122
Emmanuel Debannecea7a4e2017-09-04 16:31:01 +0200123 def test_refupdatedevent_merge_upper_crossrepo
124 commit1a = create_review(PROJECT1, "review1a on #{PROJECT1}")
125 commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}", "crossrepo/topic2")
126 commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
127 approve_review(commit1b)
128 approve_review(commit2)
129 check_status(commit1a, 'NEW')
130 check_status(commit1b, 'NEW')
131 check_status(commit2, 'NEW')
132
133 approve_review(commit1a)
134
135 check_status(commit1a, 'MERGED')
136 check_status(commit1b, 'MERGED')
137 check_status(commit2, 'MERGED')
138 end
139
Emmanuel Debanneac248a22017-09-04 15:01:30 +0200140 def test_refupdatedevent_does_not_merge_non_mergeable_upper_crossrepo
141 commit1a = create_review(PROJECT1, "review1a on #{PROJECT1}")
142 commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}", "crossrepo/topic2")
143 commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
144 approve_review(commit1b)
145 check_status(commit1a, 'NEW')
146 check_status(commit1b, 'NEW')
147 check_status(commit2, 'NEW')
148
149 approve_review(commit1a)
150
151 check_status(commit1a, 'MERGED')
152 check_status(commit1b, 'NEW')
153 check_status(commit2, 'NEW')
154 end
155
Emmanuel Debanne7f56b162014-12-29 09:29:25 +0100156 def test_two_reviews_with_same_changed_id
157 commit1 = create_review(PROJECT1, "review1 on #{PROJECT1}")
158 change_id = read_change_id(PROJECT1)
159 abandon_review(commit1)
160 # Reuse Change-Id of abandoned review
161 commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", nil, change_id)
162
163 approve_review(commit2)
164
165 check_status(commit2, 'MERGED')
166 end
167
Emmanuel Debanne52160412014-09-19 09:20:50 +0200168 private
169
170 def project_dir(project_name)
171 "#{PROJECTS_DIR}#{project_name}"
172 end
173
174 def clean_local_repo(project_name)
175 execute("cd #{project_dir(project_name)} && git fetch && git reset --hard FETCH_HEAD")
176 end
177
178 def clean_gerrit(projects)
179 projects_query = projects.map{|project| "project:#{project}" }.join(" OR ")
180 query = "status:open AND (#{projects_query})"
181 reviews = gerrit_query(query)
182 reviews.each do |review|
183 review_number = review['number']
Paladox nonecc5b3d92017-10-10 13:17:04 +0000184 execute("#{GERRIT_SSH} gerrit review --abandon #{review_number},1")
Emmanuel Debanne52160412014-09-19 09:20:50 +0200185 end
186 end
187
Emmanuel Debanne7f56b162014-12-29 09:29:25 +0100188 def read_change_id(project_name, commit_id = "HEAD")
189 change_id = execute(["cd #{project_dir(project_name)}",
190 "git show #{commit_id} | grep Change-Id | sed 's/^.*Change-Id: \\([Ia-f0-9]*\\)$/\\1/'"
191 ].join(" && "))
192 refute(change_id.empty?, "missing change-id")
193 change_id
194 end
195
Paladox nonecc5b3d92017-10-10 13:17:04 +0000196 def create_review(project_name, message, topic = nil, change_id = nil)
Emmanuel Debanne52160412014-09-19 09:20:50 +0200197 topic_suffix = "/#{topic}" if topic
Emmanuel Debanne7f56b162014-12-29 09:29:25 +0100198 message = "#{message}\n\nChange-Id: #{change_id}" if change_id
Emmanuel Debanne52160412014-09-19 09:20:50 +0200199 execute(["cd #{project_dir(project_name)}",
200 "echo 0 >> a",
Emmanuel Debanne0cf82d72015-08-20 16:49:14 +0200201 "git add .",
202 %Q(git commit -m "#{message}"),
Paladox nonecc5b3d92017-10-10 13:17:04 +0000203 "git push origin HEAD:refs/for/master#{topic_suffix}"
Emmanuel Debanne52160412014-09-19 09:20:50 +0200204 ].join(" && "))
205 commit_id = execute("cd #{project_dir(project_name)} && git rev-parse HEAD")
206 refute(commit_id.empty?, "missing commit-id")
207 commit_id
208 end
209
210 def approve_review(commit_id)
Emmanuel Debannec37f0a12017-08-29 22:32:53 +0200211 execute("#{GERRIT_SSH} gerrit review --strict-labels --verified 1 --code-review 2 #{commit_id}")
Emmanuel Debanne52160412014-09-19 09:20:50 +0200212 end
213
Emmanuel Debanne7f56b162014-12-29 09:29:25 +0100214 def abandon_review(commit_id)
215 execute("#{GERRIT_SSH} gerrit review --abandon #{commit_id}")
216 end
217
Emmanuel Debanne52160412014-09-19 09:20:50 +0200218 def check_status(commit_id, expected_status)
219 reviews = gerrit_query("commit:#{commit_id}")
220 assert_equal(1, reviews.size, "missing review with commit #{commit_id}")
221 review = reviews[0]
Emmanuel Debannec37f0a12017-08-29 22:32:53 +0200222 assert_equal(expected_status, review['status'], "wrong status on review #{review['number']} '#{review['subject']}'")
Emmanuel Debanne52160412014-09-19 09:20:50 +0200223 end
224
225 def check_label(commit_id, label_name, expected_label_value)
226 reviews = gerrit_query("commit:#{commit_id}", "--all-approvals")
227 assert_equal(1, reviews.size, "missing review with commit #{commit_id}")
228 review = reviews[0]
Emmanuel Debannec37f0a12017-08-29 22:32:53 +0200229 approvals = review['patchSets'][0]['approvals']
230 refute(approvals.nil?, "No approval on #{commit_id}")
231 code_review_approvals = approvals.select {|ap| ap['description'] == "Code-Review"}
232 refute(code_review_approvals.empty?, "No code-review score on #{commit_id}")
Emmanuel Debanne52160412014-09-19 09:20:50 +0200233 assert_equal(expected_label_value, code_review_approvals[0]['value'], "wrong label on review: #{review['number']}")
234 end
235
Emmanuel Debanne0cf82d72015-08-20 16:49:14 +0200236 def check_last_message_contains(commit_id, expected_content)
237 reviews = gerrit_query("commit:#{commit_id}", "--comments")
238 assert_equal(1, reviews.size, "missing review with commit #{commit_id}")
239 messages = reviews[0]['comments'].map{|comment| comment['message'] }
240 assert(messages.last.include?(expected_content), "missing comment containing '#{expected_content}'")
241 end
Emmanuel Debanne52160412014-09-19 09:20:50 +0200242
243 def gerrit_query(query, options = "")
244 jsons = `#{GERRIT_SSH} gerrit query --format JSON #{options} '#{query}' | grep -v type\\"\\:\\"stats`
245 hashes = []
246 jsons.each_line do |line|
247 hashes << JSON.parse(line)
248 end
249 hashes
250 end
251
252 # Run a command as a child process and wait for its end.
253 def execute(command, opts={})
254 _, stdout, stderr, wait_thr = Open3.popen3(command, opts)
255 if wait_thr.value != 0
256 puts "Command failed: #{command}: stdout: #{stdout.read}, stderr: #{stderr.read}"
257 raise RuntimeError, stderr.read
258 end
259 return stdout.read
260 end
261end