blob: 859d375d8254b6d994c75e3b28884030d9b16ee8 [file] [log] [blame] [edit]
#!/usr/bin/env ruby
# Tests for the automerge plugin.
#
# Required local configuration:
# - gerrit running on 0.0.0.0:29418
# - 2 cloned projects
# - local user must be an owner of these repos
gem "minitest"
require 'minitest/autorun'
require 'json'
require 'open3'
PROJECTS_DIR="~/"
PROJECT1="project1"
PROJECT2="project2"
USER="admin"
class TestAutomerge < MiniTest::Test
HOST = "0.0.0.0"
PORT = 29418
GERRIT_SSH = "ssh -l #{USER} -p #{PORT} #{HOST}"
def setup
clean_local_repo(PROJECT1)
clean_local_repo(PROJECT2)
clean_gerrit([PROJECT1, PROJECT2])
end
def test_no_topic
commit_id = create_review(PROJECT1, "review0 on #{PROJECT1}")
approve_review(commit_id)
check_status(commit_id, 'MERGED')
end
def test_normal_topic_1_repo
commit_id = create_review(PROJECT1, "review0 on #{PROJECT1}", "topic1")
check_status(commit_id, 'NEW')
approve_review(commit_id)
check_status(commit_id, 'MERGED')
end
def test_crossrepo_topic_1_repo
commit_id = create_review(PROJECT1, "review0 on #{PROJECT1}", "crossrepo/topic1")
approve_review(commit_id)
check_status(commit_id, 'MERGED')
end
def test_crossrepo_topic_1_repo_over_not_merged_commit
commit0 = create_review(PROJECT1, "review0 on #{PROJECT1}")
commit0b = create_review(PROJECT1, "review0b on #{PROJECT1}", "crossrepo/topic1")
check_last_message_contains(commit0b, "This cross-repo review depends on a not merged commit")
end
def test_normal_topic_2_repos
commit1 = create_review(PROJECT1, "review1 on #{PROJECT1}", "topic2")
commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "topic2")
approve_review(commit1)
check_status(commit1, 'MERGED')
check_status(commit2, 'NEW')
approve_review(commit2)
check_status(commit2, 'MERGED')
end
def test_crossrepo_topic_2_repos
commit1 = create_review(PROJECT1, "review1 on #{PROJECT1}", "crossrepo/topic2")
commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
approve_review(commit1)
check_status(commit1, 'NEW')
check_status(commit2, 'NEW')
approve_review(commit2)
check_status(commit1, 'MERGED')
check_status(commit2, 'MERGED')
end
def test_crossrepo_topic_2_repos_above_non_mergeable_commit
commit1a = create_review(PROJECT1, "review1a on #{PROJECT1}")
commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}", "crossrepo/topic2")
commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
approve_review(commit1b)
check_status(commit1a, 'NEW')
check_status(commit1b, 'NEW')
check_status(commit2, 'NEW')
approve_review(commit2)
check_last_message_contains(commit2, "blocked by a non merged commit below")
check_status(commit1a, 'NEW')
check_status(commit1b, 'NEW')
check_status(commit2, 'NEW')
end
def test_crossrepo_topic_2_repos_below_not_merged_commit
commit1 = create_review(PROJECT1, "review1 on #{PROJECT1}", "crossrepo/topic2")
commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}")
commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
approve_review(commit1)
check_status(commit1, 'NEW')
check_status(commit2, 'NEW')
approve_review(commit2)
check_status(commit1, 'MERGED')
check_status(commit2, 'MERGED')
end
def test_commit_above_non_mergeable_crossrepo
commit1a = create_review(PROJECT1, "review1a on #{PROJECT1}", "crossrepo/topic1")
commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}")
commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic1")
approve_review(commit1a)
check_status(commit1a, 'NEW')
check_status(commit1b, 'NEW')
check_status(commit2, 'NEW')
approve_review(commit1b)
check_status(commit1b, 'NEW')
end
def test_refupdatedevent_merge_upper_commit
commit1a = create_review(PROJECT1, "review1a on #{PROJECT1}")
commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}")
approve_review(commit1b)
check_status(commit1a, 'NEW')
check_status(commit1b, 'NEW')
approve_review(commit1a)
check_status(commit1a, 'MERGED')
check_status(commit1b, 'MERGED')
end
def test_refupdatedevent_merge_upper_crossrepo
commit1a = create_review(PROJECT1, "review1a on #{PROJECT1}")
commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}", "crossrepo/topic2")
commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
approve_review(commit1b)
approve_review(commit2)
check_status(commit1a, 'NEW')
check_status(commit1b, 'NEW')
check_status(commit2, 'NEW')
approve_review(commit1a)
check_status(commit1a, 'MERGED')
check_status(commit1b, 'MERGED')
check_status(commit2, 'MERGED')
end
def test_refupdatedevent_does_not_merge_non_mergeable_upper_crossrepo
commit1a = create_review(PROJECT1, "review1a on #{PROJECT1}")
commit1b = create_review(PROJECT1, "review1b on #{PROJECT1}", "crossrepo/topic2")
commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", "crossrepo/topic2")
approve_review(commit1b)
check_status(commit1a, 'NEW')
check_status(commit1b, 'NEW')
check_status(commit2, 'NEW')
approve_review(commit1a)
check_status(commit1a, 'MERGED')
check_status(commit1b, 'NEW')
check_status(commit2, 'NEW')
end
def test_two_reviews_with_same_changed_id
commit1 = create_review(PROJECT1, "review1 on #{PROJECT1}")
change_id = read_change_id(PROJECT1)
abandon_review(commit1)
# Reuse Change-Id of abandoned review
commit2 = create_review(PROJECT2, "review2 on #{PROJECT2}", nil, change_id)
approve_review(commit2)
check_status(commit2, 'MERGED')
end
private
def project_dir(project_name)
"#{PROJECTS_DIR}#{project_name}"
end
def clean_local_repo(project_name)
execute("cd #{project_dir(project_name)} && git fetch && git reset --hard FETCH_HEAD")
end
def clean_gerrit(projects)
projects_query = projects.map{|project| "project:#{project}" }.join(" OR ")
query = "status:open AND (#{projects_query})"
reviews = gerrit_query(query)
reviews.each do |review|
review_number = review['number']
execute("#{GERRIT_SSH} gerrit review --abandon #{review_number},1")
end
end
def read_change_id(project_name, commit_id = "HEAD")
change_id = execute(["cd #{project_dir(project_name)}",
"git show #{commit_id} | grep Change-Id | sed 's/^.*Change-Id: \\([Ia-f0-9]*\\)$/\\1/'"
].join(" && "))
refute(change_id.empty?, "missing change-id")
change_id
end
def create_review(project_name, message, topic = nil, change_id = nil)
topic_suffix = "/#{topic}" if topic
message = "#{message}\n\nChange-Id: #{change_id}" if change_id
execute(["cd #{project_dir(project_name)}",
"echo 0 >> a",
"git add .",
%Q(git commit -m "#{message}"),
"git push origin HEAD:refs/for/master#{topic_suffix}"
].join(" && "))
commit_id = execute("cd #{project_dir(project_name)} && git rev-parse HEAD")
refute(commit_id.empty?, "missing commit-id")
commit_id
end
def approve_review(commit_id)
execute("#{GERRIT_SSH} gerrit review --strict-labels --verified 1 --code-review 2 #{commit_id}")
end
def abandon_review(commit_id)
execute("#{GERRIT_SSH} gerrit review --abandon #{commit_id}")
end
def check_status(commit_id, expected_status)
reviews = gerrit_query("commit:#{commit_id}")
assert_equal(1, reviews.size, "missing review with commit #{commit_id}")
review = reviews[0]
assert_equal(expected_status, review['status'], "wrong status on review #{review['number']} '#{review['subject']}'")
end
def check_label(commit_id, label_name, expected_label_value)
reviews = gerrit_query("commit:#{commit_id}", "--all-approvals")
assert_equal(1, reviews.size, "missing review with commit #{commit_id}")
review = reviews[0]
approvals = review['patchSets'][0]['approvals']
refute(approvals.nil?, "No approval on #{commit_id}")
code_review_approvals = approvals.select {|ap| ap['description'] == "Code-Review"}
refute(code_review_approvals.empty?, "No code-review score on #{commit_id}")
assert_equal(expected_label_value, code_review_approvals[0]['value'], "wrong label on review: #{review['number']}")
end
def check_last_message_contains(commit_id, expected_content)
reviews = gerrit_query("commit:#{commit_id}", "--comments")
assert_equal(1, reviews.size, "missing review with commit #{commit_id}")
messages = reviews[0]['comments'].map{|comment| comment['message'] }
assert(messages.last.include?(expected_content), "missing comment containing '#{expected_content}'")
end
def gerrit_query(query, options = "")
jsons = `#{GERRIT_SSH} gerrit query --format JSON #{options} '#{query}' | grep -v type\\"\\:\\"stats`
hashes = []
jsons.each_line do |line|
hashes << JSON.parse(line)
end
hashes
end
# Run a command as a child process and wait for its end.
def execute(command, opts={})
_, stdout, stderr, wait_thr = Open3.popen3(command, opts)
if wait_thr.value != 0
puts "Command failed: #{command}: stdout: #{stdout.read}, stderr: #{stderr.read}"
raise RuntimeError, stderr.read
end
return stdout.read
end
end