python-pickle 0.1.0

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.
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