subwrap 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/ProjectInfo.rb +54 -0
- data/Readme +458 -0
- data/bin/_subwrap_post_install +25 -0
- data/bin/command_completion_for_subwrap +21 -0
- data/bin/rscm_test +19 -0
- data/bin/subwrap +7 -0
- data/bin/svn +7 -0
- data/lib/subwrap.rb +6 -0
- data/lib/subwrap/subversion.rb +599 -0
- data/lib/subwrap/subversion_extensions.rb +148 -0
- data/lib/subwrap/svn_command.rb +1568 -0
- data/test/subversion_extensions_test.rb +72 -0
- data/test/subversion_test.rb +132 -0
- data/test/svn_command_test.rb +649 -0
- data/test/test_helper.rb +30 -0
- metadata +120 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require 'subversion_extensions'
|
3
|
+
|
4
|
+
Subversion.color = false # Makes testing simpler. We can just test that the *colorization* features are working via *manual* tests.
|
5
|
+
|
6
|
+
class SubversionExtensionsTest < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_status_lines_filter
|
11
|
+
|
12
|
+
#String.any_instance.stubs(:underline).returns(lambda {|a| a}) # Doesn't work! Lame! So we can't make the return value depend on the input?
|
13
|
+
String.any_instance.stubs(:underline).returns(lambda {' externals '})
|
14
|
+
|
15
|
+
input = <<End
|
16
|
+
M gemables/calculator/test/calculator_test.rb
|
17
|
+
X gemables/calculator/tasks/shared
|
18
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
19
|
+
|
20
|
+
Performing status on external item at 'plugins/flubber/tasks/shared'
|
21
|
+
|
22
|
+
Performing status on external item at 'applications/underlord/vendor/plugins/nifty'
|
23
|
+
X applications/underlord/vendor/plugins/nifty/tasks/shared
|
24
|
+
X applications/underlord/vendor/plugins/nifty/doc_include/template
|
25
|
+
|
26
|
+
Performing status on external item at 'applications/underlord/vendor/plugins/nifty/tasks/shared'
|
27
|
+
M applications/underlord/vendor/plugins/nifty/tasks/shared/base.rake
|
28
|
+
End
|
29
|
+
expected = <<End
|
30
|
+
M gemables/calculator/test/calculator_test.rb
|
31
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
32
|
+
________________________________________ externals ________________________________________
|
33
|
+
M applications/underlord/vendor/plugins/nifty/tasks/shared/base.rake
|
34
|
+
End
|
35
|
+
|
36
|
+
assert_equal expected, out = Subversion::Extensions.status_lines_filter( input ), out.inspect
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_update_lines_filter
|
40
|
+
input = <<End
|
41
|
+
U gemables/calculator/test/calculator_test.rb
|
42
|
+
U gemables/calculator/tasks/shared
|
43
|
+
U gemables/calculator/lib/calculator_extensions.rb
|
44
|
+
|
45
|
+
Fetching external item into 'plugins/flubber/tasks/shared'
|
46
|
+
U plugins/flubber/tasks/shared/blah.rb
|
47
|
+
External at revision 134143078
|
48
|
+
End
|
49
|
+
expected = <<End
|
50
|
+
U gemables/calculator/test/calculator_test.rb
|
51
|
+
U gemables/calculator/tasks/shared
|
52
|
+
U gemables/calculator/lib/calculator_extensions.rb
|
53
|
+
U plugins/flubber/tasks/shared/blah.rb
|
54
|
+
End
|
55
|
+
|
56
|
+
assert_equal expected, out = Subversion::Extensions.update_lines_filter( input ), out.inspect
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_unadded_filter
|
60
|
+
input = <<End
|
61
|
+
M gemables/calculator/test/calculator_test.rb
|
62
|
+
X gemables/calculator/tasks/shared
|
63
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
64
|
+
End
|
65
|
+
expected = <<End
|
66
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
67
|
+
End
|
68
|
+
|
69
|
+
assert_equal expected, out = Subversion::Extensions.unadded_lines_filter( input ), out.inspect
|
70
|
+
assert_equal ['gemables/calculator/lib/calculator_extensions.rb'], out = Subversion::Extensions.unadded_filter( input ), out.inspect
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,132 @@
|
|
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
|
+
Subversion.externalize('some/repo/path', :as => 'vendor/plugins/foo')
|
25
|
+
|
26
|
+
assert_equal "svn propset svn:externals '#{'path'} some/repo/path' .", Subversion.executed[1] # Used to be 'path'.ljust(29)
|
27
|
+
assert_equal "svn propset svn:externals '#{'foo'} some/repo/path' .", Subversion.executed[3]
|
28
|
+
assert_equal "svn propset svn:externals '#{'foo'} some/repo/path' local/path", Subversion.executed[5]
|
29
|
+
assert_equal "svn propset svn:externals '#{'foo'} some/repo/path' vendor/plugins", Subversion.executed[7]
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_remove
|
33
|
+
Subversion.remove 'foo', 'bar', 'hello/world'
|
34
|
+
assert_equal 'svn rm foo bar hello/world', Subversion.executed.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_remove_without_delete
|
38
|
+
tmpdir = "tmp#{$$}"
|
39
|
+
entries = "#{tmpdir}/.svn/entries"
|
40
|
+
FileUtils.mkdir_p "#{tmpdir}/.svn", :mode => 0755
|
41
|
+
File.open entries, 'w' do |file|
|
42
|
+
file.write <<-EOS
|
43
|
+
<?xml version="1.0" encoding="utf-8"?>
|
44
|
+
<wc-entries
|
45
|
+
xmlns="svn:">
|
46
|
+
<entry
|
47
|
+
name="existing_file"
|
48
|
+
kind="file"/>
|
49
|
+
<entry
|
50
|
+
name="just_added_file"
|
51
|
+
schedule="add"
|
52
|
+
kind="file"/>
|
53
|
+
<entry
|
54
|
+
name="unchanging_file"
|
55
|
+
kind="file"/>
|
56
|
+
</wc-entries>
|
57
|
+
EOS
|
58
|
+
end
|
59
|
+
File.chmod(0444, entries)
|
60
|
+
FileUtils.touch ["#{tmpdir}/existing_file", "#{tmpdir}/just_added_file", "#{tmpdir}/unchanging_file"]
|
61
|
+
|
62
|
+
begin
|
63
|
+
doc = REXML::Document.new(IO.read(entries))
|
64
|
+
assert_not_nil REXML::XPath.first(doc, '//entry[@name="existing_file"]')
|
65
|
+
Subversion.remove_without_delete "#{tmpdir}/existing_file"
|
66
|
+
# the element should now be scheduled for delete
|
67
|
+
doc = REXML::Document.new(IO.read(entries))
|
68
|
+
assert_not_nil REXML::XPath.first(doc, '//entry[@name="existing_file"][@schedule="delete"]')
|
69
|
+
|
70
|
+
doc = REXML::Document.new(IO.read(entries))
|
71
|
+
assert_not_nil REXML::XPath.first(doc, '//entry[@name="just_added_file"][@schedule="add"]')
|
72
|
+
Subversion.remove_without_delete "#{tmpdir}/just_added_file"
|
73
|
+
doc = REXML::Document.new(IO.read(entries))
|
74
|
+
# the element should now be gone
|
75
|
+
assert_nil REXML::XPath.first(doc, '//entry[@name="just_added_file"]')
|
76
|
+
ensure
|
77
|
+
FileUtils.rm_r tmpdir
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class DiffsParserTest < Test::Unit::TestCase
|
83
|
+
# def test_diff_class_acts_like_hash
|
84
|
+
# diff = Subversion::Diff['file.rb' => "some differences"]
|
85
|
+
# assert_equal 'some differences', diff['file.rb']
|
86
|
+
# end
|
87
|
+
def test_parser_error
|
88
|
+
assert_raise(Subversion::DiffsParser::ParseError) { Subversion::DiffsParser.new('what').parse }
|
89
|
+
end
|
90
|
+
def test_parser
|
91
|
+
diffs = Subversion::DiffsParser.new(<<End).parse
|
92
|
+
Index: lib/test_extensions.rb
|
93
|
+
===================================================================
|
94
|
+
--- lib/test_extensions.rb (revision 2871)
|
95
|
+
+++ lib/test_extensions.rb (revision 2872)
|
96
|
+
@@ -6,6 +6,7 @@
|
97
|
+
require_local 'some_file'
|
98
|
+
|
99
|
+
gem 'quality_extensions'
|
100
|
+
+require 'quality_extensions/regexp/join'
|
101
|
+
require 'quality_extensions/kernel/capture_output.rb'
|
102
|
+
require 'quality_extensions/kernel/simulate_input.rb'
|
103
|
+
|
104
|
+
Index: Readme
|
105
|
+
===================================================================
|
106
|
+
--- Readme (revision 0)
|
107
|
+
+++ Readme (revision 2872)
|
108
|
+
@@ -0,0 +1,2 @@
|
109
|
+
+* Blah blah blah
|
110
|
+
+* Blah blah blah
|
111
|
+
End
|
112
|
+
assert_equal Subversion::Diffs, diffs.class
|
113
|
+
assert diffs.frozen?
|
114
|
+
assert_equal 2, diffs.keys.size
|
115
|
+
assert_equal 2, diffs.values.size
|
116
|
+
assert_equal <<End, diffs['lib/test_extensions.rb'].diff
|
117
|
+
require_local 'some_file'
|
118
|
+
|
119
|
+
gem 'quality_extensions'
|
120
|
+
+require 'quality_extensions/regexp/join'
|
121
|
+
require 'quality_extensions/kernel/capture_output.rb'
|
122
|
+
require 'quality_extensions/kernel/simulate_input.rb'
|
123
|
+
|
124
|
+
End
|
125
|
+
assert_equal <<End, diffs['Readme'].diff
|
126
|
+
+* Blah blah blah
|
127
|
+
+* Blah blah blah
|
128
|
+
End
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
@@ -0,0 +1,649 @@
|
|
1
|
+
$mock_subversion = true
|
2
|
+
require File.dirname(__FILE__) + '/test_helper'
|
3
|
+
require_local '../lib/subwrap/svn_command.rb'
|
4
|
+
require 'facets/core/string/to_re'
|
5
|
+
require 'yaml'
|
6
|
+
require 'facets/core/module/alias_method_chain'
|
7
|
+
require 'quality_extensions/colored/toggleability'
|
8
|
+
require 'quality_extensions/regexp/join'
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
# Makes testing simpler. We can test all the *colorization* features via *manual* testing (since they're not as critical).
|
13
|
+
Subversion.color = false
|
14
|
+
String.color_on! false
|
15
|
+
|
16
|
+
#pp Subversion::SvnCommand.instance_methods.sort
|
17
|
+
|
18
|
+
# Since this doesn't work: Subversion::SvnCommand.any_instance.stubs(:system).returns(Proc.new {|a| p a; puts "Tried to call system(#{a})" })
|
19
|
+
class Subversion::SvnCommand
|
20
|
+
def system(*args)
|
21
|
+
Subversion::SvnCommand.executed_system << args.join(' ')
|
22
|
+
end
|
23
|
+
def self.executed_system
|
24
|
+
@@executed_system ||= []
|
25
|
+
end
|
26
|
+
def self.reset_executed_system(as = [])
|
27
|
+
@@executed_system = as
|
28
|
+
end
|
29
|
+
end
|
30
|
+
module Kernel
|
31
|
+
def exit_code
|
32
|
+
o = Object.new
|
33
|
+
class << o
|
34
|
+
def success?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
o
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
module Subversion
|
44
|
+
class BaseSvnCommandTest < Test::Unit::TestCase
|
45
|
+
def setup
|
46
|
+
Subversion.reset_executed
|
47
|
+
end
|
48
|
+
def test_dummy_test
|
49
|
+
# Because it tries to run this base class too!
|
50
|
+
end
|
51
|
+
# When we don't care what the output is -- we just don't want to see it while running the test!
|
52
|
+
def silence(&block)
|
53
|
+
capture_output(&block)
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
class SvnCommandTest < BaseSvnCommandTest
|
60
|
+
def test_invalid_quotes_gives_informative_error
|
61
|
+
assert_exception(ArgumentError, lambda { |exception|
|
62
|
+
assert_equal "Unmatched single quote: '", exception.message
|
63
|
+
}) do
|
64
|
+
SvnCommand.execute("foo -m 'stuff''")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
def test_unrecognized_option
|
68
|
+
output = capture_output($stderr) do
|
69
|
+
assert_exception(SystemExit, lambda { |exception|
|
70
|
+
}) do
|
71
|
+
SvnCommand.execute("status --blarg")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
assert_match /Unknown option '--blarg'/, output
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class ArgEscapingTest < BaseSvnCommandTest
|
79
|
+
def test_argument_escaping
|
80
|
+
args = nil
|
81
|
+
silence { SvnCommand.execute( args = %q{commit -m 'a message with lots of !&`$0 |()<> garbage'} ) }
|
82
|
+
assert_equal "svn #{args} --force-log", Subversion.executed[0]
|
83
|
+
end
|
84
|
+
def test_asterisk
|
85
|
+
# 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...
|
86
|
+
SvnCommand.execute("add dir/* --non-recursive")
|
87
|
+
assert_equal "svn add --non-recursive 'dir/*'", Subversion.executed[0]
|
88
|
+
# Actually, I lied... The * will *not* be expanded in the (rather uncommon) case that there are *no files matching the glob*. But that seems like a bash/shell problem, not our concern. Demo:
|
89
|
+
# > mkdir foo
|
90
|
+
# > echo foo/*
|
91
|
+
# foo/*
|
92
|
+
# > touch foo/file
|
93
|
+
# > echo foo/*
|
94
|
+
# foo/file
|
95
|
+
end
|
96
|
+
def test_multiline
|
97
|
+
args = nil
|
98
|
+
silence { SvnCommand.execute(args = "commit -m 'This is a
|
99
|
+
|multi-line
|
100
|
+
|message'".margin) }
|
101
|
+
assert_equal "svn #{args} --force-log", Subversion.executed[0]
|
102
|
+
end
|
103
|
+
def test_multiword_arg_preserved_even_for_passthrough_subcommands
|
104
|
+
# foo, for instance is entirely a passthrough subcommand; no 'def foo' exists, so it's handled entirely through method_missing.
|
105
|
+
args = nil
|
106
|
+
silence { SvnCommand.execute( args = %q{foo -m 'a multi-word message' --something-else 'something else'} ) }
|
107
|
+
assert_equal "svn #{args}", Subversion.executed[0]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
112
|
+
# Built-in subcommands
|
113
|
+
|
114
|
+
# Test method_missing. Can we still call svn info, rm, etc. even if we haven't written subcommand modules for them?
|
115
|
+
class SubcommandPassThroughTest < BaseSvnCommandTest
|
116
|
+
def test_1
|
117
|
+
SvnCommand.execute("rm -q file1 file2 --force")
|
118
|
+
assert_equal "svn rm -q file1 file2 --force", Subversion.executed[0]
|
119
|
+
end
|
120
|
+
def test_2
|
121
|
+
SvnCommand.execute("info -q file1 file2 --force")
|
122
|
+
assert_equal "svn info -q file1 file2 --force", Subversion.executed[0]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class SvnAddTest < BaseSvnCommandTest
|
127
|
+
def test_1
|
128
|
+
end
|
129
|
+
def test_2
|
130
|
+
SvnCommand.execute('add "a b"')
|
131
|
+
assert_equal "svn add 'a b'", Subversion.executed[0]
|
132
|
+
end
|
133
|
+
def test_3
|
134
|
+
SvnCommand.execute('add a b')
|
135
|
+
assert_equal "svn add a b", Subversion.executed[0]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class SvnCommitTest < BaseSvnCommandTest
|
140
|
+
def test_1
|
141
|
+
silence { SvnCommand.execute("commit -m 'just an ordinary commit message!'") }
|
142
|
+
assert_equal "svn commit -m 'just an ordinary commit message!' --force-log", Subversion.executed[0]
|
143
|
+
end
|
144
|
+
def test_lots_of_options
|
145
|
+
silence { SvnCommand.execute("commit --non-recursive -q -m '' --targets some_file ") }
|
146
|
+
assert_equal "svn commit --non-recursive -q -m '' --targets some_file --force-log", Subversion.executed[0]
|
147
|
+
end
|
148
|
+
def test_that_complex_quoting_doesnt_confuse_it
|
149
|
+
original_message = "Can't decide how many \"'quotes'\" to use!"
|
150
|
+
silence { SvnCommand.execute(%Q{commit -m "#{original_message.gsub('"', '\"')}"}) }
|
151
|
+
|
152
|
+
expected_escaped_part = %q{'Can'\\''t decide how many "'\\''quotes'\\''" to use!'}
|
153
|
+
assert_equal "svn commit -m #{expected_escaped_part} --force-log", Subversion.executed[0]
|
154
|
+
assert_equal original_message, `echo #{expected_escaped_part}`.chomp # We should have gotten back exactly what we put in originally
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class SvnDiffTest < BaseSvnCommandTest
|
159
|
+
def test_1
|
160
|
+
SvnCommand.execute("diff -r 123:125")
|
161
|
+
assert_equal [
|
162
|
+
"svn diff -r 123:125 ./",
|
163
|
+
"svn status ./"
|
164
|
+
], Subversion.executed
|
165
|
+
end
|
166
|
+
def test_2
|
167
|
+
#:fixme:
|
168
|
+
#capture_output { SvnCommand.execute("diff -r { 2006-07-01 }") }
|
169
|
+
#p Subversion.executed
|
170
|
+
# Currently does this, since it thinks the arity is 1: 2006-07-01 } -r '{'
|
171
|
+
#assert_equal "svn diff -r { 2006-07-01 }", Subversion.executed[0]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class SvnHelpTest < BaseSvnCommandTest
|
176
|
+
def test_1
|
177
|
+
output = capture_output { SvnCommand.execute("help") }
|
178
|
+
assert_equal "svn help ", Subversion.executed[0]
|
179
|
+
assert_match /wrapper/, output
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class SvnLogTest < BaseSvnCommandTest
|
184
|
+
def test_1
|
185
|
+
capture_output { SvnCommand.execute("log") }
|
186
|
+
assert_equal "svn log ", Subversion.executed[0]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class SvnStatusTest < BaseSvnCommandTest
|
191
|
+
# Duplicates a test in subversion_extensions_test.rb -- maybe can abbreviate this and leave the detailed filter tests to the other TestCase
|
192
|
+
def test_status_does_some_filtering
|
193
|
+
Subversion.stubs(:status).returns("
|
194
|
+
M gemables/calculator/test/calculator_test.rb
|
195
|
+
X gemables/calculator/tasks/shared
|
196
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
197
|
+
|
198
|
+
Performing status on external item at 'plugins/flubber/tasks/shared'
|
199
|
+
|
200
|
+
Performing status on external item at 'applications/underlord/vendor/plugins/nifty'
|
201
|
+
X applications/underlord/vendor/plugins/nifty/tasks/shared
|
202
|
+
X applications/underlord/vendor/plugins/nifty/lib/calculator
|
203
|
+
X applications/underlord/vendor/plugins/nifty/doc_include/template
|
204
|
+
|
205
|
+
Performing status on external item at 'applications/underlord/vendor/plugins/nifty/tasks/shared'
|
206
|
+
M applications/underlord/vendor/plugins/nifty/tasks/shared/base.rake
|
207
|
+
")
|
208
|
+
String.any_instance.stubs(:underline).returns(lambda {' externals '})
|
209
|
+
|
210
|
+
expected = <<End
|
211
|
+
M gemables/calculator/test/calculator_test.rb
|
212
|
+
? gemables/calculator/lib/calculator_extensions.rb
|
213
|
+
________________________________________ externals ________________________________________
|
214
|
+
M applications/underlord/vendor/plugins/nifty/tasks/shared/base.rake
|
215
|
+
End
|
216
|
+
|
217
|
+
assert_equal expected, out = capture_output { Subversion::SvnCommand.execute('st') }
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_status_accepts_arguments
|
221
|
+
SvnCommand.execute('st -u /path/to/file1 file2')
|
222
|
+
assert_equal "svn status -u /path/to/file1 file2", Subversion.executed[0]
|
223
|
+
|
224
|
+
Subversion.reset_executed
|
225
|
+
SvnCommand.execute('st dir --no-ignore')
|
226
|
+
# It will reorder some of the args (it puts all pass-through options and their args at the *beginning*), but that's okay...
|
227
|
+
assert_equal "svn status --no-ignore dir", Subversion.executed[0]
|
228
|
+
|
229
|
+
end
|
230
|
+
end #class SvnStatusTest
|
231
|
+
|
232
|
+
class SvnUpdateTest < BaseSvnCommandTest
|
233
|
+
def test_1
|
234
|
+
capture_output { SvnCommand.execute("up -q file1 file2 -r 17") }
|
235
|
+
assert_equal "svn update -q -r 17 file1 file2", Subversion.executed[0]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
240
|
+
# Custom subcommands
|
241
|
+
|
242
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
243
|
+
|
244
|
+
# Notes about this test:
|
245
|
+
# If you start seeing errors like this:
|
246
|
+
# NoMethodError: undefined method `chr' for nil:NilClass
|
247
|
+
# coming from one of the $stdin.getc.chr lines, it means that you didn't feed it enough simulated input! It was expectin to get
|
248
|
+
# another character from stdin but you didn't supply one!
|
249
|
+
|
250
|
+
class SvnEachUnaddedTest < BaseSvnCommandTest
|
251
|
+
def setup
|
252
|
+
super
|
253
|
+
FileUtils.rm_rf('temp_dir/')
|
254
|
+
FileUtils.mkdir_p('temp_dir/calculator/lib/')
|
255
|
+
File.open(@filename = 'temp_dir/calculator/lib/unused.rb', 'w') { |file| file.puts "line 1 of unused.rb" }
|
256
|
+
end
|
257
|
+
def teardown
|
258
|
+
FileUtils.rm_rf('temp_dir/')
|
259
|
+
end
|
260
|
+
|
261
|
+
def stub_status_1
|
262
|
+
Subversion.stubs(:status).returns("
|
263
|
+
M temp_dir/calculator/test/calculator_test.rb
|
264
|
+
X temp_dir/calculator/tasks/shared
|
265
|
+
? temp_dir/calculator/lib/unused.rb
|
266
|
+
")
|
267
|
+
end
|
268
|
+
def test_add
|
269
|
+
stub_status_1
|
270
|
+
output = simulate_input('a') do
|
271
|
+
capture_output { SvnCommand.execute('each_unadded dir') }
|
272
|
+
end
|
273
|
+
assert_match /What do you want to do with .*unused\.rb/, output
|
274
|
+
assert_match /Adding/, output
|
275
|
+
assert_equal "svn add temp_dir/calculator/lib/unused.rb", Subversion.executed[0]
|
276
|
+
end
|
277
|
+
def test_ignore
|
278
|
+
stub_status_1
|
279
|
+
output = simulate_input('i') do
|
280
|
+
capture_output { SvnCommand.execute('each_unadded dir') }
|
281
|
+
end
|
282
|
+
assert_match /What do you want to do with .*unused\.rb/, output
|
283
|
+
assert_match /Ignoring/, output
|
284
|
+
assert_equal [
|
285
|
+
"svn propget svn:ignore temp_dir/calculator/lib",
|
286
|
+
"svn propset svn:ignore 'unused.rb' temp_dir/calculator/lib"
|
287
|
+
], Subversion.executed
|
288
|
+
end
|
289
|
+
def test_preview_is_now_automatic
|
290
|
+
stub_status_1
|
291
|
+
output = simulate_input(
|
292
|
+
#"p" + # Preview
|
293
|
+
"\n" + # Blank line to do nothing (and exit loop)
|
294
|
+
"\n" # Blank line to do nothing (and exit loop)
|
295
|
+
) do
|
296
|
+
capture_output { SvnCommand.execute('each_unadded dir') }
|
297
|
+
end
|
298
|
+
assert_match /What do you want to do with .*unused\.rb/, output
|
299
|
+
assert_match "line 1 of unused.rb".to_re, output
|
300
|
+
end
|
301
|
+
def test_delete
|
302
|
+
Subversion.stubs(:status).returns("
|
303
|
+
M temp_dir/calculator/test/calculator_test.rb
|
304
|
+
X temp_dir/calculator/tasks/shared
|
305
|
+
? temp_dir/calculator/lib/unused.rb
|
306
|
+
? temp_dir/calculator/lib/useless_directory
|
307
|
+
")
|
308
|
+
FileUtils.mkdir_p(@dirname = 'temp_dir/calculator/lib/useless_directory')
|
309
|
+
File.open( 'temp_dir/calculator/lib/useless_directory/foo', 'w') { |file| file.puts "line 1 of foo" }
|
310
|
+
|
311
|
+
assert File.exist?( @dirname )
|
312
|
+
assert File.exist?( @filename )
|
313
|
+
output = simulate_input(
|
314
|
+
'd' + # Delete
|
315
|
+
# (The file doesn't require confirmation.)
|
316
|
+
'd' + # Delete
|
317
|
+
'y' # Yes I'm sure (The non-empty directory does.)
|
318
|
+
) do
|
319
|
+
capture_output { SvnCommand.execute('each_unadded dir') }
|
320
|
+
end
|
321
|
+
assert_match /What do you want to do with .*unused\.rb/, output
|
322
|
+
assert_match /What do you want to do with .*useless_directory/, output
|
323
|
+
assert_match /Are you .*SURE/, output
|
324
|
+
assert_match /Deleting.*Deleting/m, output
|
325
|
+
assert !File.exist?( @filename )
|
326
|
+
assert !File.exist?( @dirname )
|
327
|
+
end
|
328
|
+
end #class
|
329
|
+
|
330
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
331
|
+
# Ignore
|
332
|
+
class SvnIgnoreTest < BaseSvnCommandTest
|
333
|
+
def test_svn_ignore_relative_to_wd
|
334
|
+
output = capture_output { SvnCommand.execute('ignore log') }
|
335
|
+
assert_equal '', output
|
336
|
+
assert_equal [
|
337
|
+
"svn propget svn:ignore ./",
|
338
|
+
"svn propset svn:ignore 'log' ./"
|
339
|
+
], Subversion.executed
|
340
|
+
end
|
341
|
+
def test_svn_ignore_relative_to_other_path
|
342
|
+
output = capture_output { SvnCommand.execute('ignore log/*') }
|
343
|
+
assert_equal '', output
|
344
|
+
assert_equal [
|
345
|
+
"svn propget svn:ignore log",
|
346
|
+
"svn propset svn:ignore '*' log"
|
347
|
+
], Subversion.executed
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
352
|
+
# Externals
|
353
|
+
|
354
|
+
class SvnExternalsTest < BaseSvnCommandTest
|
355
|
+
|
356
|
+
# Causes .stub to break??
|
357
|
+
#def setup
|
358
|
+
#end
|
359
|
+
def set_up_stubs # This ought to just go in the normal "setup", but that caused weird errors. Ideas?
|
360
|
+
yaml = %q{
|
361
|
+
- !ruby/object:Subversion::ExternalsContainer
|
362
|
+
container_dir: /home/tyler/code/gemables/subwrap/test
|
363
|
+
entries: |+
|
364
|
+
shared http://code.qualitysmith.com/gemables/test_extensions/lib
|
365
|
+
|
366
|
+
- !ruby/object:Subversion::ExternalsContainer
|
367
|
+
container_dir: /home/tyler/code/gemables/subwrap/tasks
|
368
|
+
entries: |+
|
369
|
+
shared http://code.qualitysmith.com/gemables/shared_tasks/tasks
|
370
|
+
|
371
|
+
- !ruby/object:Subversion::ExternalsContainer
|
372
|
+
container_dir: /home/tyler/code/gemables/subwrap/doc_include
|
373
|
+
entries: |+
|
374
|
+
template http://code.qualitysmith.com/gemables/template/doc/template
|
375
|
+
}
|
376
|
+
external_containers = YAML.load yaml
|
377
|
+
Subversion.stubs(:externals_containers).returns(external_containers)
|
378
|
+
end
|
379
|
+
|
380
|
+
def test_svn_externalize
|
381
|
+
set_up_stubs
|
382
|
+
output = capture_output { SvnCommand.execute('externalize http://imaginethat.com/a/repo') }
|
383
|
+
assert_equal '', output
|
384
|
+
assert_equal [
|
385
|
+
"svn propget svn:externals .",
|
386
|
+
"svn propset svn:externals 'repo http://imaginethat.com/a/repo' ."
|
387
|
+
], Subversion.executed
|
388
|
+
end
|
389
|
+
|
390
|
+
|
391
|
+
def test_svn_externals_outline
|
392
|
+
set_up_stubs
|
393
|
+
output = capture_output { SvnCommand.execute('externals_outline') }
|
394
|
+
|
395
|
+
assert_contains output, %q(
|
396
|
+
|/home/tyler/code/gemables/subwrap/test
|
397
|
+
| * shared http://code.qualitysmith.com/gemables/test_extensions/lib
|
398
|
+
|/home/tyler/code/gemables/subwrap/tasks
|
399
|
+
| * shared http://code.qualitysmith.com/gemables/shared_tasks/tasks
|
400
|
+
|/home/tyler/code/gemables/subwrap/doc_include
|
401
|
+
| * template http://code.qualitysmith.com/gemables/template/doc/template
|
402
|
+
).margin
|
403
|
+
assert_equal [], Subversion.executed
|
404
|
+
end
|
405
|
+
|
406
|
+
def test_svn_externals_items
|
407
|
+
set_up_stubs
|
408
|
+
output = capture_output { SvnCommand.execute('externals_items') }
|
409
|
+
|
410
|
+
# :todo:
|
411
|
+
|
412
|
+
assert_equal [], Subversion.executed
|
413
|
+
end
|
414
|
+
|
415
|
+
def test_svn_externals_containers
|
416
|
+
set_up_stubs
|
417
|
+
output = capture_output { SvnCommand.execute('externals_containers') }
|
418
|
+
|
419
|
+
assert_contains output, %q(
|
420
|
+
|/home/tyler/code/gemables/subwrap/test
|
421
|
+
|/home/tyler/code/gemables/subwrap/tasks
|
422
|
+
|/home/tyler/code/gemables/subwrap/doc_include
|
423
|
+
).margin
|
424
|
+
assert_equal [], Subversion.executed
|
425
|
+
end
|
426
|
+
|
427
|
+
def test_svn_edit_externals
|
428
|
+
set_up_stubs
|
429
|
+
output = simulate_input('yyy') do
|
430
|
+
capture_output { SvnCommand.execute('edit_externals') }
|
431
|
+
end
|
432
|
+
assert_match "No directory specified. Editing externals for *all*".to_re, output
|
433
|
+
|
434
|
+
assert_match /Do you want to edit svn:externals for this directory/, output
|
435
|
+
assert_equal [
|
436
|
+
"svn propedit svn:externals /home/tyler/code/gemables/subwrap/test",
|
437
|
+
"svn propedit svn:externals /home/tyler/code/gemables/subwrap/tasks",
|
438
|
+
"svn propedit svn:externals /home/tyler/code/gemables/subwrap/doc_include"
|
439
|
+
], Subversion.executed
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
444
|
+
# Commit messages
|
445
|
+
|
446
|
+
class SvnGetMessageTest < BaseSvnCommandTest
|
447
|
+
def test_1
|
448
|
+
Subversion.stubs(:status_against_server).returns("Status against revision: 56")
|
449
|
+
output = simulate_input('i') do
|
450
|
+
capture_output { SvnCommand.execute('get_message') }
|
451
|
+
end
|
452
|
+
assert_match "Message for r56 :".to_re, output
|
453
|
+
assert_equal ["svn propget --revprop svn:log -r head"], Subversion.executed
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
class SvnSetMessageTest < BaseSvnCommandTest
|
458
|
+
def test_1
|
459
|
+
Subversion.stubs(:status_against_server).returns("Status against revision: 56")
|
460
|
+
output = simulate_input('i') do
|
461
|
+
capture_output { SvnCommand.execute('set_message "this is my message"') }
|
462
|
+
end
|
463
|
+
assert_match "Message before changing:".to_re, output
|
464
|
+
assert_match "Message for r56 :".to_re, output
|
465
|
+
assert_equal [
|
466
|
+
"svn propget --revprop svn:log -r head",
|
467
|
+
"svn propset --revprop svn:log -r head 'this is my message'"
|
468
|
+
], Subversion.executed
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
class SvnEditMessageTest < BaseSvnCommandTest
|
473
|
+
def test_1
|
474
|
+
Subversion.stubs(:status_against_server).returns("Status against revision: 56")
|
475
|
+
Subversion.stubs(:get_revision_property).returns("The value I just set it to using vim, my favorite editor")
|
476
|
+
output = simulate_input('i') do
|
477
|
+
capture_output { SvnCommand.execute('edit_message') }
|
478
|
+
end
|
479
|
+
assert_equal ["svn propedit --revprop svn:log ./ -r head"], Subversion.executed
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
class SvnEditMessageTest < BaseSvnCommandTest
|
484
|
+
def test_can_actually_delete_property_too
|
485
|
+
Subversion.stubs(:status_against_server).returns("Status against revision: 56")
|
486
|
+
Subversion.stubs(:get_revision_property).returns("")
|
487
|
+
output = simulate_input(
|
488
|
+
'y' # Yes I'm sure I want to delete the svn:fooo property for this revision.
|
489
|
+
) do
|
490
|
+
capture_output { SvnCommand.execute('edit_revision_property svn:foo') }
|
491
|
+
end
|
492
|
+
assert_match /Are you sure you want to delete/, output
|
493
|
+
assert_equal [
|
494
|
+
"svn propedit --revprop svn:foo ./ -r head",
|
495
|
+
"svn propdel --revprop svn:foo -r head"
|
496
|
+
], Subversion.executed
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
501
|
+
|
502
|
+
class SvnViewCommitsTest < BaseSvnCommandTest
|
503
|
+
def test_parse_revision_ranges
|
504
|
+
assert_equal [134], SvnCommand.parse_revision_ranges(["134"])
|
505
|
+
assert_equal [134, 135, 136], SvnCommand.parse_revision_ranges(["134-136"])
|
506
|
+
assert_equal [134, 135, 136], SvnCommand.parse_revision_ranges(["134:136"])
|
507
|
+
assert_equal [134, 135, 136], SvnCommand.parse_revision_ranges(["134..136"])
|
508
|
+
assert_equal [134, 135, 136, 139], SvnCommand.parse_revision_ranges(["134-136", "139"])
|
509
|
+
end
|
510
|
+
def test_1
|
511
|
+
messages = Dictionary[
|
512
|
+
14, "Committed a bunch of really important stuff.",
|
513
|
+
15, "Fixed a typo.",
|
514
|
+
30, "Injected a horrible defect."
|
515
|
+
]
|
516
|
+
Subversion.stubs(:log).with("-r 14 -v ").returns(messages[14])
|
517
|
+
Subversion.stubs(:log).with("-r 15 -v ").returns(messages[15])
|
518
|
+
Subversion.stubs(:log).with("-r 30 -v ").returns(messages[30])
|
519
|
+
Subversion.stubs(:diff).returns(combined_diff = %q(
|
520
|
+
|Index: lib/svn_command.rb
|
521
|
+
|===================================================================
|
522
|
+
|--- lib/svn_command.rb (revision 2327)
|
523
|
+
|+++ lib/svn_command.rb (revision 2342)
|
524
|
+
|@@ -3,9 +3,11 @@
|
525
|
+
| require 'facets/more/command'
|
526
|
+
| require 'facets/core/string/margin'
|
527
|
+
| require 'facets/core/kernel/require_local'
|
528
|
+
|+require 'extensions/symbol'
|
529
|
+
| require 'pp'
|
530
|
+
| require 'termios'
|
531
|
+
| require 'stringio'
|
532
|
+
|+require 'escape' # http://www.a-k-r.org/escape/
|
533
|
+
).margin)
|
534
|
+
output = capture_output { SvnCommand.execute('view-commits -r 14:15 30') }
|
535
|
+
assert_equal %Q(
|
536
|
+
|#{messages.values.join("\n")}
|
537
|
+
|#{combined_diff}
|
538
|
+
|
|
539
|
+
).margin, output
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
544
|
+
# Changeset/commit/log Browser
|
545
|
+
|
546
|
+
class SvnRevisionsTest < BaseSvnCommandTest
|
547
|
+
def set_up_stubs
|
548
|
+
Subversion.stubs(:revisions).returns(
|
549
|
+
begin
|
550
|
+
RSCM::Revisions.class_eval do
|
551
|
+
attr_accessor :revisions
|
552
|
+
end
|
553
|
+
RSCM::Revision.class_eval do
|
554
|
+
attr_accessor :files
|
555
|
+
end
|
556
|
+
|
557
|
+
file1 = RSCM::RevisionFile.new
|
558
|
+
file1.status = 'added'.upcase
|
559
|
+
file1.path = 'dir/file1'
|
560
|
+
file2 = RSCM::RevisionFile.new
|
561
|
+
file2.status = 'modified'.upcase
|
562
|
+
file2.path = 'dir/file2'
|
563
|
+
|
564
|
+
revision1 = RSCM::Revision.new
|
565
|
+
revision1.identifier = 1800
|
566
|
+
revision1.developer = 'tyler'
|
567
|
+
revision1.time = Time.utc(2007, 12, 01)
|
568
|
+
revision1.message = 'I say! Quite the storm, what!'
|
569
|
+
revision1.files = [file1, file2]
|
570
|
+
|
571
|
+
revision2 = RSCM::Revision.new
|
572
|
+
revision2.identifier = 1801
|
573
|
+
revision2.developer = 'tyler'
|
574
|
+
revision2.time = Time.utc(2007, 12, 02)
|
575
|
+
revision2.message = 'These Romans are crazy!'
|
576
|
+
revision2.files = [file2]
|
577
|
+
|
578
|
+
revisions = RSCM::Revisions.new
|
579
|
+
revisions.revisions = [revision1, revision2]
|
580
|
+
end
|
581
|
+
)
|
582
|
+
Subversion.stubs(:latest_revision).returns(42)
|
583
|
+
Subversion.stubs(:latest_revision_for_path).returns(42)
|
584
|
+
Subversion.stubs(:diff).returns("the diff")
|
585
|
+
end
|
586
|
+
|
587
|
+
def test_view_changeset
|
588
|
+
set_up_stubs
|
589
|
+
|
590
|
+
output = simulate_input(
|
591
|
+
'v' + # View this changeset
|
592
|
+
"\n" + # Continue to revision 1800
|
593
|
+
"\n" # Try to continue, but of course there won't be any more revisions, so it will exit.
|
594
|
+
) do
|
595
|
+
capture_output { SvnCommand.execute('revisions') }
|
596
|
+
end
|
597
|
+
#puts output
|
598
|
+
#require 'unroller'
|
599
|
+
#Unroller::trace :exclude_classes => /PP|PrettyPrint/ do
|
600
|
+
|
601
|
+
assert_match Regexp.loose_join(
|
602
|
+
"Getting list of revisions for './' ...
|
603
|
+
2 revisions found. Starting with most recent revision and going backward in time...",
|
604
|
+
|
605
|
+
# Show 1800
|
606
|
+
"2. r1800 | tyler | 2007-12-01 00:00:00
|
607
|
+
I say! Quite the storm, what!
|
608
|
+
|
609
|
+
A dir/file1
|
610
|
+
M dir/file2
|
611
|
+
r1800: View this changeset, Diff against specific revision, Grep the changeset, List or Edit revision properties, svn Cat all files, grep the cat,
|
612
|
+
mark as Reviewed, edit log Message, or browse using Up/Down/Enter keys >",
|
613
|
+
|
614
|
+
# Show the diff
|
615
|
+
"Diffing 1799:1800...",
|
616
|
+
"the diff",
|
617
|
+
|
618
|
+
# Show 1800 again
|
619
|
+
"r1800: View this changeset, Diff against specific revision, Grep the changeset, List or Edit revision properties, svn Cat all files, grep the cat,
|
620
|
+
mark as Reviewed, edit log Message, or browse using Up/Down/Enter keys > Next...",
|
621
|
+
|
622
|
+
# Now show 1801
|
623
|
+
"1. r1801 | tyler | 2007-12-02 00:00:00
|
624
|
+
These Romans are crazy!
|
625
|
+
|
626
|
+
M dir/file2
|
627
|
+
r1801: View this changeset, Diff against specific revision, Grep the changeset, List or Edit revision properties, svn Cat all files, grep the cat,
|
628
|
+
mark as Reviewed, edit log Message, or browse using Up/Down/Enter keys > Next...",
|
629
|
+
:multi_line => true
|
630
|
+
), output
|
631
|
+
|
632
|
+
|
633
|
+
=begin
|
634
|
+
/.*/
|
635
|
+
}.*
|
636
|
+
=end
|
637
|
+
|
638
|
+
|
639
|
+
assert_equal [
|
640
|
+
#"svn status -u ./" # To find head
|
641
|
+
], Subversion.executed
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
|
646
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
647
|
+
end #module Subversion
|
648
|
+
|
649
|
+
|