1# ====================================================================
2#    Licensed to the Apache Software Foundation (ASF) under one
3#    or more contributor license agreements.  See the NOTICE file
4#    distributed with this work for additional information
5#    regarding copyright ownership.  The ASF licenses this file
6#    to you under the Apache License, Version 2.0 (the
7#    "License"); you may not use this file except in compliance
8#    with the License.  You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12#    Unless required by applicable law or agreed to in writing,
13#    software distributed under the License is distributed on an
14#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15#    KIND, either express or implied.  See the License for the
16#    specific language governing permissions and limitations
17#    under the License.
18# ====================================================================
19
20require "my-assertions"
21require "util"
22
23require "svn/core"
24require "svn/client"
25
26class SvnClientTest < Test::Unit::TestCase
27  include SvnTestUtil
28
29  def setup
30    setup_basic(true)
31  end
32
33  def teardown
34    teardown_basic
35  end
36
37  def test_version
38    assert_equal(Svn::Core.subr_version, Svn::Client.version)
39  end
40
41  def test_add_not_recurse
42    log = "sample log"
43    dir = "dir"
44    dir_path = File.join(@wc_path, dir)
45    path = File.join(dir_path, dir)
46    uri = "#{@repos_uri}/#{dir}/#{dir}"
47
48    make_context(log) do |ctx|
49      FileUtils.mkdir(dir_path)
50      FileUtils.mkdir(path)
51      ctx.add(dir_path, false)
52      ctx.commit(@wc_path)
53
54      assert_raise(Svn::Error::FS_NOT_FOUND) do
55        ctx.cat(uri)
56      end
57    end
58  end
59
60  def test_add_recurse
61    log = "sample log"
62    file = "hello.txt"
63    src = "Hello"
64    dir = "dir"
65    dir_path = File.join(@wc_path, dir)
66    path = File.join(dir_path, file)
67    uri = "#{@repos_uri}/#{dir}/#{file}"
68
69    make_context(log) do |ctx|
70      FileUtils.mkdir(dir_path)
71      File.open(path, "w") {|f| f.print(src)}
72      ctx.add(dir_path)
73      ctx.commit(@wc_path)
74
75      assert_equal(src, ctx.cat(uri))
76    end
77  end
78
79  def test_add_force
80    log = "sample log"
81    file = "hello.txt"
82    src = "Hello"
83    dir = "dir"
84    dir_path = File.join(@wc_path, dir)
85    path = File.join(dir_path, file)
86    uri = "#{@repos_uri}/#{dir}/#{file}"
87
88    make_context(log) do |ctx|
89      FileUtils.mkdir(dir_path)
90      File.open(path, "w") {|f| f.print(src)}
91      ctx.add(dir_path, false)
92      ctx.commit(@wc_path)
93
94      assert_raise(Svn::Error::ENTRY_EXISTS) do
95        ctx.add(dir_path, true, false)
96      end
97
98      ctx.add(dir_path, true, true)
99      ctx.commit(@wc_path)
100      assert_equal(src, ctx.cat(uri))
101    end
102  end
103
104  def test_add_no_ignore
105    log = "sample log"
106    file = "hello.txt"
107    src = "Hello"
108    dir = "dir"
109    dir_path = File.join(@wc_path, dir)
110    path = File.join(dir_path, file)
111    uri = "#{@repos_uri}/#{dir}/#{file}"
112
113    make_context(log) do |ctx|
114      FileUtils.mkdir(dir_path)
115      ctx.add(dir_path, false)
116      ctx.propset(Svn::Core::PROP_IGNORE, file, dir_path)
117      ctx.commit(@wc_path)
118
119      File.open(path, "w") {|f| f.print(src)}
120
121      ctx.add(dir_path, true, true, false)
122      ctx.commit(@wc_path)
123      assert_raise(Svn::Error::FS_NOT_FOUND) do
124        ctx.cat(uri)
125      end
126
127      ctx.add(dir_path, true, true, true)
128      ctx.commit(@wc_path)
129      assert_equal(src, ctx.cat(uri))
130    end
131  end
132
133  def test_mkdir
134    log = "sample log"
135    dir = "dir"
136    deep_dir = ["d", "e", "e", "p"]
137    dir2 = "dir2"
138    dir_uri = "#{@repos_uri}/#{dir}"
139    deep_dir_uri = "#{@repos_uri}/#{deep_dir.join('/')}"
140    dir2_uri = "#{@repos_uri}/#{dir2}"
141    dir_path = File.join(@wc_path, dir)
142    deep_dir_path = File.join(@wc_path, *deep_dir)
143    dir2_path = File.join(@wc_path, dir2)
144
145    make_context(log) do |ctx|
146
147      assert(!File.exist?(dir_path))
148      ctx.mkdir(dir_path)
149      assert(File.exist?(dir_path))
150      assert_raises(Svn::Error::EntryExists) do
151        ctx.add(dir_path)
152      end
153      old_rev = ctx.commit(@wc_path).revision
154
155      new_rev = ctx.mkdir(dir2_uri).revision
156      assert_equal(old_rev + 1, new_rev)
157      assert_raises(Svn::Error::FsAlreadyExists) do
158        ctx.mkdir(dir2_uri)
159      end
160      assert(!File.exist?(dir2_path))
161      ctx.update(@wc_path)
162      assert(File.exist?(dir2_path))
163
164      assert_raises(Svn::Error::SvnError) do
165        ctx.mkdir(deep_dir_path)
166      end
167    end
168  end
169
170  def assert_mkdir_with_multiple_paths
171    log = "sample log"
172    dir = "dir"
173    dir2 = "dir2"
174    dirs = [dir, dir2]
175    dirs_path = dirs.collect {|d| Pathname.new(@wc_path) + d}
176    dirs_full_path = dirs_path.collect {|path| path.expand_path}
177
178    make_context(log) do |ctx|
179
180      infos = []
181      ctx.set_notify_func do |notify|
182        if notify.action != Svn::Wc::NOTIFY_COMMIT_FINALIZING
183          infos << [notify.path, notify]
184        end
185      end
186
187      assert_equal([false, false], dirs_path.collect {|path| path.exist?})
188      yield(ctx, dirs_path.collect {|path| path.to_s})
189      assert_equal(dirs_path.collect {|path| path.to_s}.sort,
190                   infos.collect{|path, notify| path}.sort)
191      assert_equal([true] * dirs_path.size,
192                   infos.collect{|path, notify| notify.add?})
193      assert_equal([true, true], dirs_path.collect {|path| path.exist?})
194
195      infos.clear
196      ctx.commit(@wc_path)
197      assert_equal(dirs_full_path.collect {|path| path.to_s}.sort,
198                   infos.collect{|path, notify| path}.sort)
199      assert_equal([true] * dirs_path.size,
200                   infos.collect{|path, notify| notify.commit_added?})
201    end
202  end
203
204  def test_mkdir_with_multiple_paths
205    assert_mkdir_with_multiple_paths do |ctx, dirs|
206      ctx.mkdir(*dirs)
207    end
208  end
209
210  def test_mkdir_with_multiple_paths_as_array
211    assert_mkdir_with_multiple_paths do |ctx, dirs|
212      ctx.mkdir(dirs)
213    end
214  end
215
216  def test_mkdir_p
217    log = "sample log"
218    dir = "parent"
219    child_dir = "parent/child"
220    dir_path = Pathname.new(@wc_path) + dir
221    child_dir_path = dir_path + "child"
222    full_paths = [dir_path, child_dir_path].collect {|path| path.expand_path}
223
224    make_context(log) do |ctx|
225
226      infos = []
227      ctx.set_notify_func do |notify|
228        if notify.action != Svn::Wc::NOTIFY_COMMIT_FINALIZING
229          infos << [notify.path, notify]
230        end
231      end
232
233      assert_equal([false, false], [dir_path.exist?, child_dir_path.exist?])
234      ctx.mkdir_p(child_dir_path.to_s)
235      assert_equal(full_paths.collect {|path| path.to_s}.sort,
236                   infos.collect{|path, notify| path}.sort)
237      assert_equal([true, true],
238                   infos.collect{|path, notify| notify.add?})
239      assert_equal([true, true], [dir_path.exist?, child_dir_path.exist?])
240
241      infos.clear
242      ctx.commit(@wc_path)
243      assert_equal(full_paths.collect {|path| path.to_s}.sort,
244                   infos.collect{|path, notify| path}.sort)
245      assert_equal([true, true],
246                   infos.collect{|path, notify| notify.commit_added?})
247    end
248  end
249
250  def test_delete
251    log = "sample log"
252    src = "sample source\n"
253    file = "file.txt"
254    dir = "dir"
255    path = File.join(@wc_path, file)
256    dir_path = File.join(@wc_path, dir)
257
258    make_context(log) do |ctx|
259
260      File.open(path, "w") {|f| f.print(src)}
261      ctx.add(path)
262      ctx.mkdir(dir_path)
263      ctx.commit(@wc_path)
264
265      ctx.delete([path, dir_path])
266      ctx.commit(@wc_path)
267      assert(!File.exist?(path))
268      assert(!File.exist?(dir_path))
269
270
271      File.open(path, "w") {|f| f.print(src)}
272      ctx.add(path)
273      ctx.commit(@wc_path)
274
275      File.open(path, "w") {|f| f.print(src * 2)}
276      assert_raises(Svn::Error::ClientModified) do
277        ctx.delete(path)
278      end
279      assert_nothing_raised do
280        ctx.delete(path, true)
281        ctx.commit(@wc_path)
282      end
283      assert(!File.exist?(path))
284    end
285  end
286
287  def test_delete_alias
288    log = "sample log"
289    src = "sample source\n"
290    file = "file.txt"
291    dir = "dir"
292    path = File.join(@wc_path, file)
293    dir_path = File.join(@wc_path, dir)
294
295    make_context(log) do |ctx|
296
297      File.open(path, "w") {|f| f.print(src)}
298      ctx.add(path)
299      ctx.mkdir(dir_path)
300      ctx.commit(@wc_path)
301
302      ctx.rm([path, dir_path])
303      ctx.commit(@wc_path)
304      assert(!File.exist?(path))
305      assert(!File.exist?(dir_path))
306
307
308      File.open(path, "w") {|f| f.print(src)}
309      ctx.add(path)
310      ctx.commit(@wc_path)
311
312      File.open(path, "w") {|f| f.print(src * 2)}
313      assert_raises(Svn::Error::ClientModified) do
314        ctx.rm(path)
315      end
316      assert_nothing_raised do
317        ctx.rm_f(path)
318        ctx.commit(@wc_path)
319      end
320      assert(!File.exist?(path))
321
322      File.open(path, "w") {|f| f.print(src)}
323      ctx.add(path)
324      ctx.mkdir(dir_path)
325      ctx.commit(@wc_path)
326
327      ctx.rm_f(path, dir_path)
328      ctx.commit(@wc_path)
329      assert(!File.exist?(path))
330      assert(!File.exist?(dir_path))
331    end
332  end
333
334  def test_import
335    src = "source\n"
336    log = "sample log"
337    deep_dir = File.join(%w(a b c d e))
338    file = "sample.txt"
339    deep_dir_path = File.join(@wc_path, deep_dir)
340    path = File.join(deep_dir_path, file)
341    tmp_deep_dir_path = File.join(@import_path, deep_dir)
342    tmp_path = File.join(tmp_deep_dir_path, file)
343
344    make_context(log) do |ctx|
345
346      FileUtils.mkdir_p(tmp_deep_dir_path)
347      File.open(tmp_path, "w") {|f| f.print(src)}
348
349      ctx.import(@import_path, @repos_uri)
350
351      ctx.up(@wc_path)
352      assert_equal(src, File.open(path){|f| f.read})
353    end
354  end
355
356  def test_import_custom_revprops
357    src = "source\n"
358    log = "sample log"
359    deep_dir = File.join(%w(a b c d e))
360    file = "sample.txt"
361    deep_dir_path = File.join(@wc_path, deep_dir)
362    path = File.join(deep_dir_path, file)
363    tmp_deep_dir_path = File.join(@import_path, deep_dir)
364    tmp_path = File.join(tmp_deep_dir_path, file)
365
366    make_context(log) do |ctx|
367
368      FileUtils.mkdir_p(tmp_deep_dir_path)
369      File.open(tmp_path, "w") {|f| f.print(src)}
370
371      new_rev = ctx.import(@import_path, @repos_uri, true, false,
372                           {"custom-prop" => "some-value"}).revision
373      assert_equal(["some-value", new_rev],
374                   ctx.revprop_get("custom-prop", @repos_uri, new_rev))
375
376      ctx.up(@wc_path)
377      assert_equal(src, File.open(path){|f| f.read})
378    end
379  end
380
381  def test_commit
382    log = "sample log"
383    dir1 = "dir1"
384    dir2 = "dir2"
385    dir1_path = File.join(@wc_path, dir1)
386    dir2_path = File.join(dir1_path, dir2)
387
388    make_context(log) do |ctx|
389      assert_equal(Svn::Core::INVALID_REVNUM,ctx.commit(@wc_path).revision)
390      ctx.mkdir(dir1_path)
391      assert_equal(0, youngest_rev)
392      assert_equal(1, ctx.commit(@wc_path).revision)
393      ctx.mkdir(dir2_path)
394      assert_equal(Svn::Core::INVALID_REVNUM,ctx.commit(@wc_path, false).revision)
395      assert_equal(2, ctx.ci(@wc_path).revision)
396    end
397  end
398
399  def test_status
400    log = "sample log"
401    file1 = "sample1.txt"
402    file2 = "sample2.txt"
403    dir = "dir"
404    dir_path = File.join(@wc_path, dir)
405    path1 = File.join(@wc_path, file1)
406    path2 = File.join(dir_path, file2)
407
408    make_context(log) do |ctx|
409      File.open(path1, "w") {}
410      ctx.add(path1)
411      rev1 = ctx.commit(@wc_path).revision
412
413
414      ctx.mkdir(dir_path)
415      File.open(path2, "w") {}
416
417      infos = []
418      rev = ctx.status(@wc_path) do |path, status|
419        infos << [path, status]
420      end
421
422      assert_equal(youngest_rev, rev)
423      assert_equal([dir_path, path2].sort,
424                   infos.collect{|path, status| path}.sort)
425      dir_status = infos.assoc(dir_path).last
426      assert(dir_status.text_added?)
427      assert(dir_status.entry.dir?)
428      assert(dir_status.entry.add?)
429      path2_status = infos.assoc(path2).last
430      assert(!path2_status.text_added?)
431      assert_nil(path2_status.entry)
432
433
434      infos = []
435      rev = ctx.st(@wc_path, rev1, true, true) do |path, status|
436        infos << [path, status]
437      end
438
439      assert_equal(rev1, rev)
440      assert_equal([@wc_path, dir_path, path1, path2].sort,
441                   infos.collect{|path, status| path}.sort)
442      wc_status = infos.assoc(@wc_path).last
443      assert(wc_status.text_normal?)
444      assert(wc_status.entry.dir?)
445      assert(wc_status.entry.normal?)
446      dir_status = infos.assoc(dir_path).last
447      assert(dir_status.text_added?)
448      assert(dir_status.entry.dir?)
449      assert(dir_status.entry.add?)
450      path1_status = infos.assoc(path1).last
451      assert(path1_status.text_normal?)
452      assert(path1_status.entry.file?)
453      assert(path1_status.entry.normal?)
454      path2_status = infos.assoc(path2).last
455      assert(!path2_status.text_added?)
456      assert_nil(path2_status.entry)
457
458
459      ctx.prop_set(Svn::Core::PROP_IGNORE, file2, dir_path)
460
461      infos = []
462      rev = ctx.status(@wc_path, nil, true, true, true, false) do |path, status|
463        infos << [path, status]
464      end
465
466      assert_equal(rev1, rev)
467      assert_equal([@wc_path, dir_path, path1].sort,
468                   infos.collect{|path, status| path}.sort)
469
470
471      infos = []
472      rev = ctx.status(@wc_path, nil, true, true, true, true) do |path, status|
473        infos << [path, status]
474      end
475
476      assert_equal(rev1, rev)
477      assert_equal([@wc_path, dir_path, path1, path2].sort,
478                   infos.collect{|path, status| path}.sort)
479      end
480  end
481
482  def test_status_with_depth
483    setup_greek_tree
484
485    log = "sample log"
486      make_context(log) do |ctx|
487
488      # make everything out-of-date
489      ctx.prop_set('propname', 'propvalue', @greek.path(:b), :infinity)
490
491      recurse_and_depth_choices.each do |rd|
492        ctx.status(@greek.path(:mu), nil, rd) do |path, status|
493          assert_equal @greek.uri(:mu), status.url
494        end
495      end
496
497      expected_statuses_by_depth = {
498        true => [:beta, :b, :lambda, :e, :f, :alpha],
499        false => [:b, :lambda, :e, :f],
500        'empty' => [:b],
501        'files' => [:b, :lambda],
502        'immediates' => [:b, :lambda, :e, :f],
503        'infinity' => [:beta, :b, :lambda, :e, :f, :alpha],
504      }
505
506      recurse_and_depth_choices.each do |rd|
507        urls = []
508        ctx.status(@greek.path(:b), nil, rd) do |path, status|
509          urls << status.url
510        end
511        assert_equal(expected_statuses_by_depth[rd].map{|s| @greek.uri(s)}.sort,
512                     urls.sort,
513                     "depth '#{rd}")
514      end
515    end
516  end
517
518  def test_checkout
519    log = "sample log"
520    file = "hello.txt"
521    dir = "dir"
522    dir_path = File.join(@wc_path, dir)
523    path = File.join(dir_path, file)
524    content = "Hello"
525
526    wc_path2 = @wc_path + '2'
527    path2 = File.join(wc_path2, dir, file)
528    wc_path3 = @wc_path + '3'
529    path3 = File.join(wc_path3, dir, file)
530
531    make_context(log) do |ctx|
532      ctx.mkdir(dir_path)
533      File.open(path, "w"){|f| f.print(content)}
534      ctx.add(path)
535      ctx.commit(@wc_path)
536
537      ctx.checkout(@repos_uri, wc_path2)
538      assert(File.exist?(path2))
539
540      ctx.co(@repos_uri, wc_path3, nil, nil, false)
541      assert(!File.exist?(path3))
542    end
543  ensure
544    remove_recursively_with_retry(wc_path3)
545    remove_recursively_with_retry(wc_path2)
546  end
547
548  def test_update
549    log = "sample log"
550    file = "hello.txt"
551    path = File.join(@wc_path, file)
552    content = "Hello"
553    File.open(path, "w"){|f| f.print(content)}
554
555    make_context(log) do |ctx|
556
557      assert_nothing_raised do
558        ctx.update(File.join(@wc_path, "non-exist"), youngest_rev)
559      end
560
561      ctx.add(path)
562      commit_info = ctx.commit(@wc_path)
563
564      FileUtils.rm(path)
565      assert(!File.exist?(path))
566      assert_equal(commit_info.revision,
567                   ctx.update(path, commit_info.revision))
568      assert_equal(content, File.read(path))
569
570      FileUtils.rm(path)
571      assert(!File.exist?(path))
572      assert_equal([commit_info.revision],
573                   ctx.update([path], commit_info.revision))
574      assert_equal(content, File.read(path))
575
576      assert_raise(Svn::Error::FS_NO_SUCH_REVISION) do
577        begin
578          ctx.update(path, commit_info.revision + 1)
579        ensure
580          ctx.cleanup(@wc_path)
581        end
582      end
583      assert_nothing_raised do
584        ctx.update(path + "non-exist", commit_info.revision)
585      end
586    end
587  end
588
589  def test_revert
590    log = "sample log"
591    file1 = "hello1.txt"
592    file2 = "hello2.txt"
593    file3 = "hello3.txt"
594    dir = "dir"
595    dir_path = File.join(@wc_path, dir)
596    path1 = File.join(@wc_path, file1)
597    path2 = File.join(@wc_path, file2)
598    path3 = File.join(dir_path, file3)
599    content = "Hello"
600
601    make_context(log) do |ctx|
602      File.open(path1, "w"){|f| f.print(content)}
603      File.open(path2, "w"){|f| f.print(content)}
604      ctx.add(path1)
605      ctx.add(path2)
606      ctx.mkdir(dir_path)
607      File.open(path3, "w"){|f| f.print(content)}
608      ctx.add(path3)
609      commit_info = ctx.commit(@wc_path)
610
611      File.open(path1, "w"){}
612      assert_equal("", File.open(path1){|f| f.read})
613
614      ctx.revert(path1)
615      assert_equal(content, File.open(path1){|f| f.read})
616
617      File.open(path1, "w"){}
618      File.open(path2, "w"){}
619      assert_equal("", File.open(path1){|f| f.read})
620      assert_equal("", File.open(path2){|f| f.read})
621      ctx.revert([path1, path2])
622      assert_equal(content, File.open(path1){|f| f.read})
623      assert_equal(content, File.open(path2){|f| f.read})
624
625      File.open(path1, "w"){}
626      File.open(path2, "w"){}
627      File.open(path3, "w"){}
628      assert_equal("", File.open(path1){|f| f.read})
629      assert_equal("", File.open(path2){|f| f.read})
630      assert_equal("", File.open(path3){|f| f.read})
631      ctx.revert(@wc_path)
632      assert_equal(content, File.open(path1){|f| f.read})
633      assert_equal(content, File.open(path2){|f| f.read})
634      assert_equal(content, File.open(path3){|f| f.read})
635
636      File.open(path1, "w"){}
637      File.open(path2, "w"){}
638      File.open(path3, "w"){}
639      assert_equal("", File.open(path1){|f| f.read})
640      assert_equal("", File.open(path2){|f| f.read})
641      assert_equal("", File.open(path3){|f| f.read})
642      ctx.revert(@wc_path, false)
643      assert_equal("", File.open(path1){|f| f.read})
644      assert_equal("", File.open(path2){|f| f.read})
645      assert_equal("", File.open(path3){|f| f.read})
646
647      File.open(path1, "w"){}
648      File.open(path2, "w"){}
649      File.open(path3, "w"){}
650      assert_equal("", File.open(path1){|f| f.read})
651      assert_equal("", File.open(path2){|f| f.read})
652      assert_equal("", File.open(path3){|f| f.read})
653      ctx.revert(dir_path)
654      assert_equal("", File.open(path1){|f| f.read})
655      assert_equal("", File.open(path2){|f| f.read})
656      assert_equal(content, File.open(path3){|f| f.read})
657    end
658  end
659
660  def test_log
661    log1 = "sample log1"
662    log2 = "sample log2"
663    log3 = "sample log3"
664    src1 = "source1\n"
665    src2 = "source2\n"
666    src3 = "source3\n"
667    file1 = "sample1.txt"
668    file2 = "sample2.txt"
669    file3 = "sample3.txt"
670    path1 = File.join(@wc_path, file1)
671    path2 = File.join(@wc_path, file2)
672    path3 = File.join(@wc_path, file3)
673    abs_path1 = File.join('', file1)
674    abs_path2 = File.join('', file2)
675    abs_path3 = File.join('', file3)
676
677    rev1 = make_context(log1) do |ctx|
678      File.open(path1, "w") {|f| f.print(src1)}
679      ctx.add(path1)
680      rev1 = ctx.ci(@wc_path).revision
681    end
682
683    rev2 = make_context(log2) do |ctx|
684      ctx.cp(path1, path2)
685      rev2 = ctx.ci(@wc_path).revision
686    end
687
688    make_context(log3) do |ctx|
689      ctx.cp(path1, path3)
690      File.open(path1, "w") {|f| f.print(src2)}
691      File.open(path3, "w") {|f| f.print(src3)}
692      rev3 = ctx.ci(@wc_path).revision
693
694      changed_paths_lists = {}
695      revs = {}
696      messages = {}
697      keys = [@wc_path, path1, path2, path3]
698      keys.each do |key|
699        revs[key] = []
700        changed_paths_lists[key] = []
701        messages[key] = []
702        args = [key, 1, "HEAD", 0, true, nil]
703        ctx.log(*args) do |changed_paths, rev, author, date, message|
704          revs[key] << rev
705          changed_paths_lists[key] << changed_paths
706          messages[key] << message
707        end
708      end
709      changed_paths_list = changed_paths_lists[@wc_path]
710
711      assert_equal([rev1, rev2, rev3], revs[@wc_path])
712      assert_equal([rev1, rev3], revs[path1])
713      assert_equal([rev1, rev2], revs[path2])
714      assert_equal([rev1, rev3], revs[path3])
715      assert_equal([log1, log2, log3], messages[@wc_path])
716
717      expected = [[abs_path1], [abs_path2], [abs_path1, abs_path3]]
718      actual = changed_paths_list.collect {|changed_paths| changed_paths.keys}
719      assert_nested_sorted_array(expected, actual)
720
721      assert_equal('A', changed_paths_list[0][abs_path1].action)
722      assert_false(changed_paths_list[0][abs_path1].copied?)
723      assert_equal('A', changed_paths_list[1][abs_path2].action)
724      assert_true(changed_paths_list[1][abs_path2].copied?)
725      assert_equal(abs_path1, changed_paths_list[1][abs_path2].copyfrom_path)
726      assert_equal(rev1, changed_paths_list[1][abs_path2].copyfrom_rev)
727      assert_equal('M', changed_paths_list[2][abs_path1].action)
728      assert_equal('A', changed_paths_list[2][abs_path3].action)
729    end
730  end
731
732  def test_log_message
733    log = "sample log"
734    file = "hello.txt"
735    path = File.join(@wc_path, file)
736    FileUtils.touch(path)
737
738    make_context(log) do |ctx|
739      ctx.add(path)
740      commit_info = ctx.commit(@wc_path)
741      rev = commit_info.revision
742
743      assert_equal(log, ctx.log_message(path, rev))
744    end
745  end
746
747  def test_blame
748    log = "sample log"
749    file = "hello.txt"
750    srcs = %w(first second third)
751    infos = []
752    path = File.join(@wc_path, file)
753
754    make_context(log) do |ctx|
755
756      File.open(path, "w") {|f| f.puts(srcs[0])}
757      ctx.add(path)
758      commit_info = ctx.commit(@wc_path)
759      infos << [0, commit_info.revision, @author, commit_info.date, srcs[0]]
760
761      File.open(path, "a") {|f| f.puts(srcs[1])}
762      commit_info = ctx.commit(@wc_path)
763      infos << [1, commit_info.revision, @author, commit_info.date, srcs[1]]
764
765      File.open(path, "a") {|f| f.puts(srcs[2])}
766      commit_info = ctx.commit(@wc_path)
767      infos << [2, commit_info.revision, @author, commit_info.date, srcs[2]]
768
769      result = []
770      ctx.blame(path) do |line_no, revision, author, date, line|
771        result << [line_no, revision, author, date, line]
772      end
773      assert_equal(infos, result)
774
775
776      ctx.prop_set(Svn::Core::PROP_MIME_TYPE, "image/DUMMY", path)
777      ctx.commit(@wc_path)
778
779      assert_raise(Svn::Error::CLIENT_IS_BINARY_FILE) do
780        ctx.ann(path) {}
781      end
782    end
783  end
784
785  def test_diff
786    log = "sample log"
787    before = "before\n"
788    after = "after\n"
789    file = "hello.txt"
790    path = File.join(@wc_path, file)
791
792    File.open(path, "w") {|f| f.print(before)}
793
794    make_context(log) do |ctx|
795      ctx.add(path)
796      commit_info = ctx.commit(@wc_path)
797      rev1 = commit_info.revision
798
799      File.open(path, "w") {|f| f.print(after)}
800
801      out_file = Tempfile.new("svn")
802      err_file = Tempfile.new("svn")
803      ctx.diff([], path, rev1, path, "WORKING", out_file.path, err_file.path)
804      out_file.open
805      assert_match(/-#{before}\+#{after}\z/, out_file.read)
806
807      commit_info = ctx.commit(@wc_path)
808      rev2 = commit_info.revision
809      out_file = Tempfile.new("svn")
810      ctx.diff([], path, rev1, path, rev2, out_file.path, err_file.path)
811      out_file.open
812      assert_match(/-#{before}\+#{after}\z/, out_file.read)
813    end
814  end
815
816  def test_diff_peg
817    log = "sample log"
818    before = "before\n"
819    after = "after\n"
820    file = "hello.txt"
821    path = File.join(@wc_path, file)
822
823    File.open(path, "w") {|f| f.print(before)}
824
825    make_context(log) do |ctx|
826      ctx.add(path)
827      commit_info = ctx.commit(@wc_path)
828      rev1 = commit_info.revision
829
830      File.open(path, "w") {|f| f.print(after)}
831
832      out_file = Tempfile.new("svn")
833      err_file = Tempfile.new("svn")
834      ctx.diff_peg([], path, rev1, "WORKING", out_file.path, err_file.path)
835      out_file.open
836      assert_match(/-#{before}\+#{after}\z/, out_file.read)
837
838      commit_info = ctx.commit(@wc_path)
839      rev2 = commit_info.revision
840      out_file = Tempfile.new("svn")
841      ctx.diff_peg([], path, rev1, rev2, out_file.path, err_file.path)
842      out_file.open
843      assert_match(/-#{before}\+#{after}\z/, out_file.read)
844    end
845  end
846
847  def test_diff_summarize
848    log = "sample log"
849    before = "before\n"
850    after = "after\n"
851    file = "hello.txt"
852    path = File.join(@wc_path, file)
853
854    File.open(path, "w") {|f| f.print(before)}
855
856    make_context(log) do |ctx|
857      ctx.add(path)
858      commit_info = ctx.commit(@wc_path)
859      rev1 = commit_info.revision
860
861      File.open(path, "w") {|f| f.print(after)}
862
863      commit_info = ctx.commit(@wc_path)
864      rev2 = commit_info.revision
865
866      diffs = []
867      ctx.diff_summarize(@wc_path, rev1, @wc_path, rev2) do |diff|
868        diffs << diff
869      end
870      assert_equal([file], diffs.collect {|d| d.path})
871      kinds = diffs.collect do |d|
872        [d.kind_normal?, d.kind_added?, d.kind_modified?, d.kind_deleted?]
873      end
874      assert_equal([[false, false, true, false]], kinds)
875      assert_equal([false], diffs.collect {|d| d.prop_changed?})
876      node_kinds = diffs.collect do |d|
877        [d.node_kind_none?, d.node_kind_file?,
878         d.node_kind_dir?, d.node_kind_unknown?]
879      end
880      assert_equal([[false, true, false, false]], node_kinds)
881    end
882  end
883
884  def test_diff_summarize_peg
885    log = "sample log"
886    before = "before\n"
887    after = "after\n"
888    before_file = "before.txt"
889    after_file = "after.txt"
890    moved_file = "moved.txt"
891    before_path = File.join(@wc_path, before_file)
892    after_path = File.join(@wc_path, after_file)
893    moved_path = File.join(@wc_path, moved_file)
894
895    File.open(before_path, "w") {|f| f.print(before)}
896
897    make_context(log) do |ctx|
898      ctx.add(before_path)
899      commit_info = ctx.commit(@wc_path)
900      rev1 = commit_info.revision
901
902      ctx.mv(before_path, after_path)
903      commit_info = ctx.commit(@wc_path)
904      rev2 = commit_info.revision
905
906      File.open(after_path, "w") {|f| f.print(after)}
907      commit_info = ctx.commit(@wc_path)
908      rev3 = commit_info.revision
909
910      File.open(after_path, "w") {|f| f.print(before)}
911      commit_info = ctx.commit(@wc_path)
912      rev4 = commit_info.revision
913
914      ctx.mv(after_path, moved_path)
915      commit_info = ctx.commit(@wc_path)
916      rev5 = commit_info.revision
917
918      diffs = []
919      ctx.diff_summarize_peg(@repos_uri, rev3, rev4, rev3) do |diff|
920        diffs << diff
921      end
922      assert_equal([after_file], diffs.collect {|d| d.path})
923      kinds = diffs.collect do |d|
924        [d.kind_normal?, d.kind_added?, d.kind_modified?, d.kind_deleted?]
925      end
926      assert_equal([[false, false, true, false]], kinds)
927      assert_equal([false], diffs.collect {|d| d.prop_changed?})
928      node_kinds = diffs.collect do |d|
929        [d.node_kind_none?, d.node_kind_file?,
930         d.node_kind_dir?, d.node_kind_unknown?]
931      end
932      assert_equal([[false, true, false, false]], node_kinds)
933    end
934  end
935
936  def assert_changed(ctx, path)
937    statuses = []
938    ctx.status(path) do |_, status|
939      statuses << status
940    end
941    assert_not_equal([], statuses)
942  end
943
944  def assert_not_changed(ctx, path)
945    statuses = []
946    ctx.status(path) do |_, status|
947      statuses << status
948    end
949    assert_equal([], statuses)
950  end
951
952  def assert_merge
953    log = "sample log"
954    file = "sample.txt"
955    src = "sample\n"
956    trunk = File.join(@wc_path, "trunk")
957    branch = File.join(@wc_path, "branch")
958    branch_relative_uri = "/branch"
959    branch_uri = "#{@repos_uri}#{branch_relative_uri}"
960    trunk_path = File.join(trunk, file)
961    trunk_path_uri = "#{@repos_uri}/trunk/#{file}"
962    branch_path = File.join(branch, file)
963    branch_path_relative_uri = "#{branch_relative_uri}/#{file}"
964    branch_path_uri = "#{@repos_uri}#{branch_path_relative_uri}"
965
966    make_context(log) do |ctx|
967      ctx.mkdir(trunk, branch)
968      File.open(trunk_path, "w") {}
969      File.open(branch_path, "w") {}
970      ctx.add(trunk_path)
971      ctx.add(branch_path)
972      rev1 = ctx.commit(@wc_path).revision
973
974      File.open(branch_path, "w") {|f| f.print(src)}
975      rev2 = ctx.commit(@wc_path).revision
976
977      merged_entries = []
978      ctx.log_merged(trunk, nil, branch_uri, nil) do |entry|
979        merged_entries << entry
980      end
981      assert_equal_log_entries([], merged_entries)
982      assert_nil(ctx.merged(trunk))
983
984      merged_entries = []
985      yield(ctx, branch, rev1, rev2, trunk)
986      ctx.log_merged(trunk, nil, branch_uri, nil) do |entry|
987        merged_entries << entry
988      end
989      assert_equal_log_entries([
990                                [
991                                 {branch_path_relative_uri => ["M", nil, -1]},
992                                 rev2,
993                                 {
994                                   "svn:author" => @author,
995                                   "svn:log" => log,
996                                 },
997                                 false,
998                                ]
999                               ],
1000                               merged_entries)
1001      mergeinfo = ctx.merged(trunk)
1002      assert_not_nil(mergeinfo)
1003      assert_equal([branch_uri], mergeinfo.keys)
1004      ranges = mergeinfo[branch_uri].collect {|range| range.to_a}
1005      assert_equal([[1, 2, true]], ranges)
1006
1007      rev3 = ctx.commit(@wc_path).revision
1008
1009      assert_equal(normalize_line_break(src), ctx.cat(trunk_path, rev3))
1010
1011      ctx.rm(branch_path)
1012      rev4 = ctx.commit(@wc_path).revision
1013
1014      yield(ctx, branch, rev3, rev4, trunk)
1015      assert(!File.exist?(trunk_path))
1016
1017      merged_entries = []
1018      ctx.log_merged(trunk, rev4, branch_uri, rev4) do |entry|
1019        merged_entries << entry
1020      end
1021      assert_equal_log_entries([
1022                                [
1023                                 {branch_path_relative_uri => ["M", nil, -1]},
1024                                 rev2,
1025                                 {
1026                                   "svn:author" => @author,
1027                                   "svn:log" => log,
1028                                 },
1029                                 false,
1030                                ]
1031                               ], merged_entries)
1032
1033      ctx.propdel("svn:mergeinfo", trunk)
1034      merged_entries = []
1035      ctx.log_merged(trunk, rev4, branch_uri, rev4) do |entry|
1036        merged_entries << entry
1037      end
1038      assert_equal_log_entries([
1039                                [
1040                                 {branch_path_relative_uri => ["M", nil, -1]},
1041                                 rev2,
1042                                 {
1043                                   "svn:author" => @author,
1044                                   "svn:log" => log,
1045                                 },
1046                                 false,
1047                                ]
1048                               ], merged_entries)
1049
1050      ctx.revert(trunk)
1051      File.open(trunk_path, "a") {|f| f.print(src)}
1052      yield(ctx, branch, rev3, rev4, trunk)
1053      ctx.revert(trunk, false)
1054      ctx.resolve(:path=>trunk_path,
1055                  :conflict_choice=>Svn::Wc::CONFLICT_CHOOSE_MERGED)
1056      rev5 = ctx.commit(@wc_path).revision
1057      assert(File.exist?(trunk_path))
1058      ctx.up(@wc_path)
1059
1060      yield(ctx, branch, rev3, rev4, trunk, nil, false, true)
1061      assert_changed(ctx, trunk)
1062
1063      ctx.propdel("svn:mergeinfo", trunk)
1064      rev6 = ctx.commit(@wc_path).revision
1065
1066      yield(ctx, branch, rev3, rev4, trunk, nil, false, true, true)
1067      assert_not_changed(ctx, trunk)
1068
1069      yield(ctx, branch, rev3, rev4, trunk, nil, false, true)
1070      assert_changed(ctx, trunk)
1071    end
1072  end
1073
1074  def test_merge
1075    assert_merge do |ctx, from, from_rev1, from_rev2, to, *rest|
1076      ctx.merge(from, from_rev1, from, from_rev2, to, *rest)
1077    end
1078  end
1079
1080  def test_merge_peg
1081    assert_merge do |ctx, from, from_rev1, from_rev2, to, *rest|
1082      ctx.merge_peg(from, from_rev1, from_rev2, to, nil, *rest)
1083    end
1084  end
1085
1086=begin
1087  We haven't yet figured out what to expect in the case of an obstruction,
1088  but it is no longer an error.  Commenting out this test until that
1089  decision is made (see issue #3680:
1090  https://issues.apache.org/jira/browse/SVN-3680)
1091
1092  def test_cleanup
1093    log = "sample log"
1094    file = "sample.txt"
1095    src = "sample\n"
1096    path = File.join(@wc_path, file)
1097
1098    make_context(log) do |ctx|
1099      File.open(path, "w") {|f| f.print(src)}
1100      ctx.add(path)
1101      rev = ctx.commit(@wc_path).revision
1102
1103      ctx.up(@wc_path, rev - 1)
1104      File.open(path, "w") {|f| f.print(src)}
1105
1106      assert_raise(Svn::Error::WC_OBSTRUCTED_UPDATE) do
1107        ctx.up(@wc_path, rev)
1108      end
1109
1110      Svn::Wc::AdmAccess.open(nil, @wc_path, true, -1) do |access|
1111        assert_raise(Svn::Error::WC_LOCKED) do
1112          ctx.commit(@wc_path)
1113        end
1114      end
1115
1116      ctx.set_cancel_func do
1117        raise Svn::Error::CANCELLED
1118      end
1119      Svn::Wc::AdmAccess.open(nil, @wc_path, true, -1) do |access|
1120        assert_raise(Svn::Error::CANCELLED) do
1121          ctx.cleanup(@wc_path)
1122        end
1123        assert_raise(Svn::Error::WC_LOCKED) do
1124          ctx.commit(@wc_path)
1125        end
1126      end
1127
1128      ctx.set_cancel_func(nil)
1129      assert_nothing_raised do
1130        ctx.cleanup(@wc_path)
1131      end
1132      assert_nothing_raised do
1133        ctx.commit(@wc_path)
1134      end
1135    end
1136  end
1137=end
1138
1139  def test_relocate
1140    log = "sample log"
1141    file = "sample.txt"
1142    src = "sample\n"
1143    path = File.join(@wc_path, file)
1144
1145    make_context(log) do |ctx|
1146      File.open(path, "w") {|f| f.print(src)}
1147      ctx.add(path)
1148      ctx.commit(@wc_path)
1149
1150      assert_nothing_raised do
1151        ctx.cat(path)
1152      end
1153
1154      ctx.add_simple_prompt_provider(0) do |cred, realm, username, may_save|
1155        cred.username = @author
1156        cred.password = @password
1157        cred.may_save = true
1158      end
1159      ctx.relocate(@wc_path, @repos_uri, @repos_svnserve_uri)
1160
1161      make_context(log) do |ctx|
1162        # ### TODO: Verify Svn::Error::AuthnNoProvider in error chain
1163        assert_raises(Svn::Error::RaCannotCreateSession) do
1164          ctx.cat(path)
1165        end
1166      end
1167    end
1168  end
1169
1170  def test_resolved
1171    log = "sample log"
1172    file = "sample.txt"
1173    dir = "dir"
1174    src1 = "before\n"
1175    src2 = "after\n"
1176    dir_path = File.join(@wc_path, dir)
1177    path = File.join(dir_path, file)
1178
1179    make_context(log) do |ctx|
1180      ctx.mkdir(dir_path)
1181      File.open(path, "w") {}
1182      ctx.add(path)
1183      rev1 = ctx.ci(@wc_path).revision
1184
1185      File.open(path, "w") {|f| f.print(src1)}
1186      rev2 = ctx.ci(@wc_path).revision
1187
1188      ctx.up(@wc_path, rev1)
1189
1190      File.open(path, "w") {|f| f.print(src2)}
1191      ctx.up(@wc_path)
1192
1193      assert_raises(Svn::Error::WcFoundConflict) do
1194        ctx.ci(@wc_path)
1195      end
1196
1197      ctx.resolved(dir_path, false)
1198      assert_raises(Svn::Error::WcFoundConflict) do
1199        ctx.ci(@wc_path)
1200      end
1201
1202      ctx.resolved(dir_path)
1203      info = nil
1204      assert_nothing_raised do
1205        info = ctx.ci(@wc_path)
1206      end
1207      assert_not_nil(info)
1208      assert_equal(rev2 + 1, info.revision)
1209    end
1210  end
1211
1212  def test_copy
1213    log = "sample log"
1214    src = "source\n"
1215    file1 = "sample1.txt"
1216    file2 = "sample2.txt"
1217    path1 = Pathname.new(@wc_path) + file1
1218    path2 = Pathname.new(@wc_path) + file2
1219    full_path2 = path2.expand_path
1220
1221    make_context(log) do |ctx|
1222      File.open(path1, "w") {|f| f.print(src)}
1223      ctx.add(path1.to_s)
1224
1225      ctx.ci(@wc_path)
1226
1227      ctx.cp(path1.to_s, path2.to_s)
1228
1229      infos = []
1230      ctx.set_notify_func do |notify|
1231        infos << [notify.path, notify]
1232      end
1233      ctx.ci(@wc_path)
1234
1235      assert_equal([full_path2.to_s, '.'].sort,
1236                   infos.collect{|path, notify| path}.sort)
1237      path2_notify = infos.assoc(full_path2.to_s)[1]
1238      assert(path2_notify.commit_added?)
1239      finalizing_notify = infos.assoc('.')[1]
1240      assert(finalizing_notify.action == Svn::Wc::NOTIFY_COMMIT_FINALIZING)
1241      assert_equal(File.open(path1) {|f| f.read},
1242                   File.open(path2) {|f| f.read})
1243    end
1244  end
1245
1246  def test_move
1247    log = "sample log"
1248    src = "source\n"
1249    file1 = "sample1.txt"
1250    file2 = "sample2.txt"
1251    path1 = File.join(@wc_path, file1)
1252    path2 = File.join(@wc_path, file2)
1253
1254    make_context(log) do |ctx|
1255      File.open(path1, "w") {|f| f.print(src)}
1256      ctx.add(path1)
1257
1258      ctx.ci(@wc_path)
1259
1260      ctx.mv(path1, path2)
1261
1262      infos = []
1263      ctx.set_notify_func do |notify|
1264        infos << [notify.path, notify]
1265      end
1266      ctx.ci(@wc_path)
1267
1268      assert_equal([path1, path2].collect do |p|
1269                     File.expand_path(p)
1270                   end.push('.').sort,
1271                   infos.collect{|path, notify| path}.sort)
1272      path1_notify = infos.assoc(File.expand_path(path1))[1]
1273      assert(path1_notify.commit_deleted?)
1274      path2_notify = infos.assoc(File.expand_path(path2))[1]
1275      assert(path2_notify.commit_added?)
1276      finalizing_notify = infos.assoc('.')[1]
1277      assert(finalizing_notify.action == Svn::Wc::NOTIFY_COMMIT_FINALIZING)
1278      assert_equal(src, File.open(path2) {|f| f.read})
1279    end
1280  end
1281
1282  def test_move_force
1283    log = "sample log"
1284    src1 = "source1\n"
1285    src2 = "source2\n"
1286    file1 = "sample1.txt"
1287    file2 = "sample2.txt"
1288    path1 = File.join(@wc_path, file1)
1289    path2 = File.join(@wc_path, file2)
1290
1291    make_context(log) do |ctx|
1292      File.open(path1, "w") {|f| f.print(src1)}
1293      ctx.add(path1)
1294      ctx.ci(@wc_path)
1295
1296      File.open(path1, "w") {|f| f.print(src2)}
1297      assert_nothing_raised do
1298        ctx.mv(path1, path2)
1299      end
1300      ctx.revert([path1, path2])
1301
1302      File.open(path1, "w") {|f| f.print(src2)}
1303      assert_nothing_raised do
1304        ctx.mv_f(path1, path2)
1305      end
1306
1307      notifies = []
1308      ctx.set_notify_func do |notify|
1309        notifies << notify
1310      end
1311      ctx.ci(@wc_path)
1312
1313      paths = notifies.collect do |notify|
1314        notify.path
1315      end
1316      assert_equal([path1, path2, path2].collect do |p|
1317                     File.expand_path(p)
1318                   end.push('.').sort,
1319                   paths.sort)
1320
1321      deleted_paths = notifies.find_all do |notify|
1322        notify.commit_deleted?
1323      end.collect do |notify|
1324        notify.path
1325      end
1326      assert_equal([path1].sort.collect{|p|File.expand_path(p)},
1327                   deleted_paths.sort)
1328
1329      added_paths = notifies.find_all do |notify|
1330        notify.commit_added?
1331      end.collect do |notify|
1332        notify.path
1333      end
1334      assert_equal([path2].sort.collect{|p|File.expand_path(p)},
1335                   added_paths.sort)
1336
1337      postfix_txdelta_paths = notifies.find_all do |notify|
1338        notify.commit_postfix_txdelta?
1339      end.collect do |notify|
1340        notify.path
1341      end
1342      assert_equal([path2].sort.collect{|p|File.expand_path(p)},
1343                   postfix_txdelta_paths.sort)
1344
1345      finalizing_paths = notifies.find_all do |notify|
1346        notify.action == Svn::Wc::NOTIFY_COMMIT_FINALIZING
1347      end.collect do |notify|
1348        notify.path
1349      end
1350      assert_equal(['.'], finalizing_paths)
1351
1352      assert_equal(src2, File.open(path2) {|f| f.read})
1353    end
1354  end
1355
1356  def test_prop
1357    log = "sample log"
1358    dir = "dir"
1359    file = "sample.txt"
1360    dir_path = File.join(@wc_path, dir)
1361    dir_uri = "#{@repos_uri}/#{dir}"
1362    path = File.join(dir_path, file)
1363    uri = "#{dir_uri}/#{file}"
1364    prop_name = "sample-prop"
1365    prop_value = "sample value"
1366    invalid_mime_type_prop_value = "image"
1367
1368    make_context(log) do |ctx|
1369
1370      ctx.mkdir(dir_path)
1371      File.open(path, "w") {}
1372      ctx.add(path)
1373
1374      ctx.commit(@wc_path)
1375
1376      assert_equal({}, ctx.prop_get(prop_name, path))
1377      ctx.prop_set(prop_name, prop_value, path)
1378      ctx.commit(@wc_path)
1379      assert_equal({uri => prop_value}, ctx.pget(prop_name, path))
1380
1381      ctx.prop_del(prop_name, path)
1382      ctx.commit(@wc_path)
1383      assert_equal({}, ctx.pg(prop_name, path))
1384
1385      ctx.ps(prop_name, prop_value, path)
1386      ctx.commit(@wc_path)
1387      assert_equal({uri => prop_value}, ctx.pg(prop_name, path))
1388
1389      ctx.ps(prop_name, nil, path)
1390      ctx.commit(@wc_path)
1391      assert_equal({}, ctx.pg(prop_name, path))
1392
1393      ctx.up(@wc_path)
1394      ctx.ps(prop_name, prop_value, dir_path)
1395      ctx.ci(@wc_path)
1396      assert_equal({
1397                     dir_uri => prop_value,
1398                     uri => prop_value,
1399                   },
1400                   ctx.pg(prop_name, dir_path))
1401
1402      ctx.up(@wc_path)
1403      ctx.pdel(prop_name, dir_path, false)
1404      ctx.ci(@wc_path)
1405      assert_equal({uri => prop_value}, ctx.pg(prop_name, dir_path))
1406
1407      ctx.up(@wc_path)
1408      ctx.pd(prop_name, dir_path)
1409      ctx.ci(@wc_path)
1410      assert_equal({}, ctx.pg(prop_name, dir_path))
1411
1412      ctx.up(@wc_path)
1413      ctx.ps(prop_name, prop_value, dir_path, false)
1414      ctx.ci(@wc_path)
1415      assert_equal({dir_uri => prop_value}, ctx.pg(prop_name, dir_path))
1416
1417      assert_raises(Svn::Error::BadMimeType) do
1418        ctx.ps(Svn::Core::PROP_MIME_TYPE,
1419               invalid_mime_type_prop_value,
1420               path)
1421      end
1422      ctx.cleanup(@wc_path)
1423
1424      assert_nothing_raised do
1425        ctx.ps(Svn::Core::PROP_MIME_TYPE,
1426               invalid_mime_type_prop_value,
1427               path, false, true)
1428      end
1429      ctx.commit(@wc_path)
1430      assert_equal({uri => invalid_mime_type_prop_value},
1431                   ctx.pg(Svn::Core::PROP_MIME_TYPE, path))
1432    end
1433  end
1434
1435  def test_prop_list
1436    log = "sample log"
1437    dir = "dir"
1438    file = "sample.txt"
1439    dir_path = File.join(@wc_path, dir)
1440    path = File.join(dir_path, file)
1441    dir_uri = "#{@repos_uri}/#{dir}"
1442    uri = "#{dir_uri}/#{file}"
1443    name1 = "name1"
1444    name2 = "name2"
1445    value1 = "value1"
1446    value2 = "value2"
1447
1448    make_context(log) do |ctx|
1449
1450      ctx.mkdir(dir_path)
1451      File.open(path, "w") {}
1452      ctx.add(path)
1453
1454      ctx.ci(@wc_path)
1455
1456      assert_equal([], ctx.prop_list(path))
1457
1458      ctx.ps(name1, value1, path)
1459      ctx.ci(@wc_path)
1460      assert_equal([uri], ctx.prop_list(path).collect{|item| item.node_name})
1461      assert_equal([{name1 => value1}],
1462                   ctx.plist(path).collect{|item| item.prop_hash})
1463      assert_equal([value1], ctx.pl(path).collect{|item| item[name1]})
1464
1465      ctx.up(@wc_path)
1466      ctx.ps(name2, value2, dir_path)
1467      ctx.ci(@wc_path)
1468      assert_equal([uri, dir_uri].sort,
1469                   ctx.prop_list(dir_path).collect{|item| item.name})
1470      prop_list = ctx.plist(dir_path).collect{|item| [item.name, item.props]}
1471      props = prop_list.assoc(uri)[1]
1472      dir_props = prop_list.assoc(dir_uri)[1]
1473      assert_equal({name1 => value1, name2 => value2}, props)
1474      assert_equal({name2 => value2}, dir_props)
1475    end
1476  end
1477
1478  def recurse_and_depth_choices
1479    [false, true, 'empty', 'files', 'immediates', 'infinity']
1480  end
1481
1482  def test_file_prop
1483    setup_greek_tree
1484
1485    log = "sample log"
1486    make_context(log) do |ctx|
1487
1488      # when no props set, everything is empty
1489      recurse_and_depth_choices.each do |rd|
1490        assert_equal([],
1491                     ctx.prop_list(@greek.path(:mu), nil, nil, rd),
1492                     "prop_list with Depth '#{rd}'")
1493      end
1494
1495      recurse_and_depth_choices.each do |rd|
1496        assert_equal({},
1497                     ctx.prop_get(rd.to_s, @greek.path(:mu), nil, nil, rd),
1498                     "prop_get with Depth '#{rd}'")
1499      end
1500
1501      # set some props
1502      recurse_and_depth_choices.each do |rd|
1503        ctx.prop_set(rd.to_s, rd.to_s, @greek.path(:mu), rd)
1504      end
1505      ctx.commit(@greek.path(:mu))
1506
1507      # get the props
1508      recurse_and_depth_choices.each do |rd|
1509        assert_equal({@greek.uri(:mu) => rd.to_s},
1510                     ctx.prop_get(rd.to_s, @greek.path(:mu), nil, nil, rd),
1511                     "prop_get with Depth '#{rd}'")
1512      end
1513
1514      prop_hash = {}
1515      recurse_and_depth_choices.each {|rd| prop_hash[rd.to_s] = rd.to_s}
1516
1517      # list the props
1518      recurse_and_depth_choices.each do |rd|
1519        props = ctx.prop_list(@greek.path(:mu), nil, nil, rd)
1520        assert_equal([@greek.uri(:mu)],
1521                     props.collect {|item| item.node_name},
1522                     "prop_list (node_name) with Depth '#{rd}'")
1523
1524        props = ctx.plist(@greek.path(:mu), nil, nil, rd)
1525        assert_equal([prop_hash],
1526                     props.collect {|item| item.prop_hash},
1527                     "prop_list (prop_hash) with Depth '#{rd}'")
1528
1529        recurse_and_depth_choices.each do |rd1|
1530          props = ctx.plist(@greek.path(:mu), nil, nil, rd)
1531          assert_equal([rd1.to_s],
1532                       props.collect {|item| item[rd1.to_s]},
1533                       "prop_list (#{rd1.to_s}]) with Depth '#{rd}'")
1534        end
1535      end
1536    end
1537  end
1538
1539  def test_dir_prop
1540    setup_greek_tree
1541
1542    log = "sample log"
1543    make_context(log) do |ctx|
1544
1545      # when no props set, everything is empty
1546      recurse_and_depth_choices.each do |rd|
1547        assert_equal([],
1548                     ctx.prop_list(@greek.path(:b), nil, nil, rd),
1549                     "prop_list with Depth '#{rd}'")
1550      end
1551
1552      recurse_and_depth_choices.each do |rd|
1553        assert_equal({},
1554                     ctx.prop_get(rd.to_s, @greek.path(:b), nil, nil, rd),
1555                     "prop_get with Depth '#{rd}'")
1556      end
1557
1558      # set some props with various depths
1559      recurse_and_depth_choices.each do |rd|
1560        ctx.prop_set(rd.to_s, rd.to_s, @greek.path(:b), rd)
1561      end
1562      ctx.commit(@greek.path(:b))
1563
1564      expected_props = {
1565        true => [:beta, :b, :lambda, :e, :f, :alpha],
1566        false => [:b],
1567        'empty' => [:b],
1568        'files' => [:b, :lambda],
1569        'immediates' => [:b, :lambda, :e, :f],
1570        'infinity' => [:beta, :b, :lambda, :e, :f, :alpha],
1571      }
1572
1573      paths = [:b, :e, :alpha, :beta, :f, :lambda]
1574
1575      # how are the props set?
1576      recurse_and_depth_choices.each do |rd|
1577        paths.each do |path|
1578          if expected_props[rd].include?(path)
1579            expected = {@greek.uri(path) => rd.to_s}
1580          else
1581            expected = {}
1582          end
1583          assert_equal(expected,
1584                       ctx.prop_get(rd.to_s, @greek.path(path), nil, nil, false),
1585                       "prop_get #{@greek.resolve(path)} with Depth '#{rd}'")
1586        end
1587      end
1588
1589      recurse_and_depth_choices.each do |rd_for_prop|
1590        recurse_and_depth_choices.each do |rd_for_depth|
1591          expected = {}
1592          expected_paths = expected_props[rd_for_depth]
1593          expected_paths &= expected_props[rd_for_prop]
1594          expected_paths.each do |path|
1595            expected[@greek.uri(path)] = rd_for_prop.to_s
1596          end
1597
1598          assert_equal(expected,
1599                       ctx.prop_get(rd_for_prop.to_s, @greek.path(:b),
1600                                    nil, nil, rd_for_depth),
1601                       "prop_get '#{rd_for_prop}' with Depth '#{rd_for_depth}'")
1602
1603        end
1604      end
1605
1606      recurse_and_depth_choices.each do |rd|
1607        props = ctx.prop_list(@greek.path(:b), nil, nil, rd)
1608        assert_equal(expected_props[rd].collect {|path| @greek.uri(path)}.sort,
1609                     props.collect {|item| item.node_name}.sort,
1610                     "prop_list (node_name) with Depth '#{rd}'")
1611        end
1612    end
1613  end
1614
1615  def test_cat
1616    log = "sample log"
1617    src1 = "source1\n"
1618    src2 = "source2\n"
1619    file = "sample.txt"
1620    path = File.join(@wc_path, file)
1621
1622    File.open(path, "w") {|f| f.print(src1)}
1623
1624    make_context(log) do |ctx|
1625      ctx.add(path)
1626      commit_info = ctx.commit(@wc_path)
1627      rev1 = commit_info.revision
1628
1629      assert_equal(normalize_line_break(src1), ctx.cat(path, rev1))
1630      assert_equal(normalize_line_break(src1), ctx.cat(path))
1631
1632      File.open(path, "w") {|f| f.print(src2)}
1633
1634      commit_info = ctx.commit(@wc_path)
1635      rev2 = commit_info.revision
1636
1637      assert_equal(normalize_line_break(src1), ctx.cat(path, rev1))
1638      assert_equal(normalize_line_break(src2), ctx.cat(path, rev2))
1639      assert_equal(normalize_line_break(src2), ctx.cat(path))
1640    end
1641  end
1642
1643  def test_lock
1644    log = "sample log"
1645    src = "source\n"
1646    file = "sample.txt"
1647    path = File.join(@wc_path, file)
1648    absolute_path = File.expand_path(path)
1649
1650    File.open(path, "w") {|f| f.print(src)}
1651
1652    make_context(log) do |ctx|
1653      ctx.add(path)
1654      ctx.commit(@wc_path)
1655
1656      infos = []
1657      ctx.set_notify_func do |notify|
1658        infos << [notify.path, notify]
1659      end
1660      ctx.lock(path)
1661
1662      assert_equal([absolute_path], infos.collect{|_path, notify| _path})
1663      file_notify = infos.assoc(absolute_path)[1]
1664      assert(file_notify.locked?)
1665    end
1666  end
1667
1668  def test_unlock
1669    log = "sample log"
1670    src = "source\n"
1671    file = "sample.txt"
1672    path = File.join(@wc_path, file)
1673    absolute_path = File.expand_path(path)
1674
1675    File.open(path, "w") {|f| f.print(src)}
1676
1677    make_context(log) do |ctx|
1678      ctx.add(path)
1679      ctx.commit(@wc_path)
1680
1681      ctx.lock(path)
1682
1683      infos = []
1684      ctx.set_notify_func do |notify|
1685        infos << [notify.path, notify]
1686      end
1687      ctx.unlock(path)
1688
1689      assert_equal([absolute_path], infos.collect{|_path, notify| _path})
1690      file_notify = infos.assoc(absolute_path)[1]
1691      assert(file_notify.unlocked?)
1692    end
1693  end
1694
1695  def test_info
1696    log = "sample log"
1697    make_context(log) do |ctx|
1698      repos_base = File.basename(@repos_path)
1699
1700      infos = []
1701      ctx.info(@wc_path) do |path, info|
1702        infos << [path, info]
1703      end
1704      assert_equal([repos_base],
1705                   infos.collect{|path, info| path})
1706      top_info = infos.assoc(repos_base)[1]
1707      assert_equal(@repos_uri, top_info.url)
1708    end
1709  end
1710
1711  def test_info_with_depth
1712    setup_greek_tree
1713
1714    log = "sample log"
1715    make_context(log) do |ctx|
1716
1717      recurse_and_depth_choices.each do |rd|
1718        ctx.info(@greek.path(:mu),nil,nil,rd) do |path, info|
1719          assert_equal @greek.uri(:mu), info.URL
1720        end
1721      end
1722
1723      expected_info_by_depth = {
1724        true => [:beta, :b, :lambda, :e, :f, :alpha],
1725        false => [:b],
1726        'empty' => [:b],
1727        'files' => [:b, :lambda],
1728        'immediates' => [:b, :lambda, :e, :f],
1729        'infinity' => [:beta, :b, :lambda, :e, :f, :alpha],
1730      }
1731
1732      recurse_and_depth_choices.each do |rd|
1733        urls = []
1734        ctx.info(@greek.path(:b),nil,nil,rd) do |path, info|
1735          urls << info.URL
1736        end
1737        assert_equal expected_info_by_depth[rd].map{|s| @greek.uri(s)}.sort,
1738                     urls.sort,
1739                     "depth '#{rd}"
1740      end
1741    end
1742  end
1743
1744  def test_url_from_path
1745    log = "sample log"
1746    make_context(log) do |ctx|
1747      assert_equal(@repos_uri, ctx.url_from_path(@wc_path))
1748      assert_equal(@repos_uri, Svn::Client.url_from_path(@wc_path))
1749    end
1750  end
1751
1752  def test_uuid
1753    log = "sample log"
1754    make_context(log) do |ctx|
1755      Svn::Wc::AdmAccess.open(nil, @wc_path, false, 0) do |adm|
1756        assert_equal(ctx.uuid_from_url(@repos_uri),
1757                     ctx.uuid_from_path(@wc_path, adm))
1758      end
1759    end
1760  end
1761
1762  def test_open_ra_session
1763    log = "sample log"
1764    make_context(log) do |ctx|
1765      assert_instance_of(Svn::Ra::Session, ctx.open_ra_session(@repos_uri))
1766    end
1767  end
1768
1769  def test_revprop
1770    log = "sample log"
1771    new_log = "new sample log"
1772    src = "source\n"
1773    file = "sample.txt"
1774    path = File.join(@wc_path, file)
1775
1776    File.open(path, "w") {|f| f.print(src)}
1777
1778    make_context(log) do |ctx|
1779      ctx.add(path)
1780      info = ctx.commit(@wc_path)
1781
1782      assert_equal([
1783                     {
1784                       Svn::Core::PROP_REVISION_AUTHOR => @author,
1785                       Svn::Core::PROP_REVISION_DATE => info.date,
1786                       Svn::Core::PROP_REVISION_LOG => log,
1787                     },
1788                     info.revision
1789                   ],
1790                   ctx.revprop_list(@repos_uri, info.revision))
1791
1792      assert_equal([log, info.revision],
1793                   ctx.revprop_get(Svn::Core::PROP_REVISION_LOG,
1794                                   @repos_uri, info.revision))
1795      assert_equal(log,
1796                   ctx.revprop(Svn::Core::PROP_REVISION_LOG,
1797                               @repos_uri, info.revision))
1798
1799      assert_equal(info.revision,
1800                   ctx.revprop_set(Svn::Core::PROP_REVISION_LOG, new_log,
1801                                   @repos_uri, info.revision))
1802      assert_equal([new_log, info.revision],
1803                   ctx.rpget(Svn::Core::PROP_REVISION_LOG,
1804                             @repos_uri, info.revision))
1805      assert_equal(new_log,
1806                   ctx.rp(Svn::Core::PROP_REVISION_LOG,
1807                          @repos_uri, info.revision))
1808      assert_equal([
1809                     {
1810                       Svn::Core::PROP_REVISION_AUTHOR => @author,
1811                       Svn::Core::PROP_REVISION_DATE => info.date,
1812                       Svn::Core::PROP_REVISION_LOG => new_log,
1813                     },
1814                     info.revision
1815                   ],
1816                   ctx.rplist(@repos_uri, info.revision))
1817
1818      assert_equal(info.revision,
1819                   ctx.revprop_del(Svn::Core::PROP_REVISION_LOG,
1820                                   @repos_uri, info.revision))
1821      assert_equal([nil, info.revision],
1822                   ctx.rpg(Svn::Core::PROP_REVISION_LOG,
1823                           @repos_uri, info.revision))
1824      assert_equal(nil,
1825                   ctx.rp(Svn::Core::PROP_REVISION_LOG,
1826                          @repos_uri, info.revision))
1827
1828      assert_equal(info.revision,
1829                   ctx.rpset(Svn::Core::PROP_REVISION_LOG, new_log,
1830                             @repos_uri, info.revision))
1831      assert_equal(new_log,
1832                   ctx.rp(Svn::Core::PROP_REVISION_LOG,
1833                          @repos_uri, info.revision))
1834      assert_equal(info.revision,
1835                   ctx.rps(Svn::Core::PROP_REVISION_LOG, nil,
1836                           @repos_uri, info.revision))
1837      assert_equal(nil,
1838                   ctx.rp(Svn::Core::PROP_REVISION_LOG,
1839                          @repos_uri, info.revision))
1840
1841      assert_equal([
1842                     {
1843                       Svn::Core::PROP_REVISION_AUTHOR => @author,
1844                       Svn::Core::PROP_REVISION_DATE => info.date,
1845                     },
1846                     info.revision
1847                   ],
1848                   ctx.rpl(@repos_uri, info.revision))
1849    end
1850  end
1851
1852  def test_export
1853    log = "sample log"
1854    src = "source\n"
1855    file = "sample.txt"
1856    dir = "sample"
1857    dir_path = File.join(@wc_path, dir)
1858    path = File.join(dir_path, file)
1859    tmp_base_path = File.join(@tmp_path, "tmp")
1860    tmp_dir_path = File.join(tmp_base_path, dir)
1861    tmp_path = File.join(tmp_dir_path, file)
1862
1863    make_context(log) do |ctx|
1864
1865      ctx.mkdir(dir_path)
1866      File.open(path, "w") {|f| f.print(src)}
1867      ctx.add(path)
1868      rev = ctx.ci(@wc_path).revision
1869
1870      assert_equal(rev, ctx.export(@repos_uri, tmp_base_path))
1871      assert_equal(src, File.open(tmp_path) {|f| f.read})
1872    end
1873  end
1874
1875  def test_ls
1876    log = "sample log"
1877    src = "source\n"
1878    file = "sample.txt"
1879    dir = "sample"
1880    dir_path = File.join(@wc_path, dir)
1881    path = File.join(@wc_path, file)
1882
1883    make_context(log) do |ctx|
1884
1885      ctx.mkdir(dir_path)
1886      File.open(path, "w") {|f| f.print(src)}
1887      ctx.add(path)
1888      rev = ctx.ci(@wc_path).revision
1889
1890      dirents, locks = ctx.ls(@wc_path, rev)
1891      assert_equal([dir, file].sort, dirents.keys.sort)
1892      dir_dirent = dirents[dir]
1893      assert(dir_dirent.directory?)
1894      file_dirent = dirents[file]
1895      assert(file_dirent.file?)
1896    end
1897  end
1898
1899  def test_list
1900    log = "sample log"
1901    src = "source\n"
1902    file = "sample.txt"
1903    dir = "sample"
1904    prop_name = "sample-prop"
1905    prop_value = "sample value"
1906    dir_path = File.join(@wc_path, dir)
1907    path = File.join(@wc_path, file)
1908
1909    make_context(log) do |ctx|
1910
1911      ctx.mkdir(dir_path)
1912      File.open(path, "w") {|f| f.print(src)}
1913      ctx.add(path)
1914      ctx.prop_set(prop_name, prop_value, path)
1915      rev = ctx.ci(@wc_path).revision
1916
1917      entries = []
1918      ctx.list(@wc_path, rev) do |path, dirent, lock, abs_path|
1919        entries << [path, dirent, lock, abs_path]
1920      end
1921      paths = entries.collect do |path, dirent, lock, abs_path|
1922        [path, abs_path]
1923      end
1924      assert_equal([["", "/"], [dir, "/"], [file, "/"]].sort, paths.sort)
1925      entries.each do |path, dirent, lock, abs_path|
1926        case path
1927        when dir, ""
1928          assert(dirent.directory?)
1929          assert_false(dirent.have_props?)
1930        when file
1931          assert(dirent.file?)
1932          assert_true(dirent.have_props?)
1933        else
1934          flunk
1935        end
1936      end
1937    end
1938  end
1939
1940  def test_list_with_depth
1941    setup_greek_tree
1942
1943    log = "sample log"
1944    make_context(log) do |ctx|
1945
1946      expected_lists_by_depth = {
1947        true => [:beta, :b, :lambda, :e, :f, :alpha],
1948        false => [:b, :lambda, :e, :f],
1949        'empty' => [:b],
1950        'files' => [:b, :lambda],
1951        'immediates' => [:b, :lambda, :e, :f],
1952        'infinity' => [:beta, :b, :lambda, :e, :f, :alpha],
1953      }
1954
1955      recurse_and_depth_choices.each do |rd|
1956        paths = []
1957        ctx.list(@greek.path(:b), 'head' ,nil, rd) do |path, dirent, lock, abs_path|
1958          paths << (path.empty? ? abs_path : File.join(abs_path, path))
1959        end
1960        assert_equal(expected_lists_by_depth[rd].map{|s| "/#{@greek.resolve(s)}"}.sort,
1961                     paths.sort,
1962                     "depth '#{rd}")
1963      end
1964    end
1965  end
1966
1967  def test_switch
1968    log = "sample log"
1969    trunk_src = "trunk source\n"
1970    tag_src = "tag source\n"
1971    file = "sample.txt"
1972    file = "sample.txt"
1973    trunk_dir = "trunk"
1974    tag_dir = "tags"
1975    tag_name = "0.0.1"
1976    trunk_repos_uri = "#{@repos_uri}/#{trunk_dir}"
1977    tag_repos_uri = "#{@repos_uri}/#{tag_dir}/#{tag_name}"
1978    trunk_dir_path = File.join(@wc_path, trunk_dir)
1979    tag_dir_path = File.join(@wc_path, tag_dir)
1980    tag_name_dir_path = File.join(@wc_path, tag_dir, tag_name)
1981    trunk_path = File.join(trunk_dir_path, file)
1982    tag_path = File.join(tag_name_dir_path, file)
1983    path = File.join(@wc_path, file)
1984
1985    make_context(log) do |ctx|
1986
1987      ctx.mkdir(trunk_dir_path)
1988      File.open(trunk_path, "w") {|f| f.print(trunk_src)}
1989      ctx.add(trunk_path)
1990      trunk_rev = ctx.commit(@wc_path).revision
1991
1992      ctx.mkdir(tag_dir_path, tag_name_dir_path)
1993      File.open(tag_path, "w") {|f| f.print(tag_src)}
1994      ctx.add(tag_path)
1995      tag_rev = ctx.commit(@wc_path).revision
1996
1997      assert_equal(youngest_rev, ctx.switch(@wc_path, trunk_repos_uri))
1998      assert_equal(normalize_line_break(trunk_src), ctx.cat(path))
1999
2000      assert_equal(youngest_rev, ctx.switch(@wc_path, tag_repos_uri))
2001      assert_equal(normalize_line_break(tag_src), ctx.cat(path))
2002
2003
2004      notify_info = []
2005      ctx.set_notify_func do |notify|
2006        notify_info << [notify.path, notify.action]
2007      end
2008
2009      assert_equal(trunk_rev, ctx.switch(@wc_path, trunk_repos_uri, trunk_rev))
2010      assert_equal(normalize_line_break(trunk_src), ctx.cat(path))
2011      assert_equal([
2012                     [path, Svn::Wc::NOTIFY_UPDATE_UPDATE],
2013                     [@wc_path, Svn::Wc::NOTIFY_UPDATE_UPDATE],
2014                     [@wc_path, Svn::Wc::NOTIFY_UPDATE_COMPLETED],
2015                   ],
2016                   notify_info)
2017
2018      notify_info.clear
2019      assert_equal(tag_rev, ctx.switch(@wc_path, tag_repos_uri, tag_rev))
2020      assert_equal(normalize_line_break(tag_src), ctx.cat(path))
2021      assert_equal([
2022                     [path, Svn::Wc::NOTIFY_UPDATE_UPDATE],
2023                     [@wc_path, Svn::Wc::NOTIFY_UPDATE_UPDATE],
2024                     [@wc_path, Svn::Wc::NOTIFY_UPDATE_COMPLETED],
2025                   ],
2026                   notify_info)
2027    end
2028  end
2029
2030  def test_authentication
2031    log = "sample log"
2032    src = "source\n"
2033    file = "sample.txt"
2034    path = File.join(@wc_path, file)
2035    svnserve_uri = "#{@repos_svnserve_uri}/#{file}"
2036
2037    File.open(path, "w") {|f| f.print(src)}
2038
2039    make_context(log) do |ctx|
2040      ctx.add(path)
2041      ctx.commit(@wc_path)
2042    end
2043
2044    Svn::Client::Context.new do |ctx|
2045      # ### TODO: Verify Svn::Error::AuthnNoProvider in error chain
2046      assert_raises(Svn::Error::RaCannotCreateSession) do
2047        ctx.cat(svnserve_uri)
2048      end
2049
2050      ctx.add_simple_prompt_provider(0) do |cred, realm, username, may_save|
2051        cred.username = "wrong-#{@author}"
2052        cred.password = @password
2053        cred.may_save = false
2054      end
2055      # ### TODO: Verify Svn::Error::RaNotAuthorized in error chain
2056      assert_raises(Svn::Error::RaCannotCreateSession) do
2057        ctx.cat(svnserve_uri)
2058      end
2059
2060      ctx.add_simple_prompt_provider(0) do |cred, realm, username, may_save|
2061        cred.username = @author
2062        cred.password = "wrong-#{@password}"
2063        cred.may_save = false
2064      end
2065      # ### TODO: Verify Svn::Error::RaNotAuthorized in error chain
2066      assert_raises(Svn::Error::RaCannotCreateSession) do
2067        ctx.cat(svnserve_uri)
2068      end
2069
2070      ctx.add_simple_prompt_provider(0) do |cred, realm, username, may_save|
2071        cred.username = @author
2072        cred.password = @password
2073        cred.may_save = false
2074      end
2075      assert_equal(normalize_line_break(src), ctx.cat(svnserve_uri))
2076    end
2077  end
2078
2079  def assert_simple_provider(method)
2080    log = "sample log"
2081    src = "source\n"
2082    file = "sample.txt"
2083    path = File.join(@wc_path, file)
2084    svnserve_uri = "#{@repos_svnserve_uri}/#{file}"
2085
2086    File.open(path, "w") {|f| f.print(src)}
2087
2088    make_context(log) do |ctx|
2089      setup_auth_baton(ctx.auth_baton)
2090      ctx.add(path)
2091      ctx.commit(@wc_path)
2092    end
2093
2094    ctx = Svn::Client::Context.new
2095    setup_auth_baton(ctx.auth_baton)
2096    ctx.send(method)
2097    # ### Verify Svn::Error::RaNotAuthorized in chain
2098    assert_raises(Svn::Error::RaCannotCreateSession) do
2099      ctx.cat(svnserve_uri)
2100    end
2101
2102    ctx = Svn::Client::Context.new
2103    setup_auth_baton(ctx.auth_baton)
2104    ctx.send(method)
2105    ctx.add_simple_prompt_provider(0) do |cred, realm, username, may_save|
2106      cred.username = @author
2107      cred.password = @password
2108    end
2109    assert_equal(normalize_line_break(src), ctx.cat(svnserve_uri))
2110  end
2111
2112  def test_simple_provider
2113    assert_simple_provider(:add_simple_provider)
2114  end
2115
2116  if Svn::Client::Context.method_defined?(:add_windows_simple_provider)
2117    def test_windows_simple_provider
2118      assert_simple_provider(:add_windows_simple_provider)
2119    end
2120  end
2121
2122  if Svn::Core.respond_to?(:auth_get_keychain_simple_provider)
2123    def test_keychain_simple_provider
2124      assert_simple_provider(:add_keychain_simple_provider)
2125    end
2126  end
2127
2128  def test_username_provider
2129    log = "sample log"
2130    new_log = "sample new log"
2131    src = "source\n"
2132    file = "sample.txt"
2133    path = File.join(@wc_path, file)
2134    repos_uri = "#{@repos_uri}/#{file}"
2135
2136    File.open(path, "w") {|f| f.print(src)}
2137
2138    info_revision = make_context(log) do |ctx|
2139      ctx.add(path)
2140      info = ctx.commit(@wc_path)
2141      info.revision
2142    end
2143
2144    Svn::Client::Context.new do |ctx|
2145      setup_auth_baton(ctx.auth_baton)
2146      ctx.auth_baton[Svn::Core::AUTH_PARAM_DEFAULT_USERNAME] = @author
2147      ctx.add_username_provider
2148      assert_nothing_raised do
2149        ctx.revprop_set(Svn::Core::PROP_REVISION_LOG, new_log,
2150                        repos_uri, info_revision)
2151      end
2152    end
2153
2154    Svn::Client::Context.new do |ctx|
2155      setup_auth_baton(ctx.auth_baton)
2156      ctx.auth_baton[Svn::Core::AUTH_PARAM_DEFAULT_USERNAME] = "#{@author}-NG"
2157      ctx.add_username_provider
2158      assert_raise(Svn::Error::REPOS_HOOK_FAILURE) do
2159        ctx.revprop_set(Svn::Core::PROP_REVISION_LOG, new_log,
2160                        repos_uri, info_revision)
2161      end
2162    end
2163
2164    Svn::Client::Context.new do |ctx|
2165      setup_auth_baton(ctx.auth_baton)
2166      ctx.auth_baton[Svn::Core::AUTH_PARAM_DEFAULT_USERNAME] = nil
2167      ctx.add_username_prompt_provider(0) do |cred, realm, may_save|
2168      end
2169      assert_raise(Svn::Error::REPOS_HOOK_FAILURE) do
2170        ctx.revprop_set(Svn::Core::PROP_REVISION_LOG, new_log,
2171                        repos_uri, info_revision)
2172      end
2173    end
2174
2175    Svn::Client::Context.new do |ctx|
2176      setup_auth_baton(ctx.auth_baton)
2177      ctx.auth_baton[Svn::Core::AUTH_PARAM_DEFAULT_USERNAME] = nil
2178      ctx.add_username_prompt_provider(0) do |cred, realm, may_save|
2179        cred.username = @author
2180      end
2181      assert_nothing_raised do
2182        ctx.revprop_set(Svn::Core::PROP_REVISION_LOG, new_log,
2183                        repos_uri, info_revision)
2184      end
2185    end
2186  end
2187
2188  def test_add_providers
2189    Svn::Client::Context.new do |ctx|
2190      assert_nothing_raised do
2191        ctx.add_ssl_client_cert_file_provider
2192        ctx.add_ssl_client_cert_pw_file_provider
2193        ctx.add_ssl_server_trust_file_provider
2194        if Svn::Core.respond_to?(:auth_get_windows_ssl_server_trust_provider)
2195          ctx.add_windows_ssl_server_trust_provider
2196        end
2197      end
2198    end
2199  end
2200
2201  def test_commit_item
2202    assert_raise(NoMethodError) do
2203      Svn::Client::CommitItem.new
2204    end
2205
2206    assert_raise(NoMethodError) do
2207      Svn::Client::CommitItem2.new
2208    end
2209
2210    item = Svn::Client::CommitItem3.new
2211    assert_kind_of(Svn::Client::CommitItem3, item)
2212
2213    url = "xxx"
2214    item.url = url
2215    assert_equal(url, item.dup.url)
2216  end
2217
2218  def test_log_msg_func_commit_items
2219    log = "sample log"
2220    file = "file"
2221    file2 = "file2"
2222    src = "source"
2223    path = File.join(@wc_path, file)
2224    repos_uri2 = "#{@repos_uri}/#{file2}"
2225
2226    File.open(path, "w") {|f| f.print(src)}
2227
2228    make_context(log) do |ctx|
2229      items = nil
2230      ctx.set_log_msg_func do |l_items|
2231        # ruby 1.8 will carry the assignment of items out of the
2232        # scope of this block, 1.9 will not, so we must assign.
2233        items = l_items
2234        [true, log]
2235      end
2236
2237      ctx.add(path)
2238      ctx.prop_set(Svn::Core::PROP_MIME_TYPE, "text/plain", path)
2239      ctx.commit(@wc_path)
2240      assert_equal([[]], items.collect {|item| item.wcprop_changes})
2241      assert_equal([[]], items.collect {|item| item.incoming_prop_changes})
2242      assert_equal([nil], items.collect {|item| item.outgoing_prop_changes})
2243
2244      items = nil
2245      ctx.cp(path, repos_uri2)
2246      assert_equal([[]], items.collect {|item| item.wcprop_changes})
2247      assert_equal([[]], items.collect {|item| item.incoming_prop_changes})
2248      assert_equal([nil], items.collect {|item| item.outgoing_prop_changes})
2249    end
2250  end
2251
2252  def test_log_msg_func_cancel
2253    log = "sample log"
2254    dir = "dir"
2255    dir_path = File.join(@wc_path, dir)
2256
2257    make_context(log) do |ctx|
2258      ctx.set_log_msg_func do |items|
2259        raise Svn::Error::Cancelled
2260      end
2261      ctx.mkdir(dir_path)
2262      assert_raise(Svn::Error::Cancelled) do
2263        ctx.commit(@wc_path)
2264      end
2265    end
2266  end
2267
2268  def test_set_config
2269    log = "sample log"
2270    make_context(log) do |ctx|
2271      options = {
2272        "groups" => {"collabnet" => "svn.collab.net"},
2273        "collabnet" => {
2274          "http-proxy-host" => "proxy",
2275          "http-proxy-port" => "8080",
2276        },
2277      }
2278      servers_config_file = File.join(@config_path,
2279                                      Svn::Core::CONFIG_CATEGORY_SERVERS)
2280      File.open(servers_config_file, "w") do |file|
2281        options.each do |section, values|
2282          file.puts("[#{section}]")
2283          values.each do |key, value|
2284            file.puts("#{key} = #{value}")
2285          end
2286        end
2287      end
2288      config = Svn::Core::Config.config(@config_path)
2289      assert_equal(options, config[Svn::Core::CONFIG_CATEGORY_SERVERS].to_hash)
2290      ctx.config = config
2291      assert_equal(options,
2292                   ctx.config[Svn::Core::CONFIG_CATEGORY_SERVERS].to_hash)
2293    end
2294  end
2295
2296  def test_context_mimetypes_map
2297    Svn::Client::Context.new do |context|
2298      assert_nil(context.mimetypes_map)
2299      context.mimetypes_map = {"txt" => "text/plain"}
2300      assert_equal({"txt" => "text/plain"}, context.mimetypes_map)
2301    end
2302  end
2303
2304  def assert_changelists
2305    log = "sample log"
2306    file1 = "hello1.txt"
2307    file2 = "hello2.txt"
2308    src = "Hello"
2309    changelist1 = "XXX"
2310    changelist2 = "YYY"
2311    path1 = File.join(@wc_path, file1)
2312    path2 = File.join(@wc_path, file2)
2313
2314    make_context(log) do |ctx|
2315      File.open(path1, "w") {|f| f.print(src)}
2316      File.open(path2, "w") {|f| f.print(src)}
2317      ctx.add(path1)
2318      ctx.add(path2)
2319      ctx.commit(@wc_path)
2320
2321      assert_equal({}, yield(ctx, changelist1))
2322      assert_equal({nil=>[@wc_path,path1,path2].map{|f| File.expand_path(f)}}, yield(ctx, nil))
2323      assert_equal({}, yield(ctx, []))
2324      assert_equal({}, yield(ctx, [changelist1]))
2325      assert_equal({}, yield(ctx, [changelist2]))
2326      ctx.add_to_changelist(changelist1, path1)
2327      assert_equal({changelist1=>[path1].map{|f| File.expand_path(f)}}, yield(ctx, changelist1))
2328      assert_equal({changelist1=>[path1].map{|f| File.expand_path(f)},nil=>[@wc_path,path2].map{|f| File.expand_path(f)}}, yield(ctx, nil))
2329      assert_equal({}, yield(ctx, []))
2330      assert_equal({changelist1=>[path1].map{|f| File.expand_path(f)}}, yield(ctx, [changelist1]))
2331      assert_equal({}, yield(ctx, [changelist2]))
2332
2333      assert_equal({}, yield(ctx, changelist2))
2334      ctx.add_to_changelist(changelist2, [path1, path2])
2335      assert_equal({changelist2=>[path1, path2].map{|f| File.expand_path(f)}}, yield(ctx, changelist2))
2336      assert_equal({}, yield(ctx, changelist1))
2337
2338      ctx.add_to_changelist(changelist1, [path1, path2])
2339      assert_equal({changelist1=>[path1, path2].map{|f| File.expand_path(f)}}, yield(ctx, changelist1))
2340      assert_equal({}, yield(ctx, changelist2))
2341
2342      ctx.remove_from_changelists(changelist1, path1)
2343      assert_equal({changelist1=>[path2].map{|f| File.expand_path(f)}}, yield(ctx, changelist1))
2344      ctx.remove_from_changelists(changelist1, [path2])
2345      assert_equal({}, yield(ctx, changelist1))
2346
2347      ctx.add_to_changelist(changelist1, path1)
2348      ctx.add_to_changelist(changelist2, path2)
2349      assert_equal({changelist1=>[path1].map{|f| File.expand_path(f)}}, yield(ctx, changelist1))
2350      assert_equal({changelist2=>[path2].map{|f| File.expand_path(f)}}, yield(ctx, changelist2))
2351
2352      assert_equal({changelist1=>[path1].map{|f| File.expand_path(f)}}, yield(ctx, changelist1))
2353      assert_equal({changelist2=>[path2].map{|f| File.expand_path(f)}}, yield(ctx, changelist2))
2354      assert_equal({changelist1=>[path1].map{|f| File.expand_path(f)}}, yield(ctx, [changelist1]))
2355      assert_equal({changelist2=>[path2].map{|f| File.expand_path(f)}}, yield(ctx, [changelist2]))
2356      assert_equal({changelist1=>[path1].map{|f| File.expand_path(f)},changelist2=>[path2].map{|f| File.expand_path(f)},nil=>[@wc_path].map{|f| File.expand_path(f)}},
2357		   yield(ctx, nil))
2358      assert_equal({}, yield(ctx, []))
2359      assert_equal({changelist1=>[path1].map{|f| File.expand_path(f)},changelist2=>[path2].map{|f| File.expand_path(f)}},
2360                   yield(ctx, [changelist1,changelist2]))
2361
2362      ctx.remove_from_changelists(nil, [path1, path2])
2363      assert_equal({}, yield(ctx, changelist1))
2364      assert_equal({}, yield(ctx, changelist2))
2365    end
2366  end
2367
2368  def test_changelists_get_without_block
2369    assert_changelists do |ctx, changelist_name|
2370      changelists = ctx.changelists(changelist_name, @wc_path)
2371      changelists.each_value { |v| v.sort! }
2372      changelists
2373    end
2374  end
2375
2376  def test_changelists_get_with_block
2377    assert_changelists do |ctx, changelist_name|
2378      changelists = Hash.new{|h,k| h[k]=[]}
2379      ctx.changelists(changelist_name, @wc_path) do |path,cl_name|
2380        changelists[cl_name] << path
2381      end
2382      changelists.each_value { |v| v.sort! }
2383      changelists
2384    end
2385  end
2386
2387  def test_set_revision_by_date
2388    log = "sample log"
2389    file = "hello.txt"
2390    src = "Hello"
2391    src_after = "Hello World"
2392    path = File.join(@wc_path, file)
2393    uri = "#{@repos_uri}/#{file}"
2394
2395    make_context(log) do |ctx|
2396      File.open(path, "w") {|f| f.print(src)}
2397      ctx.add(path)
2398      ctx.commit(@wc_path)
2399
2400      first_commit_time = Time.now
2401      sleep(1)
2402
2403      File.open(path, "w") {|f| f.print(src_after)}
2404      ctx.commit(@wc_path)
2405
2406      assert_equal(src, ctx.cat(uri, first_commit_time))
2407    end
2408  end
2409
2410  def assert_resolve(choice)
2411    log = "sample log"
2412    file = "sample.txt"
2413    srcs = ["before\n","after\n"]
2414    dir = "dir"
2415    dir_path = File.join(@wc_path, dir)
2416    path = File.join(dir_path, file)
2417
2418    make_context(log) do |ctx|
2419      ctx.mkdir(dir_path)
2420      File.open(path, "w") {}
2421      ctx.add(path)
2422      rev1 = ctx.ci(@wc_path).revision
2423
2424      File.open(path, "w") {|f| f.print(srcs[0])}
2425      rev2 = ctx.ci(@wc_path).revision
2426
2427      ctx.up(@wc_path, rev1)
2428
2429      File.open(path, "w") {|f| f.print(srcs[1])}
2430      ctx.up(@wc_path)
2431
2432      assert_raises(Svn::Error::WcFoundConflict) do
2433        ctx.ci(@wc_path)
2434      end
2435
2436      ctx.resolve(:path=>dir_path, :depth=>:empty, :conflict_choice=>choice)
2437      assert_raises(Svn::Error::WcFoundConflict) do
2438        ctx.ci(@wc_path)
2439      end
2440
2441      ctx.resolve(:path=>dir_path, :depth=>:infinity, :conflict_choice=>choice)
2442      yield ctx, path
2443    end
2444  end
2445
2446  def test_resolve_base
2447    assert_resolve(Svn::Wc::CONFLICT_CHOOSE_BASE) do |ctx,path|
2448      info = nil
2449      assert_nothing_raised do
2450        info = ctx.ci(@wc_path)
2451      end
2452      assert_not_nil(info)
2453      assert_equal(3, info.revision)
2454
2455      assert_equal("", File.read(path))
2456    end
2457  end
2458
2459  def test_resolve_theirs_full
2460    assert_resolve(Svn::Wc::CONFLICT_CHOOSE_THEIRS_FULL) do |ctx,path|
2461      info = nil
2462      assert_nothing_raised do
2463        info = ctx.ci(@wc_path)
2464      end
2465      assert_not_nil(info)
2466      assert_equal(-1, info.revision)
2467
2468      assert_equal("before\n", File.read(path))
2469    end
2470  end
2471
2472  def test_resolve_mine_full
2473    assert_resolve(Svn::Wc::CONFLICT_CHOOSE_MINE_FULL) do |ctx,path|
2474      info = nil
2475      assert_nothing_raised do
2476        info = ctx.ci(@wc_path)
2477      end
2478      assert_not_nil(info)
2479      assert_equal(3, info.revision)
2480
2481      assert_equal("after\n", File.read(path))
2482    end
2483  end
2484
2485  def test_resolve_theirs_conflict
2486    assert_resolve(Svn::Wc::CONFLICT_CHOOSE_THEIRS_FULL) do |ctx,path|
2487      info = nil
2488      assert_nothing_raised do
2489        info = ctx.ci(@wc_path)
2490      end
2491      assert_not_nil(info)
2492      assert_equal(-1, info.revision)
2493
2494      assert_equal("before\n", File.read(path))
2495    end
2496  end
2497
2498  def test_resolve_mine_conflict
2499    assert_resolve(Svn::Wc::CONFLICT_CHOOSE_MINE_FULL) do |ctx,path|
2500      info = nil
2501      assert_nothing_raised do
2502        info = ctx.ci(@wc_path)
2503      end
2504      assert_not_nil(info)
2505      assert_equal(3, info.revision)
2506
2507      assert_equal("after\n", File.read(path))
2508    end
2509  end
2510
2511  def test_resolve_merged
2512    assert_resolve(Svn::Wc::CONFLICT_CHOOSE_MERGED) do |ctx,path|
2513      info = nil
2514      assert_nothing_raised do
2515        info = ctx.ci(@wc_path)
2516      end
2517      assert_not_nil(info)
2518      assert_equal(3, info.revision)
2519
2520      assert_equal("<<<<<<< .mine\nafter\n||||||| .r1\n=======\nbefore\n>>>>>>> .r2\n",
2521                   File.read(path))
2522    end
2523  end
2524end
2525