Add initial tests

Cover test scenarios except the ones about conflicts.
diff --git a/test.rb b/test.rb
new file mode 100755
index 0000000..37296f2
--- /dev/null
+++ b/test.rb
@@ -0,0 +1,150 @@
+#!/usr/bin/env ruby
+# Tests for the automerge plugin.
+#
+# Required local configuration:
+# - gerrit running on 0.0.0.0:29418
+# - 2 cloned projects
+
+gem "minitest"
+require 'minitest/autorun'
+require 'json'
+require 'open3'
+
+PROJECTS_DIR="~/"
+PROJECT1="project1"
+PROJECT2="project2"
+
+class TestAutomerge < MiniTest::Test
+  HOST = "0.0.0.0"
+  PORT = 29418
+  GERRIT_SSH = "ssh -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_label(commit0b, "Code-Review", "-1")
+    #TODO: check_message_contains(commit0b, "atomic_repo_same_review.txt")
+  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
+
+  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 create_review(project_name, message, topic = nil)
+    topic_suffix = "/#{topic}" if topic
+    execute(["cd #{project_dir(project_name)}",
+             "echo 0 >> a",
+             %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 --verified 1 --code-review 2 #{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']}")
+  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]
+    code_review_approvals = review['patchSets'][0]['approvals'].select {|ap| ap['description'] == "Code-Review"}
+    refute(code_review_approvals.empty?)
+    assert_equal(expected_label_value, code_review_approvals[0]['value'], "wrong label on review: #{review['number']}")
+  end
+
+# TODO Solve internal error when fetching comments
+#  def check_message_contains(commit_id, expected_content)
+#    reviews = gerrit_query("commit:#{commit_id}", "--comments")
+#  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