subwrap 0.3.5
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/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
|
+
|