svn-command 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Readme +251 -0
- data/bin/command_completion_for_svn_command +20 -0
- data/bin/rscm_test +19 -0
- data/bin/svn +7 -0
- data/lib/attribute_accessors.rb +44 -0
- data/lib/my_wrapper.rb +72 -0
- data/lib/subversion.rb +335 -0
- data/lib/subversion_extensions.rb +60 -0
- data/lib/svn_command.rb +627 -0
- data/test/shared/test_helper.rb +3 -0
- data/test/shared/test_helpers/assertions.rb +56 -0
- data/test/shared/test_helpers/test_colorizer.rb +106 -0
- data/test/subversion_extensions_test.rb +66 -0
- data/test/subversion_test.rb +99 -0
- data/test/svn_command_test.rb +455 -0
- data/test/test_helper.rb +31 -0
- metadata +75 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
# Custom assertions
|
2
|
+
class Test::Unit::TestCase
|
3
|
+
def assert_user_error(error_message)
|
4
|
+
assert_tag({
|
5
|
+
:attributes => { :id => "errorExplanation" },
|
6
|
+
:descendant => {
|
7
|
+
:content => error_message
|
8
|
+
}
|
9
|
+
})
|
10
|
+
end
|
11
|
+
|
12
|
+
def assert_contains(container, expected_contents, failure_message = nil)
|
13
|
+
failure_message = build_message(failure_message, "Container <?> was expected to contain <?> but it didn't", container, expected_contents)
|
14
|
+
assert_block(failure_message) do
|
15
|
+
container.include?(expected_contents)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Asserts that the block that is passed in causes the value of the specified variable (+variable+) to change.
|
20
|
+
# +variable+ should be a Proc that, when evaluated, returns the current value of the variable.
|
21
|
+
#
|
22
|
+
# Options:
|
23
|
+
# * If the optional +:from+ option is supplied, it also asserts that it had that initial value.
|
24
|
+
# * If the optional +:to+ option is supplied, it also asserts that it changed _to_ that value.
|
25
|
+
#
|
26
|
+
# So instead of doing this:
|
27
|
+
# assert_equal 1, Model.count
|
28
|
+
# do_something_that_should_cause_count_to_increase
|
29
|
+
# assert_equal 2, Model.count
|
30
|
+
# we can do this:
|
31
|
+
# assert_changed(lambda {ErrorType.count}, :from => 1, :to => 2) do
|
32
|
+
# do_something_that_should_cause_count_to_increase
|
33
|
+
# end
|
34
|
+
# Or, if we don't care what it's changing _from_ as long as it increases in value _by_ 1, we can write this:
|
35
|
+
# assert_changed(c = lambda {ErrorType.count}, :to => c.call+1) do
|
36
|
+
# do_something_that_should_cause_count_to_increase
|
37
|
+
# end
|
38
|
+
# instead of this:
|
39
|
+
# before = Model.count
|
40
|
+
# do_something_that_should_cause_count_to_increase
|
41
|
+
# assert_equal before + 1, Model.count
|
42
|
+
#
|
43
|
+
def assert_changed(variable, options = {}, &block)
|
44
|
+
expected_from = options.delete(:from) || variable.call
|
45
|
+
|
46
|
+
assert_equal expected_from, variable.call
|
47
|
+
|
48
|
+
failure_message = build_message(failure_message, "The variable was expected to change from <?> to <?> but it didn't", variable.call, options.delete(:to) || "something else")
|
49
|
+
assert_block(failure_message) do
|
50
|
+
before = variable.call
|
51
|
+
yield
|
52
|
+
expected_to = options.delete(:to) || variable.call
|
53
|
+
before != variable.call and variable.call == expected_to
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'color' # Warning: Make sure you have the *right* color gem! gem install color --source require.errtheblog.com
|
3
|
+
module Test::Unit
|
4
|
+
|
5
|
+
class Error
|
6
|
+
# the 'E' that is displayed as the tests are run
|
7
|
+
def single_character_display
|
8
|
+
"E".red.bold
|
9
|
+
end
|
10
|
+
|
11
|
+
# the long message that is displayed after test is run
|
12
|
+
def long_display
|
13
|
+
backtrace = filter_backtrace(@exception.backtrace).join("\n ")
|
14
|
+
[
|
15
|
+
"Error".red.bold,
|
16
|
+
":\n",
|
17
|
+
"#@test_name:".white,
|
18
|
+
"\n", "#{message}".red,
|
19
|
+
"\n #{backtrace}".gsub(/:(\d+):/,":#{'\1'.red}:")
|
20
|
+
].join('')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
class Failure
|
26
|
+
# the 'E' that is displayed as the tests are run
|
27
|
+
def single_character_display
|
28
|
+
"F".yellow.bold
|
29
|
+
end
|
30
|
+
|
31
|
+
# the long message that is displayed after test is run
|
32
|
+
def long_display
|
33
|
+
location_display = if(location.size == 1)
|
34
|
+
location[0].sub(/\A(.+:\d+).*/, ' [\\1]')
|
35
|
+
else
|
36
|
+
"\n [#{location.join("\n ")}]"
|
37
|
+
end
|
38
|
+
[
|
39
|
+
"Failure".yellow.bold,
|
40
|
+
":\n","#@test_name".white,
|
41
|
+
"#{location_display}".gsub(/:(\d+)/, ":#{'\1'.yellow}"),
|
42
|
+
":\n", "#@message".yellow
|
43
|
+
].join('')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# it is necessary to do a class_eval for the TestRunner class
|
48
|
+
# through the AutoRunner class, since the require in the AutoRunner
|
49
|
+
# class will overwrite what we do here otherwise.
|
50
|
+
#
|
51
|
+
# We must do the same thing inside of the TestRunner class
|
52
|
+
# class_eval for the TestResult class. We Modify the
|
53
|
+
# TestRunnerMediator class after the require is called,
|
54
|
+
# which will then modify the TestResult class.
|
55
|
+
#
|
56
|
+
# test_finished is the function that will output the
|
57
|
+
# '.' period during the test runs
|
58
|
+
#
|
59
|
+
# the to_s for TestResult class returns the string
|
60
|
+
# for the final tallied results
|
61
|
+
class AutoRunner
|
62
|
+
RUNNERS[:console] = proc do |r|
|
63
|
+
require 'test/unit/ui/console/testrunner'
|
64
|
+
Test::Unit::UI::Console::TestRunner.class_eval %q{
|
65
|
+
def test_finished(name)
|
66
|
+
output_single('.'.green, 1) unless (@already_outputted)
|
67
|
+
nl(3)
|
68
|
+
@already_outputted = false
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_mediator(suite)
|
72
|
+
require 'test/unit/ui/testrunnermediator'
|
73
|
+
Test::Unit::UI::TestRunnerMediator.class_eval %q{
|
74
|
+
def create_result
|
75
|
+
Test::Unit::TestResult.class_eval %q{
|
76
|
+
def to_s
|
77
|
+
rc = [
|
78
|
+
"#{run_count}".white.bold,
|
79
|
+
"tests".white
|
80
|
+
].join(' ')
|
81
|
+
ac = [
|
82
|
+
"#{assertion_count}".white.bold,
|
83
|
+
"assertions".white
|
84
|
+
].join(' ')
|
85
|
+
fc = [
|
86
|
+
"#{failure_count}".yellow.bold,
|
87
|
+
"failures".yellow
|
88
|
+
].join(' ')
|
89
|
+
ec = [
|
90
|
+
"#{error_count}".red.bold,
|
91
|
+
"errors".red
|
92
|
+
].join(' ')
|
93
|
+
[rc, ac, fc, ec].join(', ')
|
94
|
+
end
|
95
|
+
}
|
96
|
+
TestResult.new
|
97
|
+
end
|
98
|
+
}
|
99
|
+
return Test::Unit::UI::TestRunnerMediator.new(suite)
|
100
|
+
end
|
101
|
+
}
|
102
|
+
Test::Unit::UI::Console::TestRunner
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require 'subversion_extensions'
|
3
|
+
|
4
|
+
|
5
|
+
class SubversionExtensionsTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_status_lines_filter
|
10
|
+
input = <<End
|
11
|
+
M gemables/calculator/test/calculator_test.rb
|
12
|
+
X gemables/calculator/tasks/shared
|
13
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
14
|
+
|
15
|
+
Performing status on external item at 'plugins/flubber/tasks/shared'
|
16
|
+
|
17
|
+
Performing status on external item at 'applications/underlord/vendor/plugins/nifty'
|
18
|
+
X applications/underlord/vendor/plugins/nifty/tasks/shared
|
19
|
+
X applications/underlord/vendor/plugins/nifty/doc_include/template
|
20
|
+
|
21
|
+
Performing status on external item at 'applications/underlord/vendor/plugins/nifty/tasks/shared'
|
22
|
+
M applications/underlord/vendor/plugins/nifty/tasks/shared/base.rake
|
23
|
+
End
|
24
|
+
expected = <<End
|
25
|
+
M gemables/calculator/test/calculator_test.rb
|
26
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
27
|
+
M applications/underlord/vendor/plugins/nifty/tasks/shared/base.rake
|
28
|
+
End
|
29
|
+
|
30
|
+
assert_equal expected, out = Subversion::Extensions.status_lines_filter( input ), out.inspect
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_update_lines_filter
|
34
|
+
input = <<End
|
35
|
+
U gemables/calculator/test/calculator_test.rb
|
36
|
+
U gemables/calculator/tasks/shared
|
37
|
+
U gemables/calculator/lib/calculator_extensions.rb
|
38
|
+
|
39
|
+
Fetching external item into 'plugins/flubber/tasks/shared'
|
40
|
+
U plugins/flubber/tasks/shared/blah.rb
|
41
|
+
External at revision 134143078
|
42
|
+
End
|
43
|
+
expected = <<End
|
44
|
+
U gemables/calculator/test/calculator_test.rb
|
45
|
+
U gemables/calculator/tasks/shared
|
46
|
+
U gemables/calculator/lib/calculator_extensions.rb
|
47
|
+
U plugins/flubber/tasks/shared/blah.rb
|
48
|
+
End
|
49
|
+
|
50
|
+
assert_equal expected, out = Subversion::Extensions.update_lines_filter( input ), out.inspect
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_unadded_filter
|
54
|
+
input = <<End
|
55
|
+
M gemables/calculator/test/calculator_test.rb
|
56
|
+
X gemables/calculator/tasks/shared
|
57
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
58
|
+
End
|
59
|
+
expected = <<End
|
60
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
61
|
+
End
|
62
|
+
|
63
|
+
assert_equal expected, out = Subversion::Extensions.unadded_lines_filter( input ), out.inspect
|
64
|
+
assert_equal ['gemables/calculator/lib/calculator_extensions.rb'], out = Subversion::Extensions.unadded_filter( input ), out.inspect
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
$mock_subversion = true
|
2
|
+
require File.dirname(__FILE__) + '/test_helper'
|
3
|
+
|
4
|
+
class SubversionTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Subversion.reset_executed
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_add
|
10
|
+
Subversion.add('foo', 'bar', 'hello', 'world')
|
11
|
+
assert_equal 'svn add foo bar hello world', Subversion.executed.first
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_ignore
|
15
|
+
Subversion.ignore('foo', 'some/path/foo')
|
16
|
+
assert_equal "svn propset svn:ignore 'foo' ./", Subversion.executed[1]
|
17
|
+
assert_equal "svn propset svn:ignore 'foo' some/path", Subversion.executed[3]
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_externalize
|
21
|
+
Subversion.externalize('some/repo/path')
|
22
|
+
Subversion.externalize('some/repo/path', :as => 'foo')
|
23
|
+
Subversion.externalize('some/repo/path', :as => 'foo', :local_path => 'local/path')
|
24
|
+
|
25
|
+
assert_equal "svn propset svn:externals '#{'path'.ljust(29)} some/repo/path' ./", Subversion.executed[1]
|
26
|
+
assert_equal "svn propset svn:externals '#{'foo'.ljust(29)} some/repo/path' ./", Subversion.executed[3]
|
27
|
+
assert_equal "svn propset svn:externals '#{'foo'.ljust(29)} some/repo/path' local/path", Subversion.executed[5]
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_remove
|
31
|
+
Subversion.remove 'foo', 'bar', 'hello/world'
|
32
|
+
assert_equal 'svn rm foo bar hello/world', Subversion.executed.first
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_remove_without_delete
|
36
|
+
tmpdir = "tmp#{$$}"
|
37
|
+
entries = "#{tmpdir}/.svn/entries"
|
38
|
+
FileUtils.mkdir_p "#{tmpdir}/.svn", :mode => 0755
|
39
|
+
File.open entries, 'w' do |file|
|
40
|
+
file.write <<-EOS
|
41
|
+
<?xml version="1.0" encoding="utf-8"?>
|
42
|
+
<wc-entries
|
43
|
+
xmlns="svn:">
|
44
|
+
<entry
|
45
|
+
name="existing_file"
|
46
|
+
kind="file"/>
|
47
|
+
<entry
|
48
|
+
name="just_added_file"
|
49
|
+
schedule="add"
|
50
|
+
kind="file"/>
|
51
|
+
<entry
|
52
|
+
name="unchanging_file"
|
53
|
+
kind="file"/>
|
54
|
+
</wc-entries>
|
55
|
+
EOS
|
56
|
+
end
|
57
|
+
File.chmod(0444, entries)
|
58
|
+
FileUtils.touch ["#{tmpdir}/existing_file", "#{tmpdir}/just_added_file", "#{tmpdir}/unchanging_file"]
|
59
|
+
|
60
|
+
begin
|
61
|
+
doc = REXML::Document.new(IO.read(entries))
|
62
|
+
assert_not_nil REXML::XPath.first(doc, '//entry[@name="existing_file"]')
|
63
|
+
Subversion.remove_without_delete "#{tmpdir}/existing_file"
|
64
|
+
# the element should now be scheduled for delete
|
65
|
+
doc = REXML::Document.new(IO.read(entries))
|
66
|
+
assert_not_nil REXML::XPath.first(doc, '//entry[@name="existing_file"][@schedule="delete"]')
|
67
|
+
|
68
|
+
doc = REXML::Document.new(IO.read(entries))
|
69
|
+
assert_not_nil REXML::XPath.first(doc, '//entry[@name="just_added_file"][@schedule="add"]')
|
70
|
+
Subversion.remove_without_delete "#{tmpdir}/just_added_file"
|
71
|
+
doc = REXML::Document.new(IO.read(entries))
|
72
|
+
# the element should now be gone
|
73
|
+
assert_nil REXML::XPath.first(doc, '//entry[@name="just_added_file"]')
|
74
|
+
ensure
|
75
|
+
FileUtils.rm_r tmpdir
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_revert
|
80
|
+
Subversion.revert 'foo', 'bar', 'hello/world'
|
81
|
+
assert_equal 'svn revert foo bar hello/world', Subversion.executed.first
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_executable
|
85
|
+
Subversion.make_executable 'foo', 'bar', 'hello/world'
|
86
|
+
assert_equal "svn propset svn:executable '' foo", Subversion.executed[0]
|
87
|
+
assert_equal "svn propset svn:executable '' bar", Subversion.executed[1]
|
88
|
+
assert_equal "svn propset svn:executable '' hello/world", Subversion.executed[2]
|
89
|
+
|
90
|
+
Subversion.make_not_executable 'foo'
|
91
|
+
assert_equal "svn propdel svn:executable foo", Subversion.executed[3]
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_status
|
95
|
+
Subversion.status 'foo'
|
96
|
+
assert_equal "svn status foo", Subversion.executed.first
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,455 @@
|
|
1
|
+
$mock_subversion = true
|
2
|
+
require File.dirname(__FILE__) + '/test_helper'
|
3
|
+
require_local '../lib/svn_command.rb'
|
4
|
+
require 'facets/core/string/to_re'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
module Subversion
|
10
|
+
class BaseSvnCommandTest < Test::Unit::TestCase
|
11
|
+
def setup
|
12
|
+
Subversion.reset_executed
|
13
|
+
end
|
14
|
+
def test_dummy_test
|
15
|
+
# Because it tries to run this base class too!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class SvnCommandTest < BaseSvnCommandTest
|
20
|
+
def test_invalid_quotes_gives_informative_error
|
21
|
+
assert_exception(ArgumentError, lambda { |exception|
|
22
|
+
assert_equal "Unmatched single quote: '", exception.message
|
23
|
+
}) do
|
24
|
+
SvnCommand.execute("foo -m 'stuff''")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
def test_unrecognized_option
|
28
|
+
output = capture_output($stderr) do
|
29
|
+
assert_exception(SystemExit, lambda { |exception|
|
30
|
+
}) do
|
31
|
+
SvnCommand.execute("status --blarg")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
assert_match /Unknown option '--blarg'/, output
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ArgEscapingTest < BaseSvnCommandTest
|
39
|
+
def test_argument_escaping
|
40
|
+
SvnCommand.execute( args = %q{commit -m 'a message with lots of !&`$0 |()<> garbage'} )
|
41
|
+
assert_equal "svn #{args} --force-log", Subversion.executed.join
|
42
|
+
end
|
43
|
+
def test_asterisk
|
44
|
+
# Don't worry, this'll never happen, because the shell will expand the * *before* it gets to SvnCommand. But if you *did* sneak in an asterisk to SvnCommand.execute somehow, this is what would happen...
|
45
|
+
SvnCommand.execute("add dir/* --non-recursive")
|
46
|
+
assert_equal "svn add --non-recursive 'dir/*'", Subversion.executed.join
|
47
|
+
end
|
48
|
+
def test_multiline
|
49
|
+
SvnCommand.execute(args = "commit -m 'This is a
|
50
|
+
|multi-line
|
51
|
+
|message'".margin)
|
52
|
+
assert_equal "svn #{args} --force-log", Subversion.executed.join
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
57
|
+
# Built-in subcommands
|
58
|
+
|
59
|
+
# Test method_missing. Can we still call svn info, rm, etc. even if we haven't written subcommand modules for them?
|
60
|
+
class SubcommandPassThroughTest < BaseSvnCommandTest
|
61
|
+
def test_1
|
62
|
+
SvnCommand.execute("rm -q file1 file2 --force")
|
63
|
+
assert_equal "svn rm -q file1 file2 --force", Subversion.executed.join
|
64
|
+
end
|
65
|
+
def test_2
|
66
|
+
SvnCommand.execute("info -q file1 file2 --force")
|
67
|
+
assert_equal "svn info -q file1 file2 --force", Subversion.executed.join
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class SvnAddTest < BaseSvnCommandTest
|
72
|
+
def test_1
|
73
|
+
end
|
74
|
+
def test_2
|
75
|
+
SvnCommand.execute('add "a b"')
|
76
|
+
assert_equal "svn add 'a b'", Subversion.executed.join
|
77
|
+
end
|
78
|
+
def test_3
|
79
|
+
SvnCommand.execute('add a b')
|
80
|
+
assert_equal "svn add a b", Subversion.executed.join
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class SvnCommitTest < BaseSvnCommandTest
|
85
|
+
def test_1
|
86
|
+
SvnCommand.execute("commit -m 'just an ordinary commit message!'")
|
87
|
+
assert_equal "svn commit -m 'just an ordinary commit message!' --force-log", Subversion.executed.join
|
88
|
+
end
|
89
|
+
def test_lots_of_options
|
90
|
+
SvnCommand.execute("commit --non-recursive -q -m '' --targets some_file ")
|
91
|
+
assert_equal "svn commit --non-recursive -q -m '' --targets some_file --force-log", Subversion.executed.join
|
92
|
+
end
|
93
|
+
def test_that_complex_quoting_doesnt_confuse_it
|
94
|
+
original_message = "Can't decide how many \"'quotes'\" to use!"
|
95
|
+
SvnCommand.execute(%Q{commit -m "#{original_message.gsub('"', '\"')}"})
|
96
|
+
|
97
|
+
expected_escaped_part = %q{'Can'\\''t decide how many "'\\''quotes'\\''" to use!'}
|
98
|
+
assert_equal "svn commit -m #{expected_escaped_part} --force-log", Subversion.executed.join
|
99
|
+
assert_equal original_message, `echo #{expected_escaped_part}`.chomp # We should have gotten back exactly what we put in originally
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class SvnDiffTest < BaseSvnCommandTest
|
104
|
+
def test_1
|
105
|
+
SvnCommand.execute("diff -r 123:125")
|
106
|
+
assert_equal [
|
107
|
+
"svn diff --diff-cmd colordiff ./ -r 123:125",
|
108
|
+
"svn status ./"
|
109
|
+
], Subversion.executed
|
110
|
+
end
|
111
|
+
def test_2
|
112
|
+
#:fixme:
|
113
|
+
#capture_output { SvnCommand.execute("diff -r { 2006-07-01 }") }
|
114
|
+
#p Subversion.executed
|
115
|
+
# Currently does this, since it thinks the arity is 1: --diff-cmd colordiff 2006-07-01 } -r '{'
|
116
|
+
#assert_equal "svn diff --diff-cmd colordiff -r { 2006-07-01 }", Subversion.executed.join
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class SvnHelpTest < BaseSvnCommandTest
|
121
|
+
def test_1
|
122
|
+
output = capture_output { SvnCommand.execute("help") }
|
123
|
+
assert_equal "svn help ", Subversion.executed.join
|
124
|
+
assert_match /wrapper/, output
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class SvnLogTest < BaseSvnCommandTest
|
129
|
+
def test_1
|
130
|
+
capture_output { SvnCommand.execute("log") }
|
131
|
+
assert_equal "svn log ", Subversion.executed.join
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class SvnStatusTest < BaseSvnCommandTest
|
136
|
+
# Duplicates a test in subversion_extensions_test.rb -- maybe can abbreviate this and leave the detailed filter tests to the other TestCase
|
137
|
+
def test_status_does_some_filtering
|
138
|
+
Subversion.stubs(:status).returns("
|
139
|
+
M gemables/calculator/test/calculator_test.rb
|
140
|
+
X gemables/calculator/tasks/shared
|
141
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
142
|
+
|
143
|
+
Performing status on external item at 'plugins/flubber/tasks/shared'
|
144
|
+
|
145
|
+
Performing status on external item at 'applications/underlord/vendor/plugins/nifty'
|
146
|
+
X applications/underlord/vendor/plugins/nifty/tasks/shared
|
147
|
+
X applications/underlord/vendor/plugins/nifty/lib/calculator
|
148
|
+
X applications/underlord/vendor/plugins/nifty/doc_include/template
|
149
|
+
|
150
|
+
Performing status on external item at 'applications/underlord/vendor/plugins/nifty/tasks/shared'
|
151
|
+
M applications/underlord/vendor/plugins/nifty/tasks/shared/base.rake
|
152
|
+
")
|
153
|
+
|
154
|
+
expected = <<End
|
155
|
+
M gemables/calculator/test/calculator_test.rb
|
156
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
157
|
+
M applications/underlord/vendor/plugins/nifty/tasks/shared/base.rake
|
158
|
+
End
|
159
|
+
|
160
|
+
assert_equal expected, out = capture_output { Subversion::SvnCommand.execute('st') }
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_status_accepts_arguments
|
164
|
+
SvnCommand.execute('st -u /path/to/file1 file2')
|
165
|
+
assert_equal "svn status -u /path/to/file1 file2", Subversion.executed.join
|
166
|
+
|
167
|
+
Subversion.reset_executed
|
168
|
+
SvnCommand.execute('st dir --no-ignore')
|
169
|
+
# It will reorder some of the args (it puts all pass-through options and their args at the *beginning*), but that's okay...
|
170
|
+
assert_equal "svn status --no-ignore dir", Subversion.executed.join
|
171
|
+
|
172
|
+
end
|
173
|
+
end #class SvnStatusTest
|
174
|
+
|
175
|
+
class SvnUpdateTest < BaseSvnCommandTest
|
176
|
+
def test_1
|
177
|
+
capture_output { SvnCommand.execute("up -q file1 file2 -r 17") }
|
178
|
+
assert_equal "svn update -q -r 17 file1 file2", Subversion.executed.join
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
183
|
+
# Custom subcommands
|
184
|
+
|
185
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
186
|
+
class SvnEachUnaddedTest < BaseSvnCommandTest
|
187
|
+
def setup
|
188
|
+
super
|
189
|
+
Subversion.stubs(:status).returns("
|
190
|
+
M gemables/calculator/test/calculator_test.rb
|
191
|
+
X gemables/calculator/tasks/shared
|
192
|
+
? gemables/calculator/lib/calculator.rb
|
193
|
+
")
|
194
|
+
FileUtils.mkdir_p('gemables/calculator/lib/')
|
195
|
+
File.open(@filename = 'gemables/calculator/lib/calculator.rb', 'w') do |file|
|
196
|
+
file.puts "line 1 of calculator.rb"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
def teardown
|
200
|
+
file = 'gemables/calculator/lib/calculator.rb'
|
201
|
+
FileUtils.rm(file) if File.exist?(file)
|
202
|
+
FileUtils.rmdir('gemables/calculator/lib/')
|
203
|
+
FileUtils.rmdir('gemables/calculator/')
|
204
|
+
FileUtils.rmdir('gemables/')
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_add
|
208
|
+
output = simulate_input('a') do
|
209
|
+
capture_output { SvnCommand.execute('each_unadded dir') }
|
210
|
+
end
|
211
|
+
assert_match /What do you want to do with .*calculator\.rb/, output
|
212
|
+
assert_match /Adding/, output
|
213
|
+
assert_equal "svn add gemables/calculator/lib/calculator.rb", Subversion.executed.join
|
214
|
+
end
|
215
|
+
def test_ignore
|
216
|
+
output = simulate_input('i') do
|
217
|
+
capture_output { SvnCommand.execute('each_unadded dir') }
|
218
|
+
end
|
219
|
+
assert_match /What do you want to do with .*calculator\.rb/, output
|
220
|
+
assert_match /Ignoring/, output
|
221
|
+
assert_equal [
|
222
|
+
"svn propget svn:ignore gemables/calculator/lib",
|
223
|
+
"svn propset svn:ignore 'calculator.rb' gemables/calculator/lib"
|
224
|
+
], Subversion.executed
|
225
|
+
end
|
226
|
+
def test_preview_is_now_automatic
|
227
|
+
output = simulate_input(
|
228
|
+
#"p" + # Preview
|
229
|
+
"\n" # Blank line to do nothing (and exit loop)
|
230
|
+
) do
|
231
|
+
capture_output { SvnCommand.execute('each_unadded dir') }
|
232
|
+
end
|
233
|
+
assert_match /What do you want to do with .*calculator\.rb/, output
|
234
|
+
assert_match "line 1 of calculator.rb".to_re, output
|
235
|
+
end
|
236
|
+
def test_delete
|
237
|
+
assert File.exist?( @filename )
|
238
|
+
output = simulate_input(
|
239
|
+
'd' + # Delete
|
240
|
+
'y' # Yes I'm sure
|
241
|
+
) do
|
242
|
+
capture_output { SvnCommand.execute('each_unadded dir') }
|
243
|
+
end
|
244
|
+
assert_match /What do you want to do with .*calculator\.rb/, output
|
245
|
+
assert_match "Are you pretty much".to_re, output
|
246
|
+
assert_match /Deleting/, output
|
247
|
+
#print output
|
248
|
+
assert !File.exist?( @filename )
|
249
|
+
end
|
250
|
+
end #class
|
251
|
+
|
252
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
253
|
+
# Ignore
|
254
|
+
class SvnIgnoreTest < BaseSvnCommandTest
|
255
|
+
def test_svn_ignore_relative_to_wd
|
256
|
+
output = capture_output { SvnCommand.execute('ignore log') }
|
257
|
+
assert_equal '', output
|
258
|
+
assert_equal [
|
259
|
+
"svn propget svn:ignore ./",
|
260
|
+
"svn propset svn:ignore 'log' ./"
|
261
|
+
], Subversion.executed
|
262
|
+
end
|
263
|
+
def test_svn_ignore_relative_to_other_path
|
264
|
+
output = capture_output { SvnCommand.execute('ignore log/*') }
|
265
|
+
assert_equal '', output
|
266
|
+
assert_equal [
|
267
|
+
"svn propget svn:ignore log",
|
268
|
+
"svn propset svn:ignore '*' log"
|
269
|
+
], Subversion.executed
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
274
|
+
# Externals
|
275
|
+
|
276
|
+
# Since this doesn't work: Subversion::SvnCommand.any_instance.stubs(:system).returns(Proc.new {|a| p a; puts "Tried to call system(#{a})" })
|
277
|
+
class Subversion::SvnCommand
|
278
|
+
def system(*args)
|
279
|
+
Subversion::SvnCommand.executed_system << args.join(' ')
|
280
|
+
end
|
281
|
+
def self.executed_system
|
282
|
+
@@executed_system ||= []
|
283
|
+
end
|
284
|
+
def self.reset_executed_system(as = [])
|
285
|
+
@@executed_system = as
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
class SvnExternalsTest < BaseSvnCommandTest
|
290
|
+
|
291
|
+
# Causes .stub to break??
|
292
|
+
#def setup
|
293
|
+
#end
|
294
|
+
def set_up_stubs # This ought to just go in the normal "setup", but that caused weird errors. Ideas?
|
295
|
+
yaml = %q{
|
296
|
+
- !ruby/object:Subversion::ExternalsContainer
|
297
|
+
container_dir: /home/tyler/code/gemables/svn-command/test
|
298
|
+
entries: |+
|
299
|
+
shared http://code.qualitysmith.com/gemables/test_extensions/lib
|
300
|
+
|
301
|
+
- !ruby/object:Subversion::ExternalsContainer
|
302
|
+
container_dir: /home/tyler/code/gemables/svn-command/tasks
|
303
|
+
entries: |+
|
304
|
+
shared http://code.qualitysmith.com/gemables/shared_tasks/tasks
|
305
|
+
|
306
|
+
- !ruby/object:Subversion::ExternalsContainer
|
307
|
+
container_dir: /home/tyler/code/gemables/svn-command/doc_include
|
308
|
+
entries: |+
|
309
|
+
template http://code.qualitysmith.com/gemables/template/doc/template
|
310
|
+
}
|
311
|
+
external_containers = YAML.load yaml
|
312
|
+
Subversion.stubs(:externals_containers).returns(external_containers)
|
313
|
+
end
|
314
|
+
|
315
|
+
def test_svn_externalize
|
316
|
+
set_up_stubs
|
317
|
+
output = capture_output { SvnCommand.execute('externalize http://imaginethat.com/a/repo') }
|
318
|
+
assert_equal '', output
|
319
|
+
assert_equal [
|
320
|
+
"svn propget svn:externals ./",
|
321
|
+
"svn propset svn:externals 'repo http://imaginethat.com/a/repo' ./"
|
322
|
+
], Subversion.executed
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
def test_svn_externals
|
327
|
+
set_up_stubs
|
328
|
+
output = capture_output { SvnCommand.execute('externals') }
|
329
|
+
|
330
|
+
assert_contains output, %q(
|
331
|
+
|/home/tyler/code/gemables/svn-command/test
|
332
|
+
| * shared http://code.qualitysmith.com/gemables/test_extensions/lib
|
333
|
+
|/home/tyler/code/gemables/svn-command/tasks
|
334
|
+
| * shared http://code.qualitysmith.com/gemables/shared_tasks/tasks
|
335
|
+
|/home/tyler/code/gemables/svn-command/doc_include
|
336
|
+
| * template http://code.qualitysmith.com/gemables/template/doc/template
|
337
|
+
).margin
|
338
|
+
assert_equal [], Subversion.executed
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_svn_externals_containers
|
342
|
+
set_up_stubs
|
343
|
+
output = capture_output { SvnCommand.execute('externals_containers') }
|
344
|
+
|
345
|
+
assert_contains output, %q(
|
346
|
+
|/home/tyler/code/gemables/svn-command/test
|
347
|
+
|/home/tyler/code/gemables/svn-command/tasks
|
348
|
+
|/home/tyler/code/gemables/svn-command/doc_include
|
349
|
+
).margin
|
350
|
+
assert_equal [], Subversion.executed
|
351
|
+
end
|
352
|
+
|
353
|
+
def test_svn_edit_externals
|
354
|
+
set_up_stubs
|
355
|
+
output = simulate_input('yyy') do
|
356
|
+
capture_output { SvnCommand.execute('edit_externals') }
|
357
|
+
end
|
358
|
+
assert_match "No directory specified. Editing externals for *all*".to_re, output
|
359
|
+
|
360
|
+
assert_match /Do you want to edit svn:externals for this directory/, output
|
361
|
+
assert_equal [
|
362
|
+
"svn propedit svn:externals /home/tyler/code/gemables/svn-command/test",
|
363
|
+
"svn propedit svn:externals /home/tyler/code/gemables/svn-command/tasks",
|
364
|
+
"svn propedit svn:externals /home/tyler/code/gemables/svn-command/doc_include"
|
365
|
+
], Subversion::SvnCommand.executed_system
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
370
|
+
# Commit messages
|
371
|
+
|
372
|
+
class SvnGetMessageTest < BaseSvnCommandTest
|
373
|
+
def test_1
|
374
|
+
Subversion.stubs(:status_against_server).returns("Status against revision: 56")
|
375
|
+
output = simulate_input('i') do
|
376
|
+
capture_output { SvnCommand.execute('get_message') }
|
377
|
+
end
|
378
|
+
assert_match "Message for r56 :".to_re, output
|
379
|
+
assert_equal ["svn propget --revprop svn:log -r head"], Subversion.executed
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
class SvnSetMessageTest < BaseSvnCommandTest
|
384
|
+
def test_1
|
385
|
+
Subversion.stubs(:status_against_server).returns("Status against revision: 56")
|
386
|
+
output = simulate_input('i') do
|
387
|
+
capture_output { SvnCommand.execute('set_message "this is my message"') }
|
388
|
+
end
|
389
|
+
assert_match "Message before changing:".to_re, output
|
390
|
+
assert_match "Message for r56 :".to_re, output
|
391
|
+
assert_equal [
|
392
|
+
"svn propget --revprop svn:log -r head",
|
393
|
+
"svn propset --revprop svn:log -r head 'this is my message'"
|
394
|
+
], Subversion.executed
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
class SvnEditMessageTest < BaseSvnCommandTest
|
399
|
+
def test_1
|
400
|
+
Subversion.stubs(:status_against_server).returns("Status against revision: 56")
|
401
|
+
output = simulate_input('i') do
|
402
|
+
capture_output { SvnCommand.execute('edit_message') }
|
403
|
+
end
|
404
|
+
assert_equal ["svn propedit --revprop svn:log -r head"], Subversion.executed
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
409
|
+
|
410
|
+
class SvnViewCommitsTest < BaseSvnCommandTest
|
411
|
+
def test_parse_revision_ranges
|
412
|
+
assert_equal [134], SvnCommand.parse_revision_ranges(["134"])
|
413
|
+
assert_equal [134, 135, 136], SvnCommand.parse_revision_ranges(["134-136"])
|
414
|
+
assert_equal [134, 135, 136], SvnCommand.parse_revision_ranges(["134:136"])
|
415
|
+
assert_equal [134, 135, 136], SvnCommand.parse_revision_ranges(["134..136"])
|
416
|
+
assert_equal [134, 135, 136, 139], SvnCommand.parse_revision_ranges(["134-136", "139"])
|
417
|
+
end
|
418
|
+
def test_1
|
419
|
+
messages = Dictionary[
|
420
|
+
14, "Committed a bunch of really important stuff.",
|
421
|
+
15, "Fixed a typo.",
|
422
|
+
30, "Injected a horrible defect."
|
423
|
+
]
|
424
|
+
Subversion.stubs(:log).with("-r 14 -v ").returns(messages[14])
|
425
|
+
Subversion.stubs(:log).with("-r 15 -v ").returns(messages[15])
|
426
|
+
Subversion.stubs(:log).with("-r 30 -v ").returns(messages[30])
|
427
|
+
Subversion.stubs(:diff).returns(combined_diff = %q(
|
428
|
+
|Index: lib/svn_command.rb
|
429
|
+
|===================================================================
|
430
|
+
|--- lib/svn_command.rb (revision 2327)
|
431
|
+
|+++ lib/svn_command.rb (revision 2342)
|
432
|
+
|@@ -3,9 +3,11 @@
|
433
|
+
| require 'facets/more/command'
|
434
|
+
| require 'facets/core/string/margin'
|
435
|
+
| require 'facets/core/kernel/require_local'
|
436
|
+
|+require 'extensions/symbol'
|
437
|
+
| require 'pp'
|
438
|
+
| require 'termios'
|
439
|
+
| require 'stringio'
|
440
|
+
|+require 'escape' # http://www.a-k-r.org/escape/
|
441
|
+
).margin)
|
442
|
+
output = capture_output { SvnCommand.execute('view-commits -r 14:15 30') }
|
443
|
+
assert_equal %Q(
|
444
|
+
|#{messages.values.join("\n")}
|
445
|
+
|#{combined_diff}
|
446
|
+
|
|
447
|
+
).margin, output
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
|
452
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
453
|
+
end #module Subversion
|
454
|
+
|
455
|
+
|