net-ssh 5.0.0.beta1 → 5.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.rubocop_todo.yml +98 -258
  5. data/CHANGES.txt +8 -0
  6. data/Gemfile +1 -3
  7. data/Rakefile +37 -39
  8. data/lib/net/ssh.rb +26 -25
  9. data/lib/net/ssh/authentication/agent.rb +228 -225
  10. data/lib/net/ssh/authentication/certificate.rb +166 -164
  11. data/lib/net/ssh/authentication/constants.rb +17 -14
  12. data/lib/net/ssh/authentication/ed25519.rb +107 -104
  13. data/lib/net/ssh/authentication/ed25519_loader.rb +32 -28
  14. data/lib/net/ssh/authentication/key_manager.rb +5 -3
  15. data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
  16. data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
  17. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -4
  18. data/lib/net/ssh/authentication/methods/none.rb +10 -10
  19. data/lib/net/ssh/authentication/methods/password.rb +13 -13
  20. data/lib/net/ssh/authentication/methods/publickey.rb +54 -55
  21. data/lib/net/ssh/authentication/pageant.rb +468 -465
  22. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +44 -0
  23. data/lib/net/ssh/authentication/session.rb +127 -123
  24. data/lib/net/ssh/buffer.rb +305 -303
  25. data/lib/net/ssh/buffered_io.rb +163 -162
  26. data/lib/net/ssh/config.rb +230 -227
  27. data/lib/net/ssh/connection/channel.rb +659 -654
  28. data/lib/net/ssh/connection/constants.rb +30 -26
  29. data/lib/net/ssh/connection/event_loop.rb +108 -104
  30. data/lib/net/ssh/connection/keepalive.rb +54 -50
  31. data/lib/net/ssh/connection/session.rb +677 -678
  32. data/lib/net/ssh/connection/term.rb +180 -176
  33. data/lib/net/ssh/errors.rb +101 -99
  34. data/lib/net/ssh/key_factory.rb +108 -108
  35. data/lib/net/ssh/known_hosts.rb +148 -154
  36. data/lib/net/ssh/loggable.rb +56 -54
  37. data/lib/net/ssh/packet.rb +82 -78
  38. data/lib/net/ssh/prompt.rb +55 -53
  39. data/lib/net/ssh/proxy/command.rb +103 -102
  40. data/lib/net/ssh/proxy/errors.rb +12 -8
  41. data/lib/net/ssh/proxy/http.rb +92 -91
  42. data/lib/net/ssh/proxy/https.rb +42 -39
  43. data/lib/net/ssh/proxy/jump.rb +50 -47
  44. data/lib/net/ssh/proxy/socks4.rb +0 -2
  45. data/lib/net/ssh/proxy/socks5.rb +11 -11
  46. data/lib/net/ssh/ruby_compat.rb +1 -0
  47. data/lib/net/ssh/service/forward.rb +364 -362
  48. data/lib/net/ssh/test.rb +85 -83
  49. data/lib/net/ssh/test/channel.rb +146 -142
  50. data/lib/net/ssh/test/extensions.rb +148 -146
  51. data/lib/net/ssh/test/kex.rb +35 -31
  52. data/lib/net/ssh/test/local_packet.rb +48 -44
  53. data/lib/net/ssh/test/packet.rb +87 -84
  54. data/lib/net/ssh/test/remote_packet.rb +35 -31
  55. data/lib/net/ssh/test/script.rb +173 -171
  56. data/lib/net/ssh/test/socket.rb +59 -55
  57. data/lib/net/ssh/transport/algorithms.rb +413 -412
  58. data/lib/net/ssh/transport/cipher_factory.rb +108 -105
  59. data/lib/net/ssh/transport/constants.rb +35 -31
  60. data/lib/net/ssh/transport/ctr.rb +1 -1
  61. data/lib/net/ssh/transport/hmac.rb +1 -1
  62. data/lib/net/ssh/transport/hmac/abstract.rb +67 -64
  63. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +1 -1
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +1 -1
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -51
  66. data/lib/net/ssh/transport/kex.rb +2 -4
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +47 -40
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +201 -197
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -56
  70. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +94 -87
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +17 -10
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +17 -10
  73. data/lib/net/ssh/transport/key_expander.rb +29 -25
  74. data/lib/net/ssh/transport/openssl.rb +17 -30
  75. data/lib/net/ssh/transport/packet_stream.rb +193 -192
  76. data/lib/net/ssh/transport/server_version.rb +64 -66
  77. data/lib/net/ssh/transport/session.rb +286 -284
  78. data/lib/net/ssh/transport/state.rb +198 -196
  79. data/lib/net/ssh/verifiers/lenient.rb +29 -25
  80. data/lib/net/ssh/verifiers/null.rb +13 -9
  81. data/lib/net/ssh/verifiers/secure.rb +45 -45
  82. data/lib/net/ssh/verifiers/strict.rb +20 -16
  83. data/lib/net/ssh/version.rb +55 -53
  84. data/net-ssh.gemspec +4 -4
  85. data/support/ssh_tunnel_bug.rb +2 -2
  86. metadata +25 -24
  87. metadata.gz.sig +0 -0
@@ -6,162 +6,164 @@ require 'net/ssh/connection/constants'
6
6
  require 'net/ssh/transport/constants'
7
7
  require 'net/ssh/transport/packet_stream'
8
8
 
9
- module Net; module SSH; module Test
10
-
11
- # A collection of modules used to extend/override the default behavior of
12
- # Net::SSH internals for ease of testing. As a consumer of Net::SSH, you'll
13
- # never need to use this directly--they're all used under the covers by
14
- # the Net::SSH::Test system.
15
- module Extensions
16
-
17
- # An extension to Net::SSH::BufferedIo (assumes that the underlying IO
18
- # is actually a StringIO). Facilitates unit testing.
19
- module BufferedIo
20
- # Returns +true+ if the position in the stream is less than the total
21
- # length of the stream.
22
- def select_for_read?
23
- pos < size
24
- end
25
-
26
- # Set this to +true+ if you want the IO to pretend to be available for writing
27
- attr_accessor :select_for_write
28
-
29
- # Set this to +true+ if you want the IO to pretend to be in an error state
30
- attr_accessor :select_for_error
31
-
32
- alias select_for_write? select_for_write
33
- alias select_for_error? select_for_error
34
- end
35
-
36
- # An extension to Net::SSH::Transport::PacketStream (assumes that the
37
- # underlying IO is actually a StringIO). Facilitates unit testing.
38
- module PacketStream
39
- include BufferedIo # make sure we get the extensions here, too
40
-
41
- def self.included(base) #:nodoc:
42
- base.send :alias_method, :real_available_for_read?, :available_for_read?
43
- base.send :alias_method, :available_for_read?, :test_available_for_read?
44
-
45
- base.send :alias_method, :real_enqueue_packet, :enqueue_packet
46
- base.send :alias_method, :enqueue_packet, :test_enqueue_packet
47
-
48
- base.send :alias_method, :real_poll_next_packet, :poll_next_packet
49
- base.send :alias_method, :poll_next_packet, :test_poll_next_packet
50
- end
51
-
52
- # Called when another packet should be inspected from the current
53
- # script. If the next packet is a remote packet, it pops it off the
54
- # script and shoves it onto this IO object, making it available to
55
- # be read.
56
- def idle!
57
- return false unless script.next(:first)
58
-
59
- if script.next(:first).remote?
60
- self.string << script.next.to_s
61
- self.pos = pos
9
+ module Net
10
+ module SSH
11
+ module Test
12
+
13
+ # A collection of modules used to extend/override the default behavior of
14
+ # Net::SSH internals for ease of testing. As a consumer of Net::SSH, you'll
15
+ # never need to use this directly--they're all used under the covers by
16
+ # the Net::SSH::Test system.
17
+ module Extensions
18
+
19
+ # An extension to Net::SSH::BufferedIo (assumes that the underlying IO
20
+ # is actually a StringIO). Facilitates unit testing.
21
+ module BufferedIo
22
+ # Returns +true+ if the position in the stream is less than the total
23
+ # length of the stream.
24
+ def select_for_read?
25
+ pos < size
26
+ end
27
+
28
+ # Set this to +true+ if you want the IO to pretend to be available for writing
29
+ attr_accessor :select_for_write
30
+
31
+ # Set this to +true+ if you want the IO to pretend to be in an error state
32
+ attr_accessor :select_for_error
33
+
34
+ alias select_for_write? select_for_write
35
+ alias select_for_error? select_for_error
62
36
  end
63
-
64
- return true
65
- end
66
-
67
- # The testing version of Net::SSH::Transport::PacketStream#available_for_read?.
68
- # Returns true if there is data pending to be read. Otherwise calls #idle!.
69
- def test_available_for_read?
70
- return true if select_for_read?
71
- idle!
72
- false
73
- end
74
-
75
- # The testing version of Net::SSH::Transport::PacketStream#enqueued_packet.
76
- # Simply calls Net::SSH::Test::Script#process on the packet.
77
- def test_enqueue_packet(payload)
78
- packet = Net::SSH::Buffer.new(payload.to_s)
79
- script.process(packet)
80
- end
81
-
82
- # The testing version of Net::SSH::Transport::PacketStream#poll_next_packet.
83
- # Reads the next available packet from the IO object and returns it.
84
- def test_poll_next_packet
85
- return nil if available <= 0
86
- packet = Net::SSH::Buffer.new(read_available(4))
87
- length = packet.read_long
88
- Net::SSH::Packet.new(read_available(length))
89
- end
90
- end
91
-
92
- # An extension to Net::SSH::Connection::Channel. Facilitates unit testing.
93
- module Channel
94
- def self.included(base) #:nodoc:
95
- base.send :alias_method, :send_data_for_real, :send_data
96
- base.send :alias_method, :send_data, :send_data_for_test
97
- end
98
-
99
- # The testing version of Net::SSH::Connection::Channel#send_data. Calls
100
- # the original implementation, and then immediately enqueues the data for
101
- # output so that scripted sends are properly interpreted as discrete
102
- # (rather than concatenated) data packets.
103
- def send_data_for_test(data)
104
- send_data_for_real(data)
105
- enqueue_pending_output
106
- end
107
- end
108
-
109
- # An extension to the built-in ::IO class. Simply redefines IO.select
110
- # so that it can be scripted in Net::SSH unit tests.
111
- module IO
112
- def self.included(base) #:nodoc:
113
- base.extend(ClassMethods)
114
- end
115
-
116
- @extension_enabled = false
117
-
118
- def self.with_test_extension(&block)
119
- orig_value = @extension_enabled
120
- @extension_enabled = true
121
- begin
122
- yield
123
- ensure
124
- @extension_enabled = orig_value
37
+
38
+ # An extension to Net::SSH::Transport::PacketStream (assumes that the
39
+ # underlying IO is actually a StringIO). Facilitates unit testing.
40
+ module PacketStream
41
+ include BufferedIo # make sure we get the extensions here, too
42
+
43
+ def self.included(base) #:nodoc:
44
+ base.send :alias_method, :real_available_for_read?, :available_for_read?
45
+ base.send :alias_method, :available_for_read?, :test_available_for_read?
46
+
47
+ base.send :alias_method, :real_enqueue_packet, :enqueue_packet
48
+ base.send :alias_method, :enqueue_packet, :test_enqueue_packet
49
+
50
+ base.send :alias_method, :real_poll_next_packet, :poll_next_packet
51
+ base.send :alias_method, :poll_next_packet, :test_poll_next_packet
52
+ end
53
+
54
+ # Called when another packet should be inspected from the current
55
+ # script. If the next packet is a remote packet, it pops it off the
56
+ # script and shoves it onto this IO object, making it available to
57
+ # be read.
58
+ def idle!
59
+ return false unless script.next(:first)
60
+
61
+ if script.next(:first).remote?
62
+ self.string << script.next.to_s
63
+ self.pos = pos
64
+ end
65
+
66
+ return true
67
+ end
68
+
69
+ # The testing version of Net::SSH::Transport::PacketStream#available_for_read?.
70
+ # Returns true if there is data pending to be read. Otherwise calls #idle!.
71
+ def test_available_for_read?
72
+ return true if select_for_read?
73
+ idle!
74
+ false
75
+ end
76
+
77
+ # The testing version of Net::SSH::Transport::PacketStream#enqueued_packet.
78
+ # Simply calls Net::SSH::Test::Script#process on the packet.
79
+ def test_enqueue_packet(payload)
80
+ packet = Net::SSH::Buffer.new(payload.to_s)
81
+ script.process(packet)
82
+ end
83
+
84
+ # The testing version of Net::SSH::Transport::PacketStream#poll_next_packet.
85
+ # Reads the next available packet from the IO object and returns it.
86
+ def test_poll_next_packet
87
+ return nil if available <= 0
88
+ packet = Net::SSH::Buffer.new(read_available(4))
89
+ length = packet.read_long
90
+ Net::SSH::Packet.new(read_available(length))
91
+ end
125
92
  end
126
- end
127
-
128
- def self.extension_enabled?
129
- @extension_enabled
130
- end
131
-
132
- module ClassMethods
133
- def self.extended(obj) #:nodoc:
134
- class <<obj
135
- alias_method :select_for_real, :select
136
- alias_method :select, :select_for_test
93
+
94
+ # An extension to Net::SSH::Connection::Channel. Facilitates unit testing.
95
+ module Channel
96
+ def self.included(base) #:nodoc:
97
+ base.send :alias_method, :send_data_for_real, :send_data
98
+ base.send :alias_method, :send_data, :send_data_for_test
99
+ end
100
+
101
+ # The testing version of Net::SSH::Connection::Channel#send_data. Calls
102
+ # the original implementation, and then immediately enqueues the data for
103
+ # output so that scripted sends are properly interpreted as discrete
104
+ # (rather than concatenated) data packets.
105
+ def send_data_for_test(data)
106
+ send_data_for_real(data)
107
+ enqueue_pending_output
137
108
  end
138
109
  end
139
-
140
- # The testing version of ::IO.select. Assumes that all readers,
141
- # writers, and errors arrays are either nil, or contain only objects
142
- # that mix in Net::SSH::Test::Extensions::BufferedIo.
143
- def select_for_test(readers=nil, writers=nil, errors=nil, wait=nil)
144
- return select_for_real(readers, writers, errors, wait) unless Net::SSH::Test::Extensions::IO.extension_enabled?
145
- ready_readers = Array(readers).select { |r| r.select_for_read? }
146
- ready_writers = Array(writers).select { |r| r.select_for_write? }
147
- ready_errors = Array(errors).select { |r| r.select_for_error? }
148
-
149
- if ready_readers.any? || ready_writers.any? || ready_errors.any?
150
- return [ready_readers, ready_writers, ready_errors]
110
+
111
+ # An extension to the built-in ::IO class. Simply redefines IO.select
112
+ # so that it can be scripted in Net::SSH unit tests.
113
+ module IO
114
+ def self.included(base) #:nodoc:
115
+ base.extend(ClassMethods)
151
116
  end
152
-
153
- processed = 0
154
- Array(readers).each do |reader|
155
- processed += 1 if reader.idle!
117
+
118
+ @extension_enabled = false
119
+
120
+ def self.with_test_extension(&block)
121
+ orig_value = @extension_enabled
122
+ @extension_enabled = true
123
+ begin
124
+ yield
125
+ ensure
126
+ @extension_enabled = orig_value
127
+ end
128
+ end
129
+
130
+ def self.extension_enabled?
131
+ @extension_enabled
132
+ end
133
+
134
+ module ClassMethods
135
+ def self.extended(obj) #:nodoc:
136
+ class <<obj
137
+ alias_method :select_for_real, :select
138
+ alias_method :select, :select_for_test
139
+ end
140
+ end
141
+
142
+ # The testing version of ::IO.select. Assumes that all readers,
143
+ # writers, and errors arrays are either nil, or contain only objects
144
+ # that mix in Net::SSH::Test::Extensions::BufferedIo.
145
+ def select_for_test(readers=nil, writers=nil, errors=nil, wait=nil)
146
+ return select_for_real(readers, writers, errors, wait) unless Net::SSH::Test::Extensions::IO.extension_enabled?
147
+ ready_readers = Array(readers).select { |r| r.select_for_read? }
148
+ ready_writers = Array(writers).select { |r| r.select_for_write? }
149
+ ready_errors = Array(errors).select { |r| r.select_for_error? }
150
+
151
+ return [ready_readers, ready_writers, ready_errors] if ready_readers.any? || ready_writers.any? || ready_errors.any?
152
+
153
+ processed = 0
154
+ Array(readers).each do |reader|
155
+ processed += 1 if reader.idle!
156
+ end
157
+
158
+ raise "no readers were ready for reading, and none had any incoming packets" if processed == 0 && wait != 0
159
+ end
156
160
  end
157
-
158
- raise "no readers were ready for reading, and none had any incoming packets" if processed == 0 && wait != 0
159
161
  end
160
162
  end
163
+
161
164
  end
162
165
  end
163
-
164
- end; end; end
166
+ end
165
167
 
166
168
  Net::SSH::BufferedIo.send(:include, Net::SSH::Test::Extensions::BufferedIo)
167
169
  Net::SSH::Transport::PacketStream.send(:include, Net::SSH::Test::Extensions::PacketStream)
@@ -5,40 +5,44 @@ require 'net/ssh/transport/algorithms'
5
5
  require 'net/ssh/transport/constants'
6
6
  require 'net/ssh/transport/kex'
7
7
 
8
- module Net; module SSH; module Test
8
+ module Net
9
+ module SSH
10
+ module Test
11
+
12
+ # An implementation of a key-exchange strategy specifically for unit tests.
13
+ # (This strategy would never really work against a real SSH server--it makes
14
+ # too many assumptions about the server's response.)
15
+ #
16
+ # This registers itself with the transport key-exchange system as the
17
+ # "test" algorithm.
18
+ class Kex
19
+ include Net::SSH::Transport::Constants
20
+
21
+ # Creates a new instance of the testing key-exchange algorithm with the
22
+ # given arguments.
23
+ def initialize(algorithms, connection, data)
24
+ @connection = connection
25
+ end
26
+
27
+ # Exchange keys with the server. This returns a hash of constant values,
28
+ # and does not actually exchange keys.
29
+ def exchange_keys
30
+ result = Net::SSH::Buffer.from(:byte, NEWKEYS)
31
+ @connection.send_message(result)
32
+
33
+ buffer = @connection.next_message
34
+ raise Net::SSH::Exception, "expected NEWKEYS" unless buffer.type == NEWKEYS
35
+
36
+ { session_id: "abc-xyz",
37
+ server_key: OpenSSL::PKey::RSA.new(512),
38
+ shared_secret: OpenSSL::BN.new("1234567890", 10),
39
+ hashing_algorithm: OpenSSL::Digest::SHA1 }
40
+ end
41
+ end
9
42
 
10
- # An implementation of a key-exchange strategy specifically for unit tests.
11
- # (This strategy would never really work against a real SSH server--it makes
12
- # too many assumptions about the server's response.)
13
- #
14
- # This registers itself with the transport key-exchange system as the
15
- # "test" algorithm.
16
- class Kex
17
- include Net::SSH::Transport::Constants
18
-
19
- # Creates a new instance of the testing key-exchange algorithm with the
20
- # given arguments.
21
- def initialize(algorithms, connection, data)
22
- @connection = connection
23
- end
24
-
25
- # Exchange keys with the server. This returns a hash of constant values,
26
- # and does not actually exchange keys.
27
- def exchange_keys
28
- result = Net::SSH::Buffer.from(:byte, NEWKEYS)
29
- @connection.send_message(result)
30
-
31
- buffer = @connection.next_message
32
- raise Net::SSH::Exception, "expected NEWKEYS" unless buffer.type == NEWKEYS
33
-
34
- { session_id: "abc-xyz",
35
- server_key: OpenSSL::PKey::RSA.new(512),
36
- shared_secret: OpenSSL::BN.new("1234567890", 10),
37
- hashing_algorithm: OpenSSL::Digest::SHA1 }
38
43
  end
39
44
  end
40
-
41
- end; end; end
45
+ end
42
46
 
43
47
  Net::SSH::Transport::Algorithms::ALGORITHMS[:kex] << "test"
44
48
  Net::SSH::Transport::Kex::MAP["test"] = Net::SSH::Test::Kex
@@ -1,51 +1,55 @@
1
1
  require 'net/ssh/packet'
2
2
  require 'net/ssh/test/packet'
3
3
 
4
- module Net; module SSH; module Test
5
-
6
- # This is a specialization of Net::SSH::Test::Packet for representing mock
7
- # packets that are sent from the local (client) host. These are created
8
- # automatically by Net::SSH::Test::Script and Net::SSH::Test::Channel by any
9
- # of the sends_* methods.
10
- class LocalPacket < Packet
11
- attr_reader :init
12
-
13
- # Extend the default Net::SSH::Test::Packet constructor to also accept an
14
- # optional block, which is used to finalize the initialization of the
15
- # packet when #process is first called.
16
- def initialize(type, *args, &block)
17
- super(type, *args)
18
- @init = block
19
- end
20
-
21
- # Returns +true+; this is a local packet.
22
- def local?
23
- true
24
- end
25
-
26
- # Called by Net::SSH::Test::Extensions::PacketStream#test_enqueue_packet
27
- # to mimic remote processing of a locally-sent packet. It compares the
28
- # packet it was given with the contents of this LocalPacket's data, to see
29
- # if what was sent matches what was scripted. If it differs in any way,
30
- # an exception is raised.
31
- def process(packet)
32
- @init.call(Net::SSH::Packet.new(packet.to_s)) if @init
33
- type = packet.read_byte
34
- raise "expected #{@type}, but got #{type}" if @type != type
35
-
36
- @data.zip(types).each do |expected, _type|
37
- _type ||= case expected
38
- when nil then break
39
- when Numeric then :long
40
- when String then :string
41
- when TrueClass, FalseClass then :bool
4
+ module Net
5
+ module SSH
6
+ module Test
7
+
8
+ # This is a specialization of Net::SSH::Test::Packet for representing mock
9
+ # packets that are sent from the local (client) host. These are created
10
+ # automatically by Net::SSH::Test::Script and Net::SSH::Test::Channel by any
11
+ # of the sends_* methods.
12
+ class LocalPacket < Packet
13
+ attr_reader :init
14
+
15
+ # Extend the default Net::SSH::Test::Packet constructor to also accept an
16
+ # optional block, which is used to finalize the initialization of the
17
+ # packet when #process is first called.
18
+ def initialize(type, *args, &block)
19
+ super(type, *args)
20
+ @init = block
21
+ end
22
+
23
+ # Returns +true+; this is a local packet.
24
+ def local?
25
+ true
26
+ end
27
+
28
+ # Called by Net::SSH::Test::Extensions::PacketStream#test_enqueue_packet
29
+ # to mimic remote processing of a locally-sent packet. It compares the
30
+ # packet it was given with the contents of this LocalPacket's data, to see
31
+ # if what was sent matches what was scripted. If it differs in any way,
32
+ # an exception is raised.
33
+ def process(packet)
34
+ @init.call(Net::SSH::Packet.new(packet.to_s)) if @init
35
+ type = packet.read_byte
36
+ raise "expected #{@type}, but got #{type}" if @type != type
37
+
38
+ @data.zip(types).each do |expected, _type|
39
+ _type ||= case expected
40
+ when nil then break
41
+ when Numeric then :long
42
+ when String then :string
43
+ when TrueClass, FalseClass then :bool
44
+ end
45
+
46
+ actual = packet.send("read_#{_type}")
47
+ next if expected.nil?
48
+ raise "expected #{_type} #{expected.inspect} but got #{actual.inspect}" unless expected == actual
42
49
  end
43
-
44
- actual = packet.send("read_#{_type}")
45
- next if expected.nil?
46
- raise "expected #{_type} #{expected.inspect} but got #{actual.inspect}" unless expected == actual
50
+ end
47
51
  end
52
+
48
53
  end
49
54
  end
50
-
51
- end; end; end
55
+ end