ruby/tool/test/test_sync_default_gems.rb
Sorah Fukumori fac2536776
Test_SyncDefaultGems: Fail when editor run (#9365)
When something went wrong and git launches editor, and a editor chosen
was terminal-based, a test run unnoticeably hangs.

As we intend editors not to be ran with --no-edit, GIT_EDITOR should be
defaulted to `false` so let Git command fails when it attempts to start a
editor. This allows catching such unintentional behaviour in test suite.

(Note: Prior to Git v2.32.0, git cherry-pick --no-edit doesn't work for
conflict resolution so currently the test hangs when older Git is in use.
39edfd5cbc
i.e. Ubuntu 20.04, Debian Bullseye doesn't satisfy this prerequisite.)
2023-12-26 21:43:23 +00:00

297 lines
10 KiB
Ruby
Executable file

#!/usr/bin/ruby
require 'test/unit'
require 'stringio'
require 'tmpdir'
require_relative '../sync_default_gems'
module Test_SyncDefaultGems
class TestMessageFilter < Test::Unit::TestCase
def assert_message_filter(expected, trailers, input, repo = "ruby/test", sha = "0123456789")
subject, *expected = expected
expected = [
"[#{repo}] #{subject}\n",
*expected.map {_1+"\n"},
"\n",
"https://github.com/#{repo}/commit/#{sha[0, 10]}\n",
]
if trailers
expected << "\n"
expected.concat(trailers.map {_1+"\n"})
end
out, err = capture_output do
SyncDefaultGems.message_filter(repo, sha, input: StringIO.new(input, "r"))
end
all_assertions do |a|
a.for("error") {assert_empty err}
a.for("result") {assert_pattern_list(expected, out)}
end
end
def test_subject_only
expected = [
"initial commit",
]
assert_message_filter(expected, nil, "initial commit")
end
def test_link_in_parenthesis
expected = [
"fix (https://github.com/ruby/test/pull/1)",
]
assert_message_filter(expected, nil, "fix (#1)")
end
def test_co_authored_by
expected = [
"commit something",
]
trailers = [
"Co-Authored-By: git <git@ruby-lang.org>",
]
assert_message_filter(expected, trailers, [expected, "", trailers, ""].join("\n"))
end
def test_multiple_co_authored_by
expected = [
"many commits",
]
trailers = [
"Co-authored-by: git <git@ruby-lang.org>",
"Co-authored-by: svn <svn@ruby-lang.org>",
]
assert_message_filter(expected, trailers, [expected, "", trailers, ""].join("\n"))
end
def test_co_authored_by_no_newline
expected = [
"commit something",
]
trailers = [
"Co-Authored-By: git <git@ruby-lang.org>",
]
assert_message_filter(expected, trailers, [expected, "", trailers].join("\n"))
end
def test_dot_ending_subject
expected = [
"subject with a dot.",
"",
"- next body line",
]
assert_message_filter(expected, nil, [expected[0], expected[2], ""].join("\n"))
end
end
class TestSyncWithCommits < Test::Unit::TestCase
def setup
super
@target = nil
pend "No git" unless system("git --version", out: IO::NULL)
@testdir = Dir.mktmpdir("sync")
@git_config = %W"HOME GIT_CONFIG_GLOBAL".each_with_object({}) {|k, c| c[k] = ENV[k]}
ENV["HOME"] = @testdir
ENV["GIT_CONFIG_GLOBAL"] = @testdir + "/gitconfig"
git(*%W"config --global user.email test@ruby-lang.org")
git(*%W"config --global user.name", "Ruby")
git(*%W"config --global init.defaultBranch default")
@target = "sync-test"
SyncDefaultGems::REPOSITORIES[@target] = ["ruby/#{@target}", "default"]
@sha = {}
@origdir = Dir.pwd
Dir.chdir(@testdir)
["src", @target].each do |dir|
git(*%W"init -q #{dir}")
File.write("#{dir}/.gitignore", "*~\n")
Dir.mkdir("#{dir}/lib")
File.write("#{dir}/lib/common.rb", ":ok\n")
Dir.mkdir("#{dir}/.github")
Dir.mkdir("#{dir}/.github/workflows")
File.write("#{dir}/.github/workflows/default.yml", "default:\n")
git(*%W"add .gitignore lib/common.rb .github", chdir: dir)
git(*%W"commit -q -m", "Initialize", chdir: dir)
if dir == "src"
File.write("#{dir}/lib/fine.rb", "return\n")
Dir.mkdir("#{dir}/test")
File.write("#{dir}/test/test_fine.rb", "return\n")
git(*%W"add lib/fine.rb test/test_fine.rb", chdir: dir)
git(*%W"commit -q -m", "Looks fine", chdir: dir)
end
Dir.mkdir("#{dir}/tool")
File.write("#{dir}/tool/ok", "#!/bin/sh\n""echo ok\n")
git(*%W"add tool/ok", chdir: dir)
git(*%W"commit -q -m", "Add tool #{dir}", chdir: dir)
@sha[dir] = top_commit(dir)
end
git(*%W"remote add #{@target} ../#{@target}", chdir: "src")
end
def teardown
if @target
Dir.chdir(@origdir)
SyncDefaultGems::REPOSITORIES.delete(@target)
ENV.update(@git_config)
FileUtils.rm_rf(@testdir)
end
super
end
def capture_process_output_to(outputs)
return yield unless outputs&.empty? == false
IO.pipe do |r, w|
orig = outputs.map {|out| out.dup}
outputs.each {|out| out.reopen(w)}
w.close
reader = Thread.start {r.read}
yield
ensure
outputs.each {|out| o = orig.shift; out.reopen(o); o.close}
return reader.value
end
end
def capture_process_outputs
out = err = nil
synchronize do
out = capture_process_output_to(STDOUT) do
err = capture_process_output_to(STDERR) do
yield
end
end
end
return out, err
end
def git(*commands, **opts)
system("git", *commands, exception: true, **opts)
end
def top_commit(dir, format: "%H")
IO.popen(%W[git log --format=#{format} -1], chdir: dir, &:read)&.chomp
end
def assert_sync(commits = true, success: true, editor: nil)
result = nil
out = capture_process_output_to([STDOUT, STDERR]) do
Dir.chdir("src") do
orig_editor = ENV["GIT_EDITOR"]
ENV["GIT_EDITOR"] = editor || 'false'
edit = true if editor
result = SyncDefaultGems.sync_default_gems_with_commits(@target, commits, edit: edit)
ensure
ENV["GIT_EDITOR"] = orig_editor
end
end
assert_equal(success, result, out)
out
end
def test_sync
File.write("#@target/lib/common.rb", "# OK!\n")
git(*%W"commit -q -m", "OK", "lib/common.rb", chdir: @target)
out = assert_sync()
assert_not_equal(@sha["src"], top_commit("src"), out)
assert_equal("# OK!\n", File.read("src/lib/common.rb"))
log = top_commit("src", format: "%B").lines
assert_equal("[ruby/#@target] OK\n", log.first, out)
assert_match(%r[/ruby/#{@target}/commit/\h+$], log.last, out)
assert_operator(top_commit(@target), :start_with?, log.last[/\h+$/], out)
end
def test_skip_tool
git(*%W"rm -q tool/ok", chdir: @target)
git(*%W"commit -q -m", "Remove tool", chdir: @target)
out = assert_sync()
assert_equal(@sha["src"], top_commit("src"), out)
end
def test_skip_test_fixtures
Dir.mkdir("#@target/test")
Dir.mkdir("#@target/test/fixtures")
File.write("#@target/test/fixtures/fixme.rb", "")
git(*%W"add test/fixtures/fixme.rb", chdir: @target)
git(*%W"commit -q -m", "Add fixtures", chdir: @target)
out = assert_sync(["#{@sha[@target]}..#{@target}/default"])
assert_equal(@sha["src"], top_commit("src"), out)
end
def test_skip_toplevel
Dir.mkdir("#@target/docs")
File.write("#@target/docs/NEWS.md", "= NEWS!!!\n")
git(*%W"add --", "docs/NEWS.md", chdir: @target)
File.write("#@target/docs/hello.md", "Hello\n")
git(*%W"add --", "docs/hello.md", chdir: @target)
git(*%W"commit -q -m", "It's a news", chdir: @target)
out = assert_sync()
assert_equal(@sha["src"], top_commit("src"), out)
end
def test_adding_toplevel
Dir.mkdir("#@target/docs")
File.write("#@target/docs/NEWS.md", "= New library\n")
File.write("#@target/lib/news.rb", "return\n")
git(*%W"add --", "docs/NEWS.md", "lib/news.rb", chdir: @target)
git(*%W"commit -q -m", "New lib", chdir: @target)
out = assert_sync()
assert_not_equal(@sha["src"], top_commit("src"), out)
assert_equal "return\n", File.read("src/lib/news.rb")
assert_include top_commit("src", format: "oneline"), "[ruby/#{@target}] New lib"
assert_not_operator File, :exist?, "src/docs"
end
def test_gitignore
File.write("#@target/.gitignore", "*.bak\n", mode: "a")
File.write("#@target/lib/common.rb", "Should.be_merged\n", mode: "a")
File.write("#@target/.github/workflows/main.yml", "# Should not merge\n", mode: "a")
git(*%W"add .github", chdir: @target)
git(*%W"commit -q -m", "Should be common.rb only",
*%W".gitignore lib/common.rb .github", chdir: @target)
out = assert_sync()
assert_not_equal(@sha["src"], top_commit("src"), out)
assert_equal("*~\n", File.read("src/.gitignore"), out)
assert_equal("#!/bin/sh\n""echo ok\n", File.read("src/tool/ok"), out)
assert_equal(":ok\n""Should.be_merged\n", File.read("src/lib/common.rb"), out)
assert_not_operator(File, :exist?, "src/.github/workflows/main.yml", out)
end
def test_gitignore_after_conflict
File.write("src/Gemfile", "# main\n")
git(*%W"add Gemfile", chdir: "src")
git(*%W"commit -q -m", "Add Gemfile", chdir: "src")
File.write("#@target/Gemfile", "# conflict\n", mode: "a")
File.write("#@target/lib/common.rb", "Should.be_merged\n", mode: "a")
File.write("#@target/.github/workflows/main.yml", "# Should not merge\n", mode: "a")
git(*%W"add Gemfile .github lib/common.rb", chdir: @target)
git(*%W"commit -q -m", "Should be common.rb only", chdir: @target)
out = assert_sync()
assert_not_equal(@sha["src"], top_commit("src"), out)
assert_equal("# main\n", File.read("src/Gemfile"), out)
assert_equal(":ok\n""Should.be_merged\n", File.read("src/lib/common.rb"), out)
assert_not_operator(File, :exist?, "src/.github/workflows/main.yml", out)
end
def test_delete_after_conflict
File.write("#@target/lib/bad.rb", "raise\n")
git(*%W"add lib/bad.rb", chdir: @target)
git(*%W"commit -q -m", "Add bad.rb", chdir: @target)
out = assert_sync
assert_equal("raise\n", File.read("src/lib/bad.rb"))
git(*%W"rm lib/bad.rb", chdir: "src", out: IO::NULL)
git(*%W"commit -q -m", "Remove bad.rb", chdir: "src")
File.write("#@target/lib/bad.rb", "raise 'bar'\n")
File.write("#@target/lib/common.rb", "Should.be_merged\n", mode: "a")
git(*%W"add lib/bad.rb lib/common.rb", chdir: @target)
git(*%W"commit -q -m", "Add conflict", chdir: @target)
head = top_commit("src")
out = assert_sync(editor: "git rm -f lib/bad.rb")
assert_not_equal(head, top_commit("src"))
assert_equal(":ok\n""Should.be_merged\n", File.read("src/lib/common.rb"), out)
assert_not_operator(File, :exist?, "src/lib/bad.rb", out)
end
end
end