require "tempfile"
require "my-assertions"
require "util"
require "svn/core"
require "svn/fs"
require "svn/repos"
require "svn/client"
class SvnReposTest < Test::Unit::TestCase
include SvnTestUtil
def setup
setup_basic
end
def teardown
teardown_basic
end
def test_version
assert_equal(Svn::Core.subr_version, Svn::Repos.version)
end
def test_path
assert_equal(@repos_path, @repos.path)
assert_equal(File.join(@repos_path, "db"), @repos.db_env)
assert_equal(File.join(@repos_path, "conf"), @repos.conf_dir)
assert_equal(File.join(@repos_path, "conf", "svnserve.conf"),
@repos.svnserve_conf)
locks_dir = File.join(@repos_path, "locks")
assert_equal(locks_dir, @repos.lock_dir)
assert_equal(File.join(locks_dir, "db.lock"),
@repos.db_lockfile)
assert_equal(File.join(locks_dir, "db-logs.lock"),
@repos.db_logs_lockfile)
hooks_dir = File.join(@repos_path, "hooks")
assert_equal(hooks_dir, @repos.hook_dir)
assert_equal(File.join(hooks_dir, "start-commit"),
@repos.start_commit_hook)
assert_equal(File.join(hooks_dir, "pre-commit"),
@repos.pre_commit_hook)
assert_equal(File.join(hooks_dir, "post-commit"),
@repos.post_commit_hook)
assert_equal(File.join(hooks_dir, "pre-revprop-change"),
@repos.pre_revprop_change_hook)
assert_equal(File.join(hooks_dir, "post-revprop-change"),
@repos.post_revprop_change_hook)
assert_equal(File.join(hooks_dir, "pre-lock"),
@repos.pre_lock_hook)
assert_equal(File.join(hooks_dir, "post-lock"),
@repos.post_lock_hook)
assert_equal(File.join(hooks_dir, "pre-unlock"),
@repos.pre_unlock_hook)
assert_equal(File.join(hooks_dir, "post-unlock"),
@repos.post_unlock_hook)
search_path = @repos_path
assert_equal(@repos_path, Svn::Repos.find_root_path(search_path))
search_path = "#{@repos_path}/XXX"
assert_equal(@repos_path, Svn::Repos.find_root_path(search_path))
search_path = "not-found"
assert_equal(nil, Svn::Repos.find_root_path(search_path))
end
def test_create
tmp_repos_path = File.join(@tmp_path, "repos")
fs_config = {Svn::Fs::CONFIG_FS_TYPE => Svn::Fs::TYPE_BDB}
repos = Svn::Repos.create(tmp_repos_path, {}, fs_config)
assert(File.exist?(tmp_repos_path))
fs_type_path = File.join(repos.fs.path, Svn::Fs::CONFIG_FS_TYPE)
assert_equal(Svn::Fs::TYPE_BDB,
File.open(fs_type_path) {|f| f.read.chop})
repos.fs.set_warning_func(&warning_func)
Svn::Repos.delete(tmp_repos_path)
assert(!File.exist?(tmp_repos_path))
end
def test_logs
log1 = "sample log1"
log2 = "sample log2"
log3 = "sample log3"
file = "file"
src = "source"
path = File.join(@wc_path, file)
ctx = make_context(log1)
File.open(path, "w") {|f| f.print(src)}
ctx.add(path)
info1 = ctx.ci(@wc_path)
start_rev = info1.revision
ctx = make_context(log2)
File.open(path, "a") {|f| f.print(src)}
info2 = ctx.ci(@wc_path)
ctx = make_context(log3)
File.open(path, "a") {|f| f.print(src)}
info3 = ctx.ci(@wc_path)
end_rev = info3.revision
logs = @repos.logs(file, start_rev, end_rev, end_rev - start_rev + 1)
logs = logs.collect do |changed_paths, revision, author, date, message|
paths = {}
changed_paths.each do |key, changed_path|
paths[key] = changed_path.action
end
[paths, revision, author, date, message]
end
assert_equal([
[
{"/#{file}" => "A"},
info1.revision,
@author,
info1.date,
log1,
],
[
{"/#{file}" => "M"},
info2.revision,
@author,
info2.date,
log2,
],
[
{"/#{file}" => "M"},
info3.revision,
@author,
info3.date,
log3,
],
],
logs)
revs = []
@repos.file_revs(file, start_rev, end_rev) do |path, rev, *rest|
revs << [path, rev]
end
assert_equal([
["/#{file}", info1.revision],
["/#{file}", info2.revision],
["/#{file}", info3.revision],
],
revs)
rev, date, author = @repos.fs.root.committed_info("/")
assert_equal(info3.revision, rev)
assert_equal(info3.date, date)
assert_equal(info3.author, author)
end
def test_hotcopy
log = "sample log"
file = "hello.txt"
path = File.join(@wc_path, file)
FileUtils.touch(path)
ctx = make_context(log)
ctx.add(path)
commit_info = ctx.commit(@wc_path)
rev = commit_info.revision
assert_equal(log, ctx.log_message(path, rev))
dest_path = File.join(@tmp_path, "dest")
backup_path = File.join(@tmp_path, "back")
config = {}
fs_config = {}
repos = Svn::Repos.create(dest_path, config, fs_config)
repos.fs.set_warning_func(&warning_func)
FileUtils.mv(@repos.path, backup_path)
FileUtils.mv(repos.path, @repos.path)
assert_raises(Svn::Error::FS_NO_SUCH_REVISION) do
assert_equal(log, ctx.log_message(path, rev))
end
FileUtils.rm_r(@repos.path)
Svn::Repos.hotcopy(backup_path, @repos.path)
assert_equal(log, ctx.log_message(path, rev))
end
def test_transaction
log = "sample log"
ctx = make_context(log)
ctx.checkout(@repos_uri, @wc_path)
ctx.mkdir(["#{@wc_path}/new_dir"])
prev_rev = @repos.youngest_rev
past_date = Time.now
@repos.transaction_for_commit(@author, log) do |txn|
txn.abort
end
assert_equal(prev_rev, @repos.youngest_rev)
assert_equal(prev_rev, @repos.dated_revision(past_date))
prev_rev = @repos.youngest_rev
@repos.transaction_for_commit(@author, log) do |txn|
end
assert_equal(prev_rev + 1, @repos.youngest_rev)
assert_equal(prev_rev, @repos.dated_revision(past_date))
assert_equal(prev_rev + 1, @repos.dated_revision(Time.now))
prev_rev = @repos.youngest_rev
@repos.transaction_for_update(@author) do |txn|
end
assert_equal(prev_rev, @repos.youngest_rev)
end
def test_trace_node_locations
file1 = "file1"
file2 = "file2"
file3 = "file3"
path1 = File.join(@wc_path, file1)
path2 = File.join(@wc_path, file2)
path3 = File.join(@wc_path, file3)
log = "sample log"
ctx = make_context(log)
FileUtils.touch(path1)
ctx.add(path1)
rev1 = ctx.ci(@wc_path).revision
ctx.mv(path1, path2)
rev2 = ctx.ci(@wc_path).revision
ctx.cp(path2, path3)
rev3 = ctx.ci(@wc_path).revision
assert_equal({
rev1 => "/#{file1}",
rev2 => "/#{file2}",
rev3 => "/#{file2}",
},
@repos.fs.trace_node_locations("/#{file2}",
[rev1, rev2, rev3]))
end
def test_report
file = "file"
file2 = "file2"
fs_base = "base"
path = File.join(@wc_path, file)
path2 = File.join(@wc_path, file2)
source = "sample source"
log = "sample log"
ctx = make_context(log)
File.open(path, "w") {|f| f.print(source)}
ctx.add(path)
rev = ctx.ci(@wc_path).revision
assert_equal(Svn::Core::NODE_FILE, @repos.fs.root.stat(file).kind)
editor = TestEditor.new
@repos.report(rev, @author, fs_base, "/", nil, editor) do |baton|
baton.link_path(file, file2, rev)
baton.delete_path(file)
end
assert_equal([
:set_target_revision,
:open_root,
:close_directory,
:close_edit,
],
editor.sequence.collect{|meth, *args| meth})
end
def test_commit_editor
trunk = "trunk"
tags = "tags"
tags_sub = "sub"
file = "file"
source = "sample source"
trunk_dir_path = File.join(@wc_path, trunk)
tags_dir_path = File.join(@wc_path, tags)
tags_sub_dir_path = File.join(tags_dir_path, tags_sub)
trunk_path = File.join(trunk_dir_path, file)
tags_path = File.join(tags_dir_path, file)
tags_sub_path = File.join(tags_sub_dir_path, file)
trunk_repos_uri = "#{@repos_uri}/#{trunk}"
rev1 = @repos.youngest_rev
editor = @repos.commit_editor(@repos_uri, "/")
root_baton = editor.open_root(rev1)
dir_baton = editor.add_directory(trunk, root_baton, nil, rev1)
file_baton = editor.add_file("#{trunk}/#{file}", dir_baton, nil, -1)
ret = editor.apply_textdelta(file_baton, nil)
ret.send(source)
editor.close_edit
assert_equal(rev1 + 1, @repos.youngest_rev)
rev2 = @repos.youngest_rev
ctx = make_context("")
ctx.up(@wc_path)
assert_equal(source, File.open(trunk_path) {|f| f.read})
editor = @repos.commit_editor(@repos_uri, "/")
root_baton = editor.open_root(rev2)
dir_baton = editor.add_directory(tags, root_baton, nil, rev2)
subdir_baton = editor.add_directory("#{tags}/#{tags_sub}",
dir_baton,
trunk_repos_uri,
rev2)
editor.close_edit
assert_equal(rev2 + 1, @repos.youngest_rev)
rev3 = @repos.youngest_rev
ctx.up(@wc_path)
assert_equal([
["/#{tags}/#{tags_sub}/#{file}", rev3],
["/#{trunk}/#{file}", rev2],
],
@repos.fs.history("#{tags}/#{tags_sub}/#{file}",
rev1, rev3, rev2))
editor = @repos.commit_editor(@repos_uri, "/")
root_baton = editor.open_root(rev3)
dir_baton = editor.delete_entry(tags, rev3, root_baton)
editor.close_edit
ctx.up(@wc_path)
assert(!File.exist?(tags_path))
end
def test_prop
file = "file"
path = File.join(@wc_path, file)
source = "sample source"
log = "sample log"
ctx = make_context(log)
File.open(path, "w") {|f| f.print(source)}
ctx.add(path)
ctx.ci(@wc_path)
assert_equal([
Svn::Core::PROP_REVISION_AUTHOR,
Svn::Core::PROP_REVISION_LOG,
Svn::Core::PROP_REVISION_DATE,
].sort,
@repos.proplist.keys.sort)
assert_equal(log, @repos.prop(Svn::Core::PROP_REVISION_LOG))
@repos.set_prop(@author, Svn::Core::PROP_REVISION_LOG, nil)
assert_nil(@repos.prop(Svn::Core::PROP_REVISION_LOG))
assert_equal([
Svn::Core::PROP_REVISION_AUTHOR,
Svn::Core::PROP_REVISION_DATE,
].sort,
@repos.proplist.keys.sort)
end
def test_load
file = "file"
path = File.join(@wc_path, file)
source = "sample source"
log = "sample log"
ctx = make_context(log)
File.open(path, "w") {|f| f.print(source)}
ctx.add(path)
rev1 = ctx.ci(@wc_path).revision
File.open(path, "a") {|f| f.print(source)}
rev2 = ctx.ci(@wc_path).revision
dest_path = File.join(@tmp_path, "dest")
repos = Svn::Repos.create(dest_path)
assert_not_equal(@repos.fs.root.committed_info("/"),
repos.fs.root.committed_info("/"))
dump = Tempfile.new("dump")
feedback = Tempfile.new("feedback")
dump.open
feedback.open
@repos.dump_fs(dump, feedback, rev1, rev2)
dump.close
feedback.close
dump.open
feedback.open
repos.load_fs(dump, feedback, Svn::Repos::LOAD_UUID_DEFAULT, "/")
assert_equal(@repos.fs.root.committed_info("/"),
repos.fs.root.committed_info("/"))
end
def test_node_editor
file = "file"
dir1 = "dir1"
dir2 = "dir2"
dir3 = "dir3"
dir1_path = File.join(@wc_path, dir1)
dir2_path = File.join(dir1_path, dir2)
dir3_path = File.join(dir2_path, dir3)
path = File.join(dir3_path, file)
source = "sample source"
log = "sample log"
ctx = make_context(log)
FileUtils.mkdir_p(dir3_path)
FileUtils.touch(path)
ctx.add(dir1_path)
rev1 = ctx.ci(@wc_path).revision
ctx.rm(dir3_path)
rev2 = ctx.ci(@wc_path).revision
rev1_root = @repos.fs.root(rev1)
rev2_root = @repos.fs.root(rev2)
editor = @repos.node_editor(rev1_root, rev2_root)
rev2_root.replay(editor)
tree = editor.baton.node
assert_equal("", tree.name)
assert_equal(dir1, tree.child.name)
assert_equal(dir2, tree.child.child.name)
end
def test_lock
file = "file"
log = "sample log"
path = File.join(@wc_path, file)
path_in_repos = "/#{file}"
ctx = make_context(log)
FileUtils.touch(path)
ctx.add(path)
rev = ctx.ci(@wc_path).revision
access = Svn::Fs::Access.new(@author)
@repos.fs.access = access
lock = @repos.lock(file)
locks = @repos.get_locks(file)
assert_equal([path_in_repos], locks.keys)
assert_equal(lock.token, locks[path_in_repos].token)
@repos.unlock(file, lock.token)
assert_equal({}, @repos.get_locks(file))
end
def test_authz
name = "REPOS"
conf_path = File.join(@tmp_path, "authz_file")
File.open(conf_path, "w") do |f|
f.print(<<-EOF)
[/]
#{@author} = r
EOF
end
authz = Svn::Repos::Authz.read(conf_path)
assert(authz.can_access?(name, "/", @author, Svn::Repos::AUTHZ_READ))
assert(!authz.can_access?(name, "/", @author, Svn::Repos::AUTHZ_WRITE))
assert(!authz.can_access?(name, "/", "FOO", Svn::Repos::AUTHZ_READ))
end
def warning_func
Proc.new do |err|
STDERR.puts err if $DEBUG
end
end
class TestEditor < Svn::Delta::BaseEditor
attr_reader :sequence
def initialize
@sequence = []
end
def set_target_revision(target_revision)
@sequence << [:set_target_revision, target_revision]
end
def open_root(base_revision)
@sequence << [:open_root, base_revision]
end
def delete_entry(path, revision, parent_baton)
@sequence << [:delete_entry, path, revision, parent_baton]
end
def add_directory(path, parent_baton,
copyfrom_path, copyfrom_revision)
@sequence << [:add_directory, path, parent_baton,
copyfrom_path, copyfrom_revision]
end
def open_directory(path, parent_baton, base_revision)
@sequence << [:open_directory, path, parent_baton, base_revision]
end
def change_dir_prop(dir_baton, name, value)
@sequence << [:change_dir_prop, dir_baton, name, value]
end
def close_directory(dir_baton)
@sequence << [:close_directory, dir_baton]
end
def absent_directory(path, parent_baton)
@sequence << [:absent_directory, path, parent_baton]
end
def add_file(path, parent_baton,
copyfrom_path, copyfrom_revision)
@sequence << [:add_file, path, parent_baton,
copyfrom_path, copyfrom_revision]
end
def open_file(path, parent_baton, base_revision)
@sequence << [:open_file, path, parent_baton, base_revision]
end
# return nil or object which has `call' method.
def apply_textdelta(file_baton, base_checksum)
@sequence << [:apply_textdelta, file_baton, base_checksum]
nil
end
def change_file_prop(file_baton, name, value)
@sequence << [:change_file_prop, file_baton, name, value]
end
def close_file(file_baton, text_checksum)
@sequence << [:close_file, file_baton, text_checksum]
end
def absent_file(path, parent_baton)
@sequence << [:absent_file, path, parent_baton]
end
def close_edit(baton)
@sequence << [:close_edit, baton]
end
def abort_edit(baton)
@sequence << [:abort_edit, baton]
end
end
end
syntax highlighted by Code2HTML, v. 0.9.1