python-pickle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +27 -0
  4. data/.gitignore +5 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +14 -0
  8. data/Gemfile +15 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +149 -0
  11. data/Rakefile +13 -0
  12. data/gemspec.yml +25 -0
  13. data/lib/python/pickle/byte_array.rb +40 -0
  14. data/lib/python/pickle/deserializer.rb +595 -0
  15. data/lib/python/pickle/exceptions.rb +12 -0
  16. data/lib/python/pickle/instruction.rb +52 -0
  17. data/lib/python/pickle/instructions/add_items.rb +26 -0
  18. data/lib/python/pickle/instructions/append.rb +24 -0
  19. data/lib/python/pickle/instructions/appends.rb +26 -0
  20. data/lib/python/pickle/instructions/bin_bytes.rb +32 -0
  21. data/lib/python/pickle/instructions/bin_bytes8.rb +32 -0
  22. data/lib/python/pickle/instructions/bin_float.rb +29 -0
  23. data/lib/python/pickle/instructions/bin_get.rb +27 -0
  24. data/lib/python/pickle/instructions/bin_int1.rb +29 -0
  25. data/lib/python/pickle/instructions/bin_put.rb +29 -0
  26. data/lib/python/pickle/instructions/bin_string.rb +32 -0
  27. data/lib/python/pickle/instructions/bin_unicode.rb +32 -0
  28. data/lib/python/pickle/instructions/bin_unicode8.rb +32 -0
  29. data/lib/python/pickle/instructions/build.rb +24 -0
  30. data/lib/python/pickle/instructions/byte_array8.rb +32 -0
  31. data/lib/python/pickle/instructions/dict.rb +17 -0
  32. data/lib/python/pickle/instructions/dup.rb +24 -0
  33. data/lib/python/pickle/instructions/empty_dict.rb +26 -0
  34. data/lib/python/pickle/instructions/empty_list.rb +26 -0
  35. data/lib/python/pickle/instructions/empty_set.rb +26 -0
  36. data/lib/python/pickle/instructions/empty_tuple.rb +26 -0
  37. data/lib/python/pickle/instructions/ext1.rb +29 -0
  38. data/lib/python/pickle/instructions/ext2.rb +29 -0
  39. data/lib/python/pickle/instructions/ext4.rb +29 -0
  40. data/lib/python/pickle/instructions/float.rb +24 -0
  41. data/lib/python/pickle/instructions/frame.rb +29 -0
  42. data/lib/python/pickle/instructions/frozen_set.rb +26 -0
  43. data/lib/python/pickle/instructions/get.rb +27 -0
  44. data/lib/python/pickle/instructions/global.rb +62 -0
  45. data/lib/python/pickle/instructions/has_length_and_value.rb +58 -0
  46. data/lib/python/pickle/instructions/has_value.rb +50 -0
  47. data/lib/python/pickle/instructions/int.rb +24 -0
  48. data/lib/python/pickle/instructions/list.rb +24 -0
  49. data/lib/python/pickle/instructions/long.rb +24 -0
  50. data/lib/python/pickle/instructions/long1.rb +32 -0
  51. data/lib/python/pickle/instructions/long4.rb +32 -0
  52. data/lib/python/pickle/instructions/long_bin_get.rb +27 -0
  53. data/lib/python/pickle/instructions/mark.rb +24 -0
  54. data/lib/python/pickle/instructions/memoize.rb +26 -0
  55. data/lib/python/pickle/instructions/new_false.rb +24 -0
  56. data/lib/python/pickle/instructions/new_obj.rb +26 -0
  57. data/lib/python/pickle/instructions/new_obj_ex.rb +26 -0
  58. data/lib/python/pickle/instructions/new_true.rb +24 -0
  59. data/lib/python/pickle/instructions/next_buffer.rb +26 -0
  60. data/lib/python/pickle/instructions/none.rb +24 -0
  61. data/lib/python/pickle/instructions/pop.rb +24 -0
  62. data/lib/python/pickle/instructions/pop_mark.rb +24 -0
  63. data/lib/python/pickle/instructions/proto.rb +29 -0
  64. data/lib/python/pickle/instructions/put.rb +24 -0
  65. data/lib/python/pickle/instructions/readonly_buffer.rb +26 -0
  66. data/lib/python/pickle/instructions/reduce.rb +24 -0
  67. data/lib/python/pickle/instructions/set_item.rb +24 -0
  68. data/lib/python/pickle/instructions/set_items.rb +26 -0
  69. data/lib/python/pickle/instructions/short_bin_bytes.rb +32 -0
  70. data/lib/python/pickle/instructions/short_bin_string.rb +32 -0
  71. data/lib/python/pickle/instructions/short_bin_unicode.rb +32 -0
  72. data/lib/python/pickle/instructions/stack_global.rb +26 -0
  73. data/lib/python/pickle/instructions/stop.rb +24 -0
  74. data/lib/python/pickle/instructions/string.rb +24 -0
  75. data/lib/python/pickle/instructions/tuple.rb +24 -0
  76. data/lib/python/pickle/instructions/tuple1.rb +24 -0
  77. data/lib/python/pickle/instructions/tuple2.rb +24 -0
  78. data/lib/python/pickle/instructions/tuple3.rb +24 -0
  79. data/lib/python/pickle/protocol.rb +56 -0
  80. data/lib/python/pickle/protocol0.rb +399 -0
  81. data/lib/python/pickle/protocol1.rb +183 -0
  82. data/lib/python/pickle/protocol2.rb +229 -0
  83. data/lib/python/pickle/protocol3.rb +163 -0
  84. data/lib/python/pickle/protocol4.rb +285 -0
  85. data/lib/python/pickle/protocol5.rb +218 -0
  86. data/lib/python/pickle/py_class.rb +75 -0
  87. data/lib/python/pickle/py_object.rb +141 -0
  88. data/lib/python/pickle/tuple.rb +19 -0
  89. data/lib/python/pickle/version.rb +6 -0
  90. data/lib/python/pickle.rb +226 -0
  91. data/python-pickle.gemspec +62 -0
  92. data/spec/byte_array_spec.rb +54 -0
  93. data/spec/deserializer_spec.rb +1201 -0
  94. data/spec/fixtures/ascii_str_v3.pkl +0 -0
  95. data/spec/fixtures/ascii_str_v4.pkl +0 -0
  96. data/spec/fixtures/ascii_str_v5.pkl +0 -0
  97. data/spec/fixtures/bin_str_v0.pkl +3 -0
  98. data/spec/fixtures/bin_str_v1.pkl +0 -0
  99. data/spec/fixtures/bin_str_v2.pkl +0 -0
  100. data/spec/fixtures/bin_str_v3.pkl +0 -0
  101. data/spec/fixtures/bin_str_v4.pkl +0 -0
  102. data/spec/fixtures/bin_str_v5.pkl +0 -0
  103. data/spec/fixtures/bytearray_v0.pkl +10 -0
  104. data/spec/fixtures/bytearray_v1.pkl +0 -0
  105. data/spec/fixtures/bytearray_v2.pkl +0 -0
  106. data/spec/fixtures/bytearray_v3.pkl +0 -0
  107. data/spec/fixtures/bytearray_v4.pkl +0 -0
  108. data/spec/fixtures/bytearray_v5.pkl +0 -0
  109. data/spec/fixtures/class_v0.pkl +4 -0
  110. data/spec/fixtures/class_v1.pkl +0 -0
  111. data/spec/fixtures/class_v2.pkl +0 -0
  112. data/spec/fixtures/class_v3.pkl +0 -0
  113. data/spec/fixtures/class_v4.pkl +0 -0
  114. data/spec/fixtures/class_v5.pkl +0 -0
  115. data/spec/fixtures/dict_v0.pkl +6 -0
  116. data/spec/fixtures/dict_v1.pkl +0 -0
  117. data/spec/fixtures/dict_v2.pkl +0 -0
  118. data/spec/fixtures/dict_v3.pkl +0 -0
  119. data/spec/fixtures/dict_v4.pkl +0 -0
  120. data/spec/fixtures/dict_v5.pkl +0 -0
  121. data/spec/fixtures/escaped_str_v0.pkl +3 -0
  122. data/spec/fixtures/escaped_str_v1.pkl +0 -0
  123. data/spec/fixtures/escaped_str_v2.pkl +0 -0
  124. data/spec/fixtures/false_v0.pkl +2 -0
  125. data/spec/fixtures/false_v1.pkl +2 -0
  126. data/spec/fixtures/false_v2.pkl +1 -0
  127. data/spec/fixtures/false_v3.pkl +1 -0
  128. data/spec/fixtures/false_v4.pkl +1 -0
  129. data/spec/fixtures/false_v5.pkl +1 -0
  130. data/spec/fixtures/float_v0.pkl +2 -0
  131. data/spec/fixtures/float_v1.pkl +1 -0
  132. data/spec/fixtures/float_v2.pkl +1 -0
  133. data/spec/fixtures/float_v3.pkl +1 -0
  134. data/spec/fixtures/float_v4.pkl +0 -0
  135. data/spec/fixtures/float_v5.pkl +0 -0
  136. data/spec/fixtures/function_v0.pkl +4 -0
  137. data/spec/fixtures/function_v1.pkl +0 -0
  138. data/spec/fixtures/function_v2.pkl +0 -0
  139. data/spec/fixtures/function_v3.pkl +0 -0
  140. data/spec/fixtures/function_v4.pkl +0 -0
  141. data/spec/fixtures/function_v5.pkl +0 -0
  142. data/spec/fixtures/hex_str_v0.pkl +3 -0
  143. data/spec/fixtures/hex_str_v1.pkl +0 -0
  144. data/spec/fixtures/hex_str_v2.pkl +0 -0
  145. data/spec/fixtures/int_v0.pkl +2 -0
  146. data/spec/fixtures/int_v1.pkl +1 -0
  147. data/spec/fixtures/int_v2.pkl +1 -0
  148. data/spec/fixtures/int_v3.pkl +1 -0
  149. data/spec/fixtures/int_v4.pkl +1 -0
  150. data/spec/fixtures/int_v5.pkl +1 -0
  151. data/spec/fixtures/list_v0.pkl +7 -0
  152. data/spec/fixtures/list_v1.pkl +0 -0
  153. data/spec/fixtures/list_v2.pkl +0 -0
  154. data/spec/fixtures/list_v3.pkl +0 -0
  155. data/spec/fixtures/list_v4.pkl +0 -0
  156. data/spec/fixtures/list_v5.pkl +0 -0
  157. data/spec/fixtures/long_v0.pkl +2 -0
  158. data/spec/fixtures/long_v1.pkl +2 -0
  159. data/spec/fixtures/long_v2.pkl +0 -0
  160. data/spec/fixtures/long_v3.pkl +0 -0
  161. data/spec/fixtures/long_v4.pkl +0 -0
  162. data/spec/fixtures/long_v5.pkl +0 -0
  163. data/spec/fixtures/nested_dict_v0.pkl +12 -0
  164. data/spec/fixtures/nested_dict_v1.pkl +0 -0
  165. data/spec/fixtures/nested_dict_v2.pkl +0 -0
  166. data/spec/fixtures/nested_dict_v3.pkl +0 -0
  167. data/spec/fixtures/nested_dict_v4.pkl +0 -0
  168. data/spec/fixtures/nested_dict_v5.pkl +0 -0
  169. data/spec/fixtures/nested_list_v0.pkl +9 -0
  170. data/spec/fixtures/nested_list_v1.pkl +0 -0
  171. data/spec/fixtures/nested_list_v2.pkl +0 -0
  172. data/spec/fixtures/nested_list_v3.pkl +0 -0
  173. data/spec/fixtures/nested_list_v4.pkl +0 -0
  174. data/spec/fixtures/nested_list_v5.pkl +0 -0
  175. data/spec/fixtures/none_v0.pkl +1 -0
  176. data/spec/fixtures/none_v1.pkl +1 -0
  177. data/spec/fixtures/none_v2.pkl +1 -0
  178. data/spec/fixtures/none_v3.pkl +1 -0
  179. data/spec/fixtures/none_v4.pkl +1 -0
  180. data/spec/fixtures/none_v5.pkl +1 -0
  181. data/spec/fixtures/object_v0.pkl +19 -0
  182. data/spec/fixtures/object_v1.pkl +0 -0
  183. data/spec/fixtures/object_v2.pkl +0 -0
  184. data/spec/fixtures/object_v3.pkl +0 -0
  185. data/spec/fixtures/object_v4.pkl +0 -0
  186. data/spec/fixtures/object_v5.pkl +0 -0
  187. data/spec/fixtures/str_v0.pkl +3 -0
  188. data/spec/fixtures/str_v1.pkl +0 -0
  189. data/spec/fixtures/str_v2.pkl +0 -0
  190. data/spec/fixtures/str_v3.pkl +0 -0
  191. data/spec/fixtures/str_v4.pkl +0 -0
  192. data/spec/fixtures/str_v5.pkl +0 -0
  193. data/spec/fixtures/true_v0.pkl +2 -0
  194. data/spec/fixtures/true_v1.pkl +2 -0
  195. data/spec/fixtures/true_v2.pkl +1 -0
  196. data/spec/fixtures/true_v3.pkl +1 -0
  197. data/spec/fixtures/true_v4.pkl +1 -0
  198. data/spec/fixtures/true_v5.pkl +1 -0
  199. data/spec/fixtures/unicode_str_v0.pkl +3 -0
  200. data/spec/fixtures/unicode_str_v1.pkl +0 -0
  201. data/spec/fixtures/unicode_str_v2.pkl +0 -0
  202. data/spec/fixtures/unicode_str_v3.pkl +0 -0
  203. data/spec/fixtures/unicode_str_v4.pkl +0 -0
  204. data/spec/fixtures/unicode_str_v5.pkl +0 -0
  205. data/spec/generate_pickles2.py +41 -0
  206. data/spec/generate_pickles3.py +40 -0
  207. data/spec/integration/load/protocol0_spec.rb +258 -0
  208. data/spec/integration/load/protocol1_spec.rb +258 -0
  209. data/spec/integration/load/protocol2_spec.rb +258 -0
  210. data/spec/integration/load/protocol3_spec.rb +258 -0
  211. data/spec/integration/load/protocol4_spec.rb +258 -0
  212. data/spec/integration/load/protocol5_spec.rb +258 -0
  213. data/spec/integration/parse/protocol0_spec.rb +467 -0
  214. data/spec/integration/parse/protocol1_spec.rb +459 -0
  215. data/spec/integration/parse/protocol2_spec.rb +471 -0
  216. data/spec/integration/parse/protocol3_spec.rb +407 -0
  217. data/spec/integration/parse/protocol4_spec.rb +439 -0
  218. data/spec/integration/parse/protocol5_spec.rb +419 -0
  219. data/spec/pickle_spec.rb +163 -0
  220. data/spec/protocol0_read_instruction_examples.rb +211 -0
  221. data/spec/protocol0_spec.rb +445 -0
  222. data/spec/protocol1_read_instruction_examples.rb +156 -0
  223. data/spec/protocol1_spec.rb +59 -0
  224. data/spec/protocol2_read_instruction_examples.rb +135 -0
  225. data/spec/protocol2_spec.rb +128 -0
  226. data/spec/protocol3_read_instruction_examples.rb +29 -0
  227. data/spec/protocol3_spec.rb +32 -0
  228. data/spec/protocol4_read_instruction_examples.rb +142 -0
  229. data/spec/protocol4_spec.rb +58 -0
  230. data/spec/protocol5_spec.rb +68 -0
  231. data/spec/py_class_spec.rb +62 -0
  232. data/spec/py_object_spec.rb +149 -0
  233. data/spec/spec_helper.rb +3 -0
  234. data/spec/tuple_spec.rb +18 -0
  235. metadata +325 -0
@@ -0,0 +1,211 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "Protocol0#read_instruction examples" do
4
+ context "when the opcode is 40" do
5
+ let(:io) { StringIO.new(40.chr) }
6
+
7
+ it "must return Python::Pickle::Instructions::MARK" do
8
+ expect(subject.read_instruction).to be(
9
+ Python::Pickle::Instructions::MARK
10
+ )
11
+ end
12
+ end
13
+
14
+ context "when the opcode is 46" do
15
+ let(:io) { StringIO.new(46.chr) }
16
+
17
+ it "must return Python::Pickle::Instructions::STOP" do
18
+ expect(subject.read_instruction).to be(
19
+ Python::Pickle::Instructions::STOP
20
+ )
21
+ end
22
+ end
23
+
24
+ context "when the opcode is 48" do
25
+ let(:io) { StringIO.new(48.chr) }
26
+
27
+ it "must return Python::Pickle::Instructions::POP" do
28
+ expect(subject.read_instruction).to be(
29
+ Python::Pickle::Instructions::POP
30
+ )
31
+ end
32
+ end
33
+
34
+ context "when the opcode is 49" do
35
+ let(:io) { StringIO.new(49.chr) }
36
+
37
+ it "must return Python::Pickle::Instructions::POP_MARK" do
38
+ expect(subject.read_instruction).to be(
39
+ Python::Pickle::Instructions::POP_MARK
40
+ )
41
+ end
42
+ end
43
+
44
+ context "when the opcode is 50" do
45
+ let(:io) { StringIO.new(50.chr) }
46
+
47
+ it "must return Python::Pickle::Instructions::DUP" do
48
+ expect(subject.read_instruction).to be(
49
+ Python::Pickle::Instructions::DUP
50
+ )
51
+ end
52
+ end
53
+
54
+ context "when the opcode is 50" do
55
+ let(:io) { StringIO.new(50.chr) }
56
+
57
+ it "must return Python::Pickle::Instructions::DUP" do
58
+ expect(subject.read_instruction).to be(
59
+ Python::Pickle::Instructions::DUP
60
+ )
61
+ end
62
+ end
63
+
64
+ context "when the opcode is 73" do
65
+ let(:int) { 1234 }
66
+ let(:io) { StringIO.new("#{73.chr}#{int}\n") }
67
+
68
+ it "must return a Python::Pickle::Instructions::Int object" do
69
+ expect(subject.read_instruction).to eq(
70
+ Python::Pickle::Instructions::Int.new(int)
71
+ )
72
+ end
73
+ end
74
+
75
+ context "when the opcode is 76" do
76
+ let(:long) { (2**64)-1 }
77
+ let(:io) { StringIO.new("#{76.chr}#{long}L\n") }
78
+
79
+ it "must return a Python::Pickle::Instructions::Long object" do
80
+ expect(subject.read_instruction).to eq(
81
+ Python::Pickle::Instructions::Long.new(long)
82
+ )
83
+ end
84
+ end
85
+
86
+ context "when the opcode is 78" do
87
+ let(:io) { StringIO.new(78.chr) }
88
+
89
+ it "must return Python::Pickle::Instructions::NONE" do
90
+ expect(subject.read_instruction).to be(
91
+ Python::Pickle::Instructions::NONE
92
+ )
93
+ end
94
+ end
95
+
96
+ context "when the opcode is 82" do
97
+ let(:io) { StringIO.new(82.chr) }
98
+
99
+ it "must return Python::Pickle::Instructions::REDUCE" do
100
+ expect(subject.read_instruction).to be(
101
+ Python::Pickle::Instructions::REDUCE
102
+ )
103
+ end
104
+ end
105
+
106
+ context "when the opcode is 86" do
107
+ let(:string) { 'ABC' }
108
+ let(:io) { StringIO.new("#{86.chr}#{string}\n") }
109
+
110
+ it "must return a Python::Pickle::Instructions::String object" do
111
+ expect(subject.read_instruction).to eq(
112
+ Python::Pickle::Instructions::String.new(string)
113
+ )
114
+ end
115
+ end
116
+
117
+ context "when the opcode is 97" do
118
+ let(:io) { StringIO.new(97.chr) }
119
+
120
+ it "must return Python::Pickle::Instructions::APPEND" do
121
+ expect(subject.read_instruction).to be(
122
+ Python::Pickle::Instructions::APPEND
123
+ )
124
+ end
125
+ end
126
+
127
+ context "when the opcode is 98" do
128
+ let(:io) { StringIO.new(98.chr) }
129
+
130
+ it "must return Python::Pickle::Instructions::BUILD" do
131
+ expect(subject.read_instruction).to be(
132
+ Python::Pickle::Instructions::BUILD
133
+ )
134
+ end
135
+ end
136
+
137
+ context "when the opcode is 99" do
138
+ let(:namespace) { "foo" }
139
+ let(:name) { "bar" }
140
+
141
+ let(:io) { StringIO.new("#{99.chr}#{namespace}\n#{name}\n") }
142
+
143
+ it "must return Python::Pickle::Instructions::GLOBAL" do
144
+ expect(subject.read_instruction).to eq(
145
+ Python::Pickle::Instructions::Global.new(namespace,name)
146
+ )
147
+ end
148
+ end
149
+
150
+ context "when the opcode is 100" do
151
+ let(:io) { StringIO.new(100.chr) }
152
+
153
+ it "must return Python::Pickle::Instructions::DICT" do
154
+ expect(subject.read_instruction).to be(
155
+ Python::Pickle::Instructions::DICT
156
+ )
157
+ end
158
+ end
159
+
160
+ context "when the opcode is 103" do
161
+ let(:index) { 1 }
162
+ let(:io) { StringIO.new("#{103.chr}#{index}\n") }
163
+
164
+ it "must return a Python::Pickle::Instructions::Get object" do
165
+ expect(subject.read_instruction).to eq(
166
+ Python::Pickle::Instructions::Get.new(index)
167
+ )
168
+ end
169
+ end
170
+
171
+ context "when the opcode is 108" do
172
+ let(:io) { StringIO.new(108.chr) }
173
+
174
+ it "must return Python::Pickle::Instructions::LIST" do
175
+ expect(subject.read_instruction).to be(
176
+ Python::Pickle::Instructions::LIST
177
+ )
178
+ end
179
+ end
180
+
181
+ context "when the opcode is 112" do
182
+ let(:index) { 1 }
183
+ let(:io) { StringIO.new("#{112.chr}#{index}\n") }
184
+
185
+ it "must return a Python::Pickle::Instructions::Put object" do
186
+ expect(subject.read_instruction).to eq(
187
+ Python::Pickle::Instructions::Put.new(index)
188
+ )
189
+ end
190
+ end
191
+
192
+ context "when the opcode is 115" do
193
+ let(:io) { StringIO.new(115.chr) }
194
+
195
+ it "must return Python::Pickle::Instructions::SETITEM" do
196
+ expect(subject.read_instruction).to be(
197
+ Python::Pickle::Instructions::SETITEM
198
+ )
199
+ end
200
+ end
201
+
202
+ context "when the opcode is 116" do
203
+ let(:io) { StringIO.new(116.chr) }
204
+
205
+ it "must return Python::Pickle::Instructions::TUPLE" do
206
+ expect(subject.read_instruction).to be(
207
+ Python::Pickle::Instructions::TUPLE
208
+ )
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,445 @@
1
+ require 'spec_helper'
2
+ require 'python/pickle/protocol0'
3
+
4
+ require 'protocol0_read_instruction_examples'
5
+
6
+ describe Python::Pickle::Protocol0 do
7
+ let(:pickle) { '' }
8
+ let(:io) { StringIO.new(pickle) }
9
+
10
+ subject { described_class.new(io) }
11
+
12
+ describe "#read_hex_escaped_char" do
13
+ let(:io) { StringIO.new("41") }
14
+ subject { described_class.new(io) }
15
+
16
+ it "must return the character represented by the hex byte value" do
17
+ expect(subject.read_hex_escaped_char).to eq('A')
18
+ end
19
+
20
+ context "when the hex string starts with a non-hex character" do
21
+ let(:io) { StringIO.new("G1") }
22
+
23
+ it do
24
+ expect {
25
+ subject.read_hex_escaped_char
26
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid hex escape character: \"\\xG1\"")
27
+ end
28
+ end
29
+
30
+ context "when the hex string ends with a non-hex character" do
31
+ let(:io) { StringIO.new("1G") }
32
+
33
+ it do
34
+ expect {
35
+ subject.read_hex_escaped_char
36
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid hex escape character: \"\\x1G\"")
37
+ end
38
+ end
39
+
40
+ context "when the hex string ends prematurely" do
41
+ let(:io) { StringIO.new("1") }
42
+
43
+ it do
44
+ expect {
45
+ subject.read_hex_escaped_char
46
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid hex escape character: \"\\x1\"")
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#read_escaped_char" do
52
+ context "when the next character is 'x'" do
53
+ let(:io) { StringIO.new("x41") }
54
+
55
+ it "must decode the hex escaped character" do
56
+ expect(subject.read_escaped_char).to eq('A')
57
+ end
58
+ end
59
+
60
+ context "when the next character is 't'" do
61
+ let(:io) { StringIO.new("t") }
62
+
63
+ it "must return '\\t'" do
64
+ expect(subject.read_escaped_char).to eq("\t")
65
+ end
66
+ end
67
+
68
+ context "when the next character is 'n'" do
69
+ let(:io) { StringIO.new("n") }
70
+
71
+ it "must return '\\n'" do
72
+ expect(subject.read_escaped_char).to eq("\n")
73
+ end
74
+ end
75
+
76
+ context "when the next character is 'r'" do
77
+ let(:io) { StringIO.new("r") }
78
+
79
+ it "must return '\\r'" do
80
+ expect(subject.read_escaped_char).to eq("\r")
81
+ end
82
+ end
83
+
84
+ context "when the next character is '\\'" do
85
+ let(:io) { StringIO.new("\\") }
86
+
87
+ it "must return '\\'" do
88
+ expect(subject.read_escaped_char).to eq("\\")
89
+ end
90
+ end
91
+
92
+ context "when the next character is '\\''" do
93
+ let(:io) { StringIO.new("'") }
94
+
95
+ it "must return '\\''" do
96
+ expect(subject.read_escaped_char).to eq("'")
97
+ end
98
+ end
99
+
100
+ context "when the next character is another character" do
101
+ let(:io) { StringIO.new("a") }
102
+
103
+ it do
104
+ expect {
105
+ subject.read_escaped_char
106
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid backslash escape character: \"\\a\"")
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "#read_nl_string" do
112
+ let(:string) { "foo" }
113
+ let(:io) { StringIO.new("#{string}\n") }
114
+
115
+ it "must read the characters until a '\\n' character is encountered" do
116
+ expect(subject.read_nl_string).to eq(string)
117
+ end
118
+
119
+ context "when the stream prematurely ends" do
120
+ let(:io) { StringIO.new("#{string}") }
121
+
122
+ it do
123
+ expect {
124
+ subject.read_nl_string
125
+ }.to raise_error(Python::Pickle::InvalidFormat,"unexpected end of stream after the end of a newline terminated string")
126
+ end
127
+ end
128
+ end
129
+
130
+ describe "#read_string" do
131
+ let(:string) { 'ABC' }
132
+ let(:io) { StringIO.new("'#{string}'\n") }
133
+
134
+ it "must read the characters within the single-quotes and return the string" do
135
+ expect(subject.read_string).to eq(string)
136
+ end
137
+
138
+ it "must return an ASCII 8bit encoded String" do
139
+ expect(subject.read_string.encoding).to eq(Encoding::ASCII_8BIT)
140
+ end
141
+
142
+ context "when the string contains backslash escaped characters" do
143
+ let(:string) { "ABC\\t\\n\\r\\\\\\'" }
144
+
145
+ it "must decode the backslash escaped characters" do
146
+ expect(subject.read_string).to eq("ABC\t\n\r\\'")
147
+ end
148
+ end
149
+
150
+ context "when the string contains hex escaped characters" do
151
+ let(:string) { "ABC\\x41\\x42\\x43" }
152
+
153
+ it "must decode the hex escaped characters" do
154
+ expect(subject.read_string).to eq("ABCABC")
155
+ end
156
+ end
157
+
158
+ context "when the next character is not a \"'\"" do
159
+ let(:io) { StringIO.new("#{string}'") }
160
+
161
+ it do
162
+ expect {
163
+ subject.read_string
164
+ }.to raise_error(Python::Pickle::InvalidFormat,"cannot find beginning single-quote of string")
165
+ end
166
+ end
167
+
168
+ context "when the string does not end with a single-quote" do
169
+ let(:io) { StringIO.new("'#{string}") }
170
+
171
+ it do
172
+ expect {
173
+ subject.read_string
174
+ }.to raise_error(Python::Pickle::InvalidFormat,"unexpected end of stream after the end of a single-quoted string")
175
+ end
176
+ end
177
+
178
+ context "when the character after the ending single-quote is not a '\\n' character" do
179
+ let(:io) { StringIO.new("'#{string}'XXX") }
180
+
181
+ it do
182
+ expect {
183
+ subject.read_string
184
+ }.to raise_error(Python::Pickle::InvalidFormat,"expected a '\\n' character following the string, but was \"X\"")
185
+ end
186
+ end
187
+ end
188
+
189
+ describe "#read_unicode_escaped_char16" do
190
+ let(:io) { StringIO.new("1234") }
191
+ subject { described_class.new(io) }
192
+
193
+ it "must read four hex characters and represent the unicode character represented by them" do
194
+ expect(subject.read_unicode_escaped_char16).to eq('ሴ')
195
+ end
196
+
197
+ context "when the hex string starts with a non-hex character" do
198
+ let(:io) { StringIO.new("G234") }
199
+
200
+ it do
201
+ expect {
202
+ subject.read_unicode_escaped_char16
203
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid unicode escape character: \"\\uG234\"")
204
+ end
205
+ end
206
+
207
+ context "when the hex string ends with a non-hex character" do
208
+ let(:io) { StringIO.new("123G") }
209
+
210
+ it do
211
+ expect {
212
+ subject.read_unicode_escaped_char16
213
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid unicode escape character: \"\\u123G\"")
214
+ end
215
+ end
216
+
217
+ context "when the hex string ends prematurely" do
218
+ let(:io) { StringIO.new("123") }
219
+
220
+ it do
221
+ expect {
222
+ subject.read_unicode_escaped_char16
223
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid unicode escape character: \"\\u123\"")
224
+ end
225
+ end
226
+ end
227
+
228
+ describe "#read_unicode_escaped_char32" do
229
+ let(:io) { StringIO.new("00001234") }
230
+ subject { described_class.new(io) }
231
+
232
+ it "must read eight hex characters and represent the unicode character represented by them" do
233
+ expect(subject.read_unicode_escaped_char32).to eq('ሴ')
234
+ end
235
+
236
+ context "when the hex string starts with a non-hex character" do
237
+ let(:io) { StringIO.new("G2345678") }
238
+
239
+ it do
240
+ expect {
241
+ subject.read_unicode_escaped_char32
242
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid unicode escape character: \"\\UG2345678\"")
243
+ end
244
+ end
245
+
246
+ context "when the hex string ends with a non-hex character" do
247
+ let(:io) { StringIO.new("1234567G") }
248
+
249
+ it do
250
+ expect {
251
+ subject.read_unicode_escaped_char32
252
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid unicode escape character: \"\\U1234567G\"")
253
+ end
254
+ end
255
+
256
+ context "when the hex string ends prematurely" do
257
+ let(:io) { StringIO.new("0000123") }
258
+
259
+ it do
260
+ expect {
261
+ subject.read_unicode_escaped_char32
262
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid unicode escape character: \"\\U0000123\"")
263
+ end
264
+ end
265
+ end
266
+
267
+ describe "#read_unicode_escaped_char" do
268
+ end
269
+
270
+ describe "#read_unicode" do
271
+ let(:string) { 'ABC' }
272
+ let(:io) { StringIO.new("#{string}\n") }
273
+
274
+ it "must read the characters within the single-quotes and return the string" do
275
+ expect(subject.read_unicode_string).to eq(string)
276
+ end
277
+
278
+ it "must return a UTF-8 encoded String" do
279
+ expect(subject.read_unicode_string.encoding).to eq(Encoding::UTF_8)
280
+ end
281
+
282
+ context "when the string contains unicode escaped characters" do
283
+ let(:string) { "ABC\\u0041\\u0042\\u0043" }
284
+
285
+ it "must decode the hex escaped characters" do
286
+ expect(subject.read_unicode_string).to eq("ABCABC")
287
+ end
288
+ end
289
+
290
+ context "when the string does not end with a '\\n' character" do
291
+ let(:io) { StringIO.new("#{string}") }
292
+
293
+ it do
294
+ expect {
295
+ subject.read_unicode_string
296
+ }.to raise_error(Python::Pickle::InvalidFormat,"unexpected end of stream while parsing unicode string: #{string.inspect}")
297
+ end
298
+ end
299
+ end
300
+
301
+ describe "#read_float" do
302
+ let(:float) { 1234.5678 }
303
+ let(:io) { StringIO.new("#{float}\n") }
304
+
305
+ it "must read until the newline and decode the read digits" do
306
+ expect(subject.read_float).to eq(float)
307
+ end
308
+
309
+ context "when a non-numeric character is read" do
310
+ let(:io) { StringIO.new("12.3x4\n") }
311
+
312
+ it do
313
+ expect {
314
+ subject.read_float
315
+ }.to raise_error(Python::Pickle::InvalidFormat,"encountered a non-numeric character while reading a float: \"x\"")
316
+ end
317
+ end
318
+
319
+ context "when the stream ends prematurely" do
320
+ let(:string) { '123.' }
321
+ let(:io) { StringIO.new(string) }
322
+
323
+ it do
324
+ expect {
325
+ subject.read_float
326
+ }.to raise_error(Python::Pickle::InvalidFormat,"unexpected end of stream while parsing a float: #{string.inspect}")
327
+ end
328
+ end
329
+ end
330
+
331
+ describe "#read_int" do
332
+ let(:int) { 1234 }
333
+ let(:io) { StringIO.new("#{int}\n") }
334
+
335
+ it "must read until the newline and decode the read digits" do
336
+ expect(subject.read_int).to eq(int)
337
+ end
338
+
339
+ context "when the next characters are '00\\n'" do
340
+ let(:io) { StringIO.new("00\n") }
341
+
342
+ it "must return false" do
343
+ expect(subject.read_int).to be(false)
344
+ end
345
+ end
346
+
347
+ context "when the next characters are '01\\n'" do
348
+ let(:io) { StringIO.new("01\n") }
349
+
350
+ it "must return true" do
351
+ expect(subject.read_int).to be(true)
352
+ end
353
+ end
354
+
355
+ context "when a non-numeric character is read" do
356
+ let(:io) { StringIO.new("100x00\n") }
357
+
358
+ it do
359
+ expect {
360
+ subject.read_int
361
+ }.to raise_error(Python::Pickle::InvalidFormat,"encountered a non-numeric character while reading an integer: \"x\"")
362
+ end
363
+ end
364
+
365
+ context "when the stream ends prematurely" do
366
+ let(:string) { '1234' }
367
+ let(:io) { StringIO.new(string) }
368
+
369
+ it do
370
+ expect {
371
+ subject.read_int
372
+ }.to raise_error(Python::Pickle::InvalidFormat,"unexpected end of stream while parsing an integer: #{string.inspect}")
373
+ end
374
+ end
375
+ end
376
+
377
+ describe "#read_long" do
378
+ let(:long) { (2**64) - 1 }
379
+ let(:io) { StringIO.new("#{long}L\n") }
380
+
381
+ it "must read until a 'L' character and decode the read digits" do
382
+ expect(subject.read_long).to eq(long)
383
+ end
384
+
385
+ context "when a non-numeric character is read" do
386
+ let(:io) { StringIO.new("1234x678L\n") }
387
+
388
+ it do
389
+ expect {
390
+ subject.read_long
391
+ }.to raise_error(Python::Pickle::InvalidFormat,"encountered a non-numeric character while reading a long integer: \"x\"")
392
+ end
393
+ end
394
+
395
+ context "when the stream ends prematurely" do
396
+ let(:string) { '1234' }
397
+ let(:io) { StringIO.new(string) }
398
+
399
+ it do
400
+ expect {
401
+ subject.read_long
402
+ }.to raise_error(Python::Pickle::InvalidFormat,"unexpected end of stream while parsing a long integer: #{string.inspect}")
403
+ end
404
+ end
405
+
406
+ context "when the stream ends just after the terminating 'L'" do
407
+ let(:string) { '1234L' }
408
+ let(:io) { StringIO.new(string) }
409
+
410
+ it do
411
+ expect {
412
+ subject.read_long
413
+ }.to raise_error(Python::Pickle::InvalidFormat,"unexpected end of stream after the end of an integer")
414
+ end
415
+ end
416
+
417
+ context "when there is no newline character after the terminating 'L'" do
418
+ let(:string) { "1234Lx" }
419
+ let(:io) { StringIO.new(string) }
420
+
421
+ it do
422
+ expect {
423
+ subject.read_long
424
+ }.to raise_error(Python::Pickle::InvalidFormat,"expected a '\\n' character following the integer, but was \"x\"")
425
+ end
426
+ end
427
+ end
428
+
429
+ let(:fixtures_dir) { File.join(__dir__,'fixtures') }
430
+
431
+ describe "#read_instruction" do
432
+ include_context "Protocol0#read_instruction examples"
433
+
434
+ context "when the opcode isn't reocgnized" do
435
+ let(:opcode) { 255 }
436
+ let(:io) { StringIO.new(opcode.chr) }
437
+
438
+ it do
439
+ expect {
440
+ subject.read_instruction
441
+ }.to raise_error(Python::Pickle::InvalidFormat,"invalid opcode (#{opcode.inspect}) for protocol 0")
442
+ end
443
+ end
444
+ end
445
+ end