* subversion/bindings/swig/python/libsvn_swig_py/swigutil_py.c
[svn.git] / subversion / bindings / swig / ruby / test / test_fs.rb
blob93e20c02bc88e0937d95cec710dbf1825f481854
1 require "my-assertions"
2 require "util"
3 require "time"
4 require "md5"
6 require "svn/core"
7 require "svn/fs"
8 require "svn/repos"
9 require "svn/client"
11 class SvnFsTest < Test::Unit::TestCase
12   include SvnTestUtil
14   def setup
15     setup_basic
16   end
18   def teardown
19     teardown_basic
20   end
22   def test_version
23     assert_equal(Svn::Core.subr_version, Svn::Fs.version)
24   end
26   def assert_create
27     path = File.join(@tmp_path, "fs")
28     fs_type = Svn::Fs::TYPE_FSFS
29     config = {Svn::Fs::CONFIG_FS_TYPE => fs_type}
31     assert(!File.exist?(path))
32     fs = nil
33     callback = Proc.new do |fs|
34       assert(File.exist?(path))
35       assert_equal(fs_type, Svn::Fs.type(path))
36       fs.set_warning_func do |err|
37         p err
38         abort
39       end
40       assert_equal(path, fs.path)
41     end
42     yield(:create, [path, config], callback)
44     assert(fs.closed?)
45     assert_raises(Svn::Error::FsAlreadyClose) do
46       fs.path
47     end
49     yield(:delete, [path])
50     assert(!File.exist?(path))
51   end
53   def test_create
54     assert_create do |method, args, callback|
55       Svn::Fs.__send__(method, *args, &callback)
56     end
57   end
59   def test_create_for_backward_compatibility
60     assert_create do |method, args, callback|
61       Svn::Fs::FileSystem.__send__(method, *args, &callback)
62     end
63   end
65   def assert_hotcopy
66     log = "sample log"
67     file = "hello.txt"
68     path = File.join(@wc_path, file)
69     FileUtils.touch(path)
71     ctx = make_context(log)
72     ctx.add(path)
73     commit_info = ctx.commit(@wc_path)
74     rev = commit_info.revision
76     assert_equal(log, ctx.log_message(path, rev))
78     backup_path = File.join(@tmp_path, "back")
80     FileUtils.mv(@fs.path, backup_path)
81     FileUtils.mkdir_p(@fs.path)
83     assert_raises(Svn::Error::RaLocalReposOpenFailed) do
84       ctx.log_message(path, rev)
85     end
87     yield(backup_path, @fs.path)
88     assert_equal(log, ctx.log_message(path, rev))
89   end
91   def test_hotcopy
92     assert_hotcopy do |src, dest|
93       Svn::Fs.hotcopy(src, dest)
94     end
95   end
97   def test_hotcopy_for_backward_compatibility
98     assert_hotcopy do |src, dest|
99       Svn::Fs::FileSystem.hotcopy(src, dest)
100     end
101   end
103   def test_root
104     log = "sample log"
105     file = "sample.txt"
106     src = "sample source"
107     path_in_repos = "/#{file}"
108     path = File.join(@wc_path, file)
110     assert_nil(@fs.root.name)
111     assert_equal(Svn::Core::INVALID_REVNUM, @fs.root.base_revision)
113     ctx = make_context(log)
114     FileUtils.touch(path)
115     ctx.add(path)
116     rev1 = ctx.commit(@wc_path).revision
117     file_id1 = @fs.root.node_id(path_in_repos)
119     assert_equal(rev1, @fs.root.revision)
120     assert_equal(Svn::Core::NODE_FILE, @fs.root.check_path(path_in_repos))
121     assert(@fs.root.file?(path_in_repos))
122     assert(!@fs.root.dir?(path_in_repos))
124     assert_equal([path_in_repos], @fs.root.paths_changed.keys)
125     info = @fs.root.paths_changed[path_in_repos]
126     assert(info.text_mod?)
127     assert(info.add?)
129     File.open(path, "w") {|f| f.print(src)}
130     rev2 = ctx.commit(@wc_path).revision
131     file_id2 = @fs.root.node_id(path_in_repos)
133     assert_equal(src, @fs.root.file_contents(path_in_repos){|f| f.read})
134     assert_equal(src.length, @fs.root.file_length(path_in_repos))
135     assert_equal(MD5.new(src).hexdigest,
136                  @fs.root.file_md5_checksum(path_in_repos))
138     assert_equal([path_in_repos], @fs.root.paths_changed.keys)
139     info = @fs.root.paths_changed[path_in_repos]
140     assert(info.text_mod?)
141     assert(info.modify?)
143     assert_equal([path_in_repos, rev2],
144                  @fs.root.node_history(file).location)
145     assert_equal([path_in_repos, rev2],
146                  @fs.root.node_history(file).prev.location)
147     assert_equal([path_in_repos, rev1],
148                  @fs.root.node_history(file).prev.prev.location)
150     assert(!@fs.root.dir?(path_in_repos))
151     assert(@fs.root.file?(path_in_repos))
153     assert(file_id1.related?(file_id2))
154     assert_equal(1, file_id1.compare(file_id2))
155     assert_equal(1, file_id2.compare(file_id1))
157     assert_equal(rev2, @fs.root.node_created_rev(path_in_repos))
158     assert_equal(path_in_repos, @fs.root.node_created_path(path_in_repos))
160     assert_raises(Svn::Error::FsNotTxnRoot) do
161       @fs.root.set_node_prop(path_in_repos, "name", "value")
162     end
163   end
165   def test_transaction
166     log = "sample log"
167     file = "sample.txt"
168     src = "sample source"
169     path_in_repos = "/#{file}"
170     path = File.join(@wc_path, file)
171     prop_name = "prop"
172     prop_value = "value"
174     ctx = make_context(log)
175     File.open(path, "w") {|f| f.print(src)}
176     ctx.add(path)
177     ctx.commit(@wc_path)
179     assert_raises(Svn::Error::FsNoSuchTransaction) do
180       @fs.open_txn("NOT-EXIST")
181     end
183     start_time = Time.now
184     txn1 = @fs.transaction
185     assert_equal([Svn::Core::PROP_REVISION_DATE], txn1.proplist.keys)
186     assert_instance_of(Time, txn1.proplist[Svn::Core::PROP_REVISION_DATE])
187     date = txn1.prop(Svn::Core::PROP_REVISION_DATE)
189     # Subversion's clock is more precise than Ruby's on
190     # Windows.  So this test can fail intermittently because
191     # the begin and end of the range are the same (to 3
192     # decimal places), but the time from Subversion has 6
193     # decimal places so it looks like it's not in the range.
194     # So we just add a smidgen to the end of the Range.
195     assert_operator(start_time..(Time.now + 0.001), :include?, date)
196     txn1.set_prop(Svn::Core::PROP_REVISION_DATE, nil)
197     assert_equal([], txn1.proplist.keys)
198     assert_equal(youngest_rev, txn1.base_revision)
199     assert(txn1.root.txn_root?)
200     assert(!txn1.root.revision_root?)
201     assert_equal(txn1.name, txn1.root.name)
202     assert_equal(txn1.base_revision, txn1.root.base_revision)
204     @fs.transaction do |txn|
205       assert_nothing_raised do
206         @fs.open_txn(txn.name)
207       end
208       txn2 = txn
209     end
211     txn3 = @fs.transaction
213     assert_equal([txn1.name, txn3.name].sort, @fs.transactions.sort)
214     @fs.purge_txn(txn3.name)
215     assert_equal([txn1.name].sort, @fs.transactions.sort)
217     @fs.transaction do |txn|
218       assert(@fs.transactions.include?(txn.name))
219       txn.abort
220       assert(!@fs.transactions.include?(txn.name))
221     end
223     txn4 = @fs.transaction
224     assert_equal({}, txn1.root.node_proplist(path_in_repos))
225     assert_nil(txn1.root.node_prop(path_in_repos, prop_name))
226     txn1.root.set_node_prop(path_in_repos, prop_name, prop_value)
227     assert_equal(prop_value, txn1.root.node_prop(path_in_repos, prop_name))
228     assert_equal({prop_name => prop_value},
229                  txn1.root.node_proplist(path_in_repos))
230     assert(txn1.root.props_changed?(path_in_repos, txn4.root, path_in_repos))
231     assert(!txn1.root.props_changed?(path_in_repos, txn1.root, path_in_repos))
232     txn1.root.set_node_prop(path_in_repos, prop_name, nil)
233     assert_nil(txn1.root.node_prop(path_in_repos, prop_name))
234     assert_equal({}, txn1.root.node_proplist(path_in_repos))
235   end
237   def test_operation
238     log = "sample log"
239     file = "sample.txt"
240     file2 = "sample2.txt"
241     file3 = "sample3.txt"
242     dir = "sample"
243     src = "sample source"
244     path_in_repos = "/#{file}"
245     path2_in_repos = "/#{file2}"
246     path3_in_repos = "/#{file3}"
247     dir_path_in_repos = "/#{dir}"
248     path = File.join(@wc_path, file)
249     path2 = File.join(@wc_path, file2)
250     path3 = File.join(@wc_path, file3)
251     dir_path = File.join(@wc_path, dir)
252     token = @fs.generate_lock_token
253     ctx = make_context(log)
255     @fs.transaction do |txn|
256       txn.root.make_file(file)
257       txn.root.make_dir(dir)
258     end
259     ctx.up(@wc_path)
260     assert(File.exist?(path))
261     assert(File.directory?(dir_path))
263     @fs.transaction do |txn|
264       txn.root.copy(file2, @fs.root, file)
265       txn.root.delete(file)
266       txn.abort
267     end
268     ctx.up(@wc_path)
269     assert(File.exist?(path))
270     assert(!File.exist?(path2))
272     @fs.transaction do |txn|
273       txn.root.copy(file2, @fs.root, file)
274       txn.root.delete(file)
275     end
276     ctx.up(@wc_path)
277     assert(!File.exist?(path))
278     assert(File.exist?(path2))
280     prev_root = @fs.root(youngest_rev - 1)
281     assert(!prev_root.contents_changed?(file, @fs.root, file2))
282     File.open(path2, "w") {|f| f.print(src)}
283     ctx.ci(@wc_path)
284     assert(prev_root.contents_changed?(file, @fs.root, file2))
286     txn1 = @fs.transaction
287     access = Svn::Fs::Access.new(@author)
288     @fs.access = access
289     @fs.access.add_lock_token(token)
290     assert_equal([], @fs.get_locks(file2))
291     lock = @fs.lock(file2)
292     assert_equal(lock.token, @fs.get_lock(file2).token)
293     assert_equal([lock.token],
294                  @fs.get_locks(file2).collect{|l| l.token})
295     @fs.unlock(file2, lock.token)
296     assert_equal([], @fs.get_locks(file2))
298     entries = @fs.root.dir_entries("/")
299     assert_equal([file2, dir].sort, entries.keys.sort)
300     assert_equal(@fs.root.node_id(path2_in_repos).to_s,
301                  entries[file2].id.to_s)
302     assert_equal(@fs.root.node_id(dir_path_in_repos).to_s,
303                  entries[dir].id.to_s)
305     @fs.transaction do |txn|
306       prev_root = @fs.root(youngest_rev - 2)
307       txn.root.revision_link(prev_root, file)
308     end
309     ctx.up(@wc_path)
310     assert(File.exist?(path))
312     closest_root, closet_path = @fs.root.closest_copy(file2)
313     assert_equal(path2_in_repos, closet_path)
314   end
316   def test_delta(use_deprecated_api=false)
317     log = "sample log"
318     file = "source.txt"
319     src = "a\nb\nc\nd\ne\n"
320     modified = "A\nb\nc\nd\nE\n"
321     result = "a\n\n\n\ne\n"
322     expected = "A\n\n\n\nE\n"
323     path_in_repos = "/#{file}"
324     path = File.join(@wc_path, file)
326     ctx = make_context(log)
328     File.open(path, "w") {|f| f.print(src)}
329     ctx.add(path)
330     rev1 = ctx.ci(@wc_path).revision
332     File.open(path, "w") {|f| f.print(modified)}
333     @fs.transaction do |txn|
334       checksum = MD5.new(normalize_line_break(result)).hexdigest
335       stream = txn.root.apply_text(path_in_repos, checksum)
336       stream.write(normalize_line_break(result))
337       stream.close
338     end
339     ctx.up(@wc_path)
340     assert_equal(expected, File.open(path){|f| f.read})
342     rev2 = ctx.ci(@wc_path).revision
343     if use_deprecated_api
344       stream = @fs.root(rev2).file_delta_stream(@fs.root(rev1),
345                                                 path_in_repos,
346                                                 path_in_repos)
347     else
348       stream = @fs.root(rev1).file_delta_stream(path_in_repos,
349                                                 @fs.root(rev2),
350                                                 path_in_repos)
351     end
353     data = ''
354     stream.each{|w| data << w.new_data}
355     assert_equal(normalize_line_break(expected), data)
357     File.open(path, "w") {|f| f.print(src)}
358     rev3 = ctx.ci(@wc_path).revision
360     File.open(path, "w") {|f| f.print(modified)}
361     @fs.transaction do |txn|
362       base_checksum = MD5.new(normalize_line_break(src)).hexdigest
363       checksum = MD5.new(normalize_line_break(result)).hexdigest
364       handler = txn.root.apply_textdelta(path_in_repos,
365                                          base_checksum, checksum)
366       assert_raises(Svn::Error::ChecksumMismatch) do
367         handler.call(nil)
368       end
369     end
370   end
372   def test_delta_with_deprecated_api
373     test_delta(true)
374   end
376   def test_prop
377     log = "sample log"
378     ctx = make_context(log)
379     ctx.checkout(@repos_uri, @wc_path)
380     ctx.mkdir(["#{@wc_path}/new_dir"])
382     start_time = Time.now
383     info = ctx.commit([@wc_path])
385     assert_equal(@author, info.author)
386     assert_equal(@fs.youngest_rev, info.revision)
387     assert_operator(start_time..(Time.now), :include?, info.date)
389     assert_equal(@author, @fs.prop(Svn::Core::PROP_REVISION_AUTHOR))
390     assert_equal(log, @fs.prop(Svn::Core::PROP_REVISION_LOG))
391     assert_equal([
392                    Svn::Core::PROP_REVISION_AUTHOR,
393                    Svn::Core::PROP_REVISION_DATE,
394                    Svn::Core::PROP_REVISION_LOG,
395                  ].sort,
396                  @fs.proplist.keys.sort)
397     @fs.set_prop(Svn::Core::PROP_REVISION_LOG, nil)
398     assert_nil(@fs.prop(Svn::Core::PROP_REVISION_LOG))
399     assert_equal([
400                    Svn::Core::PROP_REVISION_AUTHOR,
401                    Svn::Core::PROP_REVISION_DATE,
402                  ].sort,
403                  @fs.proplist.keys.sort)
404   end
406   def assert_recover
407     path = File.join(@tmp_path, "fs")
408     fs_type = Svn::Fs::TYPE_FSFS
409     config = {Svn::Fs::CONFIG_FS_TYPE => fs_type}
411     yield(:create, [path, config])
413     assert_nothing_raised do
414       yield(:recover, [path], Proc.new{})
415     end
416   end
418   def test_recover_for_backward_compatibility
419     assert_recover do |method, args, block|
420       Svn::Fs::FileSystem.__send__(method, *args, &block)
421     end
422   end
424   def test_recover
425     assert_recover do |method, args, block|
426       Svn::Fs.__send__(method, *args, &block)
427     end
428   end
430   def test_deleted_revision
431     file = "file"
432     log = "sample log"
433     path = File.join(@wc_path, file)
434     path_in_repos = "/#{file}"
435     ctx = make_context(log)
437     FileUtils.touch(path)
438     ctx.add(path)
439     rev1 = ctx.ci(@wc_path).revision
441     ctx.rm_f(path)
442     rev2 = ctx.ci(@wc_path).revision
444     FileUtils.touch(path)
445     ctx.add(path)
446     rev3 = ctx.ci(@wc_path).revision
448     ctx.rm_f(path)
449     rev4 = ctx.ci(@wc_path).revision
451     assert_equal(Svn::Core::INVALID_REVNUM,
452                  @fs.deleted_revision(path_in_repos, 0, rev4))
453     assert_equal(rev2, @fs.deleted_revision(path_in_repos, rev1, rev4))
454     assert_equal(Svn::Core::INVALID_REVNUM,
455                  @fs.deleted_revision(path_in_repos, rev2, rev4))
456     assert_equal(rev4, @fs.deleted_revision(path_in_repos, rev3, rev4))
457     assert_equal(Svn::Core::INVALID_REVNUM,
458                  @fs.deleted_revision(path_in_repos, rev4, rev4))
459   end
461   def test_mergeinfo
462     log = "sample log"
463     file = "sample.txt"
464     src = "sample\n"
465     trunk = File.join(@wc_path, "trunk")
466     branch = File.join(@wc_path, "branch")
467     trunk_path = File.join(trunk, file)
468     branch_path = File.join(branch, file)
469     trunk_in_repos = "/trunk"
470     branch_in_repos = "/branch"
472     ctx = make_context(log)
473     ctx.mkdir(trunk, branch)
474     File.open(trunk_path, "w") {}
475     File.open(branch_path, "w") {}
476     ctx.add(trunk_path)
477     ctx.add(branch_path)
478     rev1 = ctx.commit(@wc_path).revision
480     File.open(branch_path, "w") {|f| f.print(src)}
481     rev2 = ctx.commit(@wc_path).revision
483     assert_equal({}, @fs.root.mergeinfo(trunk_in_repos))
484     ctx.merge(branch, rev1, branch, rev2, trunk)
485     assert_equal({}, @fs.root.mergeinfo(trunk_in_repos))
487     rev3 = ctx.commit(@wc_path).revision
488     mergeinfo = Svn::Core::MergeInfo.parse("#{branch_in_repos}:2")
489     assert_equal({trunk_in_repos => mergeinfo},
490                  @fs.root.mergeinfo(trunk_in_repos))
492     ctx.rm(branch_path)
493     rev4 = ctx.commit(@wc_path).revision
495     ctx.merge(branch, rev3, branch, rev4, trunk)
496     assert(!File.exist?(trunk_path))
497     rev5 = ctx.commit(@wc_path).revision
498     assert_equal({trunk_in_repos => Svn::Core::MergeInfo.parse("#{branch_in_repos}:2,4")},
499                  @fs.root.mergeinfo(trunk_in_repos))
500   end