diff options
author | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-09-20 20:18:52 +0000 |
---|---|---|
committer | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-09-20 20:18:52 +0000 |
commit | 1d15d5f08032acf1b7bceacbb450d617ff6e0931 (patch) | |
tree | a3785a79899302bc149e4a6e72f624ac27dc1f10 /spec/ruby/core/string/unpack | |
parent | 75bfc6440d595bf339007f4fb280fd4d743e89c1 (diff) |
Move spec/rubyspec to spec/ruby for consistency
* Other ruby implementations use the spec/ruby directory.
[Misc #13792] [ruby-core:82287]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59979 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'spec/ruby/core/string/unpack')
29 files changed, 2674 insertions, 0 deletions
diff --git a/spec/ruby/core/string/unpack/a_spec.rb b/spec/ruby/core/string/unpack/a_spec.rb new file mode 100644 index 0000000000..18882c91a6 --- /dev/null +++ b/spec/ruby/core/string/unpack/a_spec.rb @@ -0,0 +1,63 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/string', __FILE__) + +describe "String#unpack with format 'A'" do + it_behaves_like :string_unpack_basic, 'A' + it_behaves_like :string_unpack_no_platform, 'A' + it_behaves_like :string_unpack_string, 'A' + it_behaves_like :string_unpack_Aa, 'A' + + it "removes trailing space and NULL bytes from the decoded string" do + [ ["a\x00 b \x00", ["a\x00 b", ""]], + ["a\x00 b \x00 ", ["a\x00 b", ""]], + ["a\x00 b\x00 ", ["a\x00 b", ""]], + ["a\x00 b\x00", ["a\x00 b", ""]], + ["a\x00 b ", ["a\x00 b", ""]] + ].should be_computed_by(:unpack, "A*A") + end + + it "does not remove whitespace other than space" do + [ ["a\x00 b\x00\f", ["a\x00 b\x00\f"]], + ["a\x00 b\x00\n", ["a\x00 b\x00\n"]], + ["a\x00 b\x00\r", ["a\x00 b\x00\r"]], + ["a\x00 b\x00\t", ["a\x00 b\x00\t"]], + ["a\x00 b\x00\v", ["a\x00 b\x00\v"]], + ].should be_computed_by(:unpack, "A*") + end + + it "decodes into raw (ascii) string values" do + str = "str".force_encoding('UTF-8').unpack("A*")[0] + str.encoding.name.should == 'ASCII-8BIT' + end + +end + +describe "String#unpack with format 'a'" do + it_behaves_like :string_unpack_basic, 'a' + it_behaves_like :string_unpack_no_platform, 'a' + it_behaves_like :string_unpack_string, 'a' + it_behaves_like :string_unpack_Aa, 'a' + + it "does not remove trailing whitespace or NULL bytes from the decoded string" do + [ ["a\x00 b \x00", ["a\x00 b \x00"]], + ["a\x00 b \x00 ", ["a\x00 b \x00 "]], + ["a\x00 b\x00 ", ["a\x00 b\x00 "]], + ["a\x00 b\x00", ["a\x00 b\x00"]], + ["a\x00 b ", ["a\x00 b "]], + ["a\x00 b\f", ["a\x00 b\f"]], + ["a\x00 b\n", ["a\x00 b\n"]], + ["a\x00 b\r", ["a\x00 b\r"]], + ["a\x00 b\t", ["a\x00 b\t"]], + ["a\x00 b\v", ["a\x00 b\v"]] + ].should be_computed_by(:unpack, "a*") + end + + it "decodes into raw (ascii) string values" do + str = "".unpack("a*")[0] + str.encoding.name.should == 'ASCII-8BIT' + end + +end diff --git a/spec/ruby/core/string/unpack/at_spec.rb b/spec/ruby/core/string/unpack/at_spec.rb new file mode 100644 index 0000000000..70cbebd2ba --- /dev/null +++ b/spec/ruby/core/string/unpack/at_spec.rb @@ -0,0 +1,29 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format '@'" do + it_behaves_like :string_unpack_basic, '@' + it_behaves_like :string_unpack_no_platform, '@' + + it "moves the read index to the byte specified by the count" do + "\x01\x02\x03\x04".unpack("C3@2C").should == [1, 2, 3, 3] + end + + it "implicitly has a count of zero when count is not specified" do + "\x01\x02\x03\x04".unpack("C2@C").should == [1, 2, 1] + end + + it "has no effect when passed the '*' modifier" do + "\x01\x02\x03\x04".unpack("C2@*C").should == [1, 2, 3] + end + + it "positions the read index one beyond the last readable byte in the String" do + "\x01\x02\x03\x04".unpack("C2@4C").should == [1, 2, nil] + end + + it "raises an ArgumentError if the count exceeds the size of the String" do + lambda { "\x01\x02\x03\x04".unpack("C2@5C") }.should raise_error(ArgumentError) + end +end diff --git a/spec/ruby/core/string/unpack/b_spec.rb b/spec/ruby/core/string/unpack/b_spec.rb new file mode 100644 index 0000000000..fa632e6526 --- /dev/null +++ b/spec/ruby/core/string/unpack/b_spec.rb @@ -0,0 +1,190 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'B'" do + it_behaves_like :string_unpack_basic, 'B' + it_behaves_like :string_unpack_no_platform, 'B' + + it "decodes one bit from each byte for each format character starting with the most significant bit" do + [ ["\x00", "B", ["0"]], + ["\x80", "B", ["1"]], + ["\x0f", "B", ["0"]], + ["\x8f", "B", ["1"]], + ["\x7f", "B", ["0"]], + ["\xff", "B", ["1"]], + ["\x80\x00", "BB", ["1", "0"]], + ["\x8f\x00", "BB", ["1", "0"]], + ["\x80\x0f", "BB", ["1", "0"]], + ["\x80\x8f", "BB", ["1", "1"]], + ["\x80\x80", "BB", ["1", "1"]], + ["\x0f\x80", "BB", ["0", "1"]] + ].should be_computed_by(:unpack) + end + + it "decodes only the number of bits in the string when passed a count" do + "\x83".unpack("B25").should == ["10000011"] + end + + it "decodes multiple differing bit counts from a single string" do + str = "\xaa\xaa\xaa\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7\xc3\xd4\xaa\x6b\xd7\xaa" + array = str.unpack("B5B6B7B8B9B10B13B14B16B17") + array.should == ["10101", "101010", "1010101", "10101010", "010101011", + "1101010011", "0110101111010", "10101010110101", + "1100001111010100", "10101010011010111"] + end + + it "decodes a directive with a '*' modifier after a directive with a count modifier" do + "\xd4\xc3\x6b\xd7".unpack("B5B*").should == ["11010", "110000110110101111010111"] + end + + it "decodes a directive with a count modifier after a directive with a '*' modifier" do + "\xd4\xc3\x6b\xd7".unpack("B*B5").should == ["11010100110000110110101111010111", ""] + end + + it "decodes the number of bits specified by the count modifier" do + [ ["\x00", "B0", [""]], + ["\x80", "B1", ["1"]], + ["\x7f", "B2", ["01"]], + ["\x8f", "B3", ["100"]], + ["\x7f", "B4", ["0111"]], + ["\xff", "B5", ["11111"]], + ["\xf8", "B6", ["111110"]], + ["\x9c", "B7", ["1001110"]], + ["\xbd", "B8", ["10111101"]], + ["\x80\x80", "B9", ["100000001"]], + ["\x80\x70", "B10", ["1000000001"]], + ["\x80\x20", "B11", ["10000000001"]], + ["\x8f\x10", "B12", ["100011110001"]], + ["\x8f\x0f", "B13", ["1000111100001"]], + ["\x80\x0f", "B14", ["10000000000011"]], + ["\x80\x8f", "B15", ["100000001000111"]], + ["\x0f\x81", "B16", ["0000111110000001"]] + ].should be_computed_by(:unpack) + end + + it "decodes all the bits when passed the '*' modifier" do + [ ["", [""]], + ["\x00", ["00000000"]], + ["\x80", ["10000000"]], + ["\x7f", ["01111111"]], + ["\x81", ["10000001"]], + ["\x0f", ["00001111"]], + ["\x80\x80", ["1000000010000000"]], + ["\x8f\x10", ["1000111100010000"]], + ["\x00\x10", ["0000000000010000"]] + ].should be_computed_by(:unpack, "B*") + end + + it "adds an empty string for each element requested beyond the end of the String" do + [ ["", ["", "", ""]], + ["\x80", ["1", "", ""]], + ["\x80\x08", ["1", "0", ""]] + ].should be_computed_by(:unpack, "BBB") + end + + it "ignores NULL bytes between directives" do + "\x80\x00".unpack("B\x00B").should == ["1", "0"] + end + + it "ignores spaces between directives" do + "\x80\x00".unpack("B B").should == ["1", "0"] + end +end + +describe "String#unpack with format 'b'" do + it_behaves_like :string_unpack_basic, 'b' + it_behaves_like :string_unpack_no_platform, 'b' + + it "decodes one bit from each byte for each format character starting with the least significant bit" do + [ ["\x00", "b", ["0"]], + ["\x01", "b", ["1"]], + ["\xf0", "b", ["0"]], + ["\xf1", "b", ["1"]], + ["\xfe", "b", ["0"]], + ["\xff", "b", ["1"]], + ["\x01\x00", "bb", ["1", "0"]], + ["\xf1\x00", "bb", ["1", "0"]], + ["\x01\xf0", "bb", ["1", "0"]], + ["\x01\xf1", "bb", ["1", "1"]], + ["\x01\x01", "bb", ["1", "1"]], + ["\xf0\x01", "bb", ["0", "1"]] + ].should be_computed_by(:unpack) + end + + it "decodes only the number of bits in the string when passed a count" do + "\x83".unpack("b25").should == ["11000001"] + end + + it "decodes multiple differing bit counts from a single string" do + str = "\xaa\xaa\xaa\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7\xc3\xd4\xaa\x6b\xd7\xaa" + array = str.unpack("b5b6b7b8b9b10b13b14b16b17") + array.should == ["01010", "010101", "0101010", "01010101", "101010100", + "0010101111", "1101011011101", "01010101111010", + "1100001100101011", "01010101110101101"] + end + + it "decodes a directive with a '*' modifier after a directive with a count modifier" do + "\xd4\xc3\x6b\xd7".unpack("b5b*").should == ["00101", "110000111101011011101011"] + end + + it "decodes a directive with a count modifier after a directive with a '*' modifier" do + "\xd4\xc3\x6b\xd7".unpack("b*b5").should == ["00101011110000111101011011101011", ""] + end + + it "decodes the number of bits specified by the count modifier" do + [ ["\x00", "b0", [""]], + ["\x01", "b1", ["1"]], + ["\xfe", "b2", ["01"]], + ["\xfc", "b3", ["001"]], + ["\xf7", "b4", ["1110"]], + ["\xff", "b5", ["11111"]], + ["\xfe", "b6", ["011111"]], + ["\xce", "b7", ["0111001"]], + ["\xbd", "b8", ["10111101"]], + ["\x01\xff", "b9", ["100000001"]], + ["\x01\xfe", "b10", ["1000000001"]], + ["\x01\xfc", "b11", ["10000000001"]], + ["\xf1\xf8", "b12", ["100011110001"]], + ["\xe1\xf1", "b13", ["1000011110001"]], + ["\x03\xe0", "b14", ["11000000000001"]], + ["\x47\xc0", "b15", ["111000100000001"]], + ["\x81\x0f", "b16", ["1000000111110000"]] + ].should be_computed_by(:unpack) + end + + it "decodes all the bits when passed the '*' modifier" do + [ ["", [""]], + ["\x00", ["00000000"]], + ["\x80", ["00000001"]], + ["\x7f", ["11111110"]], + ["\x81", ["10000001"]], + ["\x0f", ["11110000"]], + ["\x80\x80", ["0000000100000001"]], + ["\x8f\x10", ["1111000100001000"]], + ["\x00\x10", ["0000000000001000"]] + ].should be_computed_by(:unpack, "b*") + end + + it "adds an empty string for each element requested beyond the end of the String" do + [ ["", ["", "", ""]], + ["\x01", ["1", "", ""]], + ["\x01\x80", ["1", "0", ""]] + ].should be_computed_by(:unpack, "bbb") + end + + it "ignores NULL bytes between directives" do + "\x01\x00".unpack("b\x00b").should == ["1", "0"] + end + + it "ignores spaces between directives" do + "\x01\x00".unpack("b b").should == ["1", "0"] + end + + it "decodes into US-ASCII string values" do + str = "s".force_encoding('UTF-8').unpack("b*")[0] + str.encoding.name.should == 'US-ASCII' + end + +end diff --git a/spec/ruby/core/string/unpack/c_spec.rb b/spec/ruby/core/string/unpack/c_spec.rb new file mode 100644 index 0000000000..36de462cac --- /dev/null +++ b/spec/ruby/core/string/unpack/c_spec.rb @@ -0,0 +1,63 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe :string_unpack_8bit, shared: true do + it "decodes one byte for a single format character" do + "abc".unpack(unpack_format()).should == [97] + end + + it "decodes two bytes for two format characters" do + "abc".unpack(unpack_format(nil, 2)).should == [97, 98] + end + + it "decodes the number of bytes requested by the count modifier" do + "abc".unpack(unpack_format(2)).should == [97, 98] + end + + it "decodes the remaining bytes when passed the '*' modifier" do + "abc".unpack(unpack_format('*')).should == [97, 98, 99] + end + + it "decodes the remaining bytes when passed the '*' modifer after another directive" do + "abc".unpack(unpack_format()+unpack_format('*')).should == [97, 98, 99] + end + + it "decodes zero bytes when no bytes remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*', 2)).should == [97, 98, 99] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["a", [97, nil, nil]], + ["ab", [97, 98, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "abc".unpack(unpack_format("\000", 2)).should == [97, 98] + end + + it "ignores spaces between directives" do + "abc".unpack(unpack_format(' ', 2)).should == [97, 98] + end +end + +describe "String#unpack with format 'C'" do + it_behaves_like :string_unpack_basic, 'C' + it_behaves_like :string_unpack_8bit, 'C' + + it "decodes a byte with most significant bit set as a positive number" do + "\xff\x80\x82".unpack('C*').should == [255, 128, 130] + end +end + +describe "String#unpack with format 'c'" do + it_behaves_like :string_unpack_basic, 'c' + it_behaves_like :string_unpack_8bit, 'c' + + it "decodes a byte with most significant bit set as a negative number" do + "\xff\x80\x82".unpack('c*').should == [-1, -128, -126] + end +end diff --git a/spec/ruby/core/string/unpack/comment_spec.rb b/spec/ruby/core/string/unpack/comment_spec.rb new file mode 100644 index 0000000000..884960b337 --- /dev/null +++ b/spec/ruby/core/string/unpack/comment_spec.rb @@ -0,0 +1,25 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "String#unpack" do + it "ignores directives text from '#' to the first newline" do + "\x01\x02\x03".unpack("c#this is a comment\nc").should == [1, 2] + end + + it "ignores directives text from '#' to the end if no newline is present" do + "\x01\x02\x03".unpack("c#this is a comment c").should == [1] + end + + it "ignores comments at the start of the directives string" do + "\x01\x02\x03".unpack("#this is a comment\nc").should == [1] + end + + it "ignores the entire directive string if it is a comment" do + "\x01\x02\x03".unpack("#this is a comment c").should == [] + end + + it "ignores multiple comments" do + "\x01\x02\x03".unpack("c#comment\nc#comment\nc#c").should == [1, 2, 3] + end +end diff --git a/spec/ruby/core/string/unpack/d_spec.rb b/spec/ruby/core/string/unpack/d_spec.rb new file mode 100644 index 0000000000..db4638f8ef --- /dev/null +++ b/spec/ruby/core/string/unpack/d_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +little_endian do + describe "String#unpack with format 'D'" do + it_behaves_like :string_unpack_basic, 'D' + it_behaves_like :string_unpack_double_le, 'D' + end + + describe "String#unpack with format 'd'" do + it_behaves_like :string_unpack_basic, 'd' + it_behaves_like :string_unpack_double_le, 'd' + end +end + +big_endian do + describe "String#unpack with format 'D'" do + it_behaves_like :string_unpack_basic, 'D' + it_behaves_like :string_unpack_double_be, 'D' + end + + describe "String#unpack with format 'd'" do + it_behaves_like :string_unpack_basic, 'd' + it_behaves_like :string_unpack_double_be, 'd' + end +end diff --git a/spec/ruby/core/string/unpack/e_spec.rb b/spec/ruby/core/string/unpack/e_spec.rb new file mode 100644 index 0000000000..cb74c00206 --- /dev/null +++ b/spec/ruby/core/string/unpack/e_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +describe "String#unpack with format 'E'" do + it_behaves_like :string_unpack_basic, 'E' + it_behaves_like :string_unpack_double_le, 'E' +end + +describe "String#unpack with format 'e'" do + it_behaves_like :string_unpack_basic, 'e' + it_behaves_like :string_unpack_float_le, 'e' +end diff --git a/spec/ruby/core/string/unpack/f_spec.rb b/spec/ruby/core/string/unpack/f_spec.rb new file mode 100644 index 0000000000..60dad46703 --- /dev/null +++ b/spec/ruby/core/string/unpack/f_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +little_endian do + describe "String#unpack with format 'F'" do + it_behaves_like :string_unpack_basic, 'F' + it_behaves_like :string_unpack_float_le, 'F' + end + + describe "String#unpack with format 'f'" do + it_behaves_like :string_unpack_basic, 'f' + it_behaves_like :string_unpack_float_le, 'f' + end +end + +big_endian do + describe "String#unpack with format 'F'" do + it_behaves_like :string_unpack_basic, 'F' + it_behaves_like :string_unpack_float_be, 'F' + end + + describe "String#unpack with format 'f'" do + it_behaves_like :string_unpack_basic, 'f' + it_behaves_like :string_unpack_float_be, 'f' + end +end diff --git a/spec/ruby/core/string/unpack/g_spec.rb b/spec/ruby/core/string/unpack/g_spec.rb new file mode 100644 index 0000000000..f5bec1534e --- /dev/null +++ b/spec/ruby/core/string/unpack/g_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +describe "String#unpack with format 'G'" do + it_behaves_like :string_unpack_basic, 'G' + it_behaves_like :string_unpack_double_be, 'G' +end + +describe "String#unpack with format 'g'" do + it_behaves_like :string_unpack_basic, 'g' + it_behaves_like :string_unpack_float_be, 'g' +end diff --git a/spec/ruby/core/string/unpack/h_spec.rb b/spec/ruby/core/string/unpack/h_spec.rb new file mode 100644 index 0000000000..00d6d68eee --- /dev/null +++ b/spec/ruby/core/string/unpack/h_spec.rb @@ -0,0 +1,124 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'H'" do + it_behaves_like :string_unpack_basic, 'H' + it_behaves_like :string_unpack_no_platform, 'H' + + it "decodes one nibble from each byte for each format character starting with the most significant bit" do + [ ["\x8f", "H", ["8"]], + ["\xf8\x0f", "HH", ["f", "0"]] + ].should be_computed_by(:unpack) + end + + it "decodes only the number of nibbles in the string when passed a count" do + "\xca\xfe".unpack("H5").should == ["cafe"] + end + + it "decodes multiple differing nibble counts from a single string" do + array = "\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7".unpack("HH2H3H4H5") + array.should == ["a", "55", "aad", "c36b", "d7aad"] + end + + it "decodes a directive with a '*' modifier after a directive with a count modifier" do + "\xaa\x55\xaa\xd4\xc3\x6b".unpack("H3H*").should == ["aa5", "aad4c36b"] + end + + it "decodes a directive with a count modifier after a directive with a '*' modifier" do + "\xaa\x55\xaa\xd4\xc3\x6b".unpack("H*H3").should == ["aa55aad4c36b", ""] + end + + it "decodes the number of nibbles specified by the count modifier" do + [ ["\xab", "H0", [""]], + ["\x00", "H1", ["0"]], + ["\x01", "H2", ["01"]], + ["\x01\x23", "H3", ["012"]], + ["\x01\x23", "H4", ["0123"]], + ["\x01\x23\x45", "H5", ["01234"]] + ].should be_computed_by(:unpack) + end + + it "decodes all the nibbles when passed the '*' modifier" do + [ ["", [""]], + ["\xab", ["ab"]], + ["\xca\xfe", ["cafe"]], + ].should be_computed_by(:unpack, "H*") + end + + it "adds an empty string for each element requested beyond the end of the String" do + [ ["", ["", "", ""]], + ["\x01", ["0", "", ""]], + ["\x01\x80", ["0", "8", ""]] + ].should be_computed_by(:unpack, "HHH") + end + + it "ignores NULL bytes between directives" do + "\x01\x10".unpack("H\x00H").should == ["0", "1"] + end + + it "ignores spaces between directives" do + "\x01\x10".unpack("H H").should == ["0", "1"] + end +end + +describe "String#unpack with format 'h'" do + it_behaves_like :string_unpack_basic, 'h' + it_behaves_like :string_unpack_no_platform, 'h' + + it "decodes one nibble from each byte for each format character starting with the least significant bit" do + [ ["\x8f", "h", ["f"]], + ["\xf8\x0f", "hh", ["8", "f"]] + ].should be_computed_by(:unpack) + end + + it "decodes only the number of nibbles in the string when passed a count" do + "\xac\xef".unpack("h5").should == ["cafe"] + end + + it "decodes multiple differing nibble counts from a single string" do + array = "\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7".unpack("hh2h3h4h5") + array.should == ["a", "55", "aa4", "3cb6", "7daa7"] + end + + it "decodes a directive with a '*' modifier after a directive with a count modifier" do + "\xba\x55\xaa\xd4\xc3\x6b".unpack("h3h*").should == ["ab5", "aa4d3cb6"] + end + + it "decodes a directive with a count modifier after a directive with a '*' modifier" do + "\xba\x55\xaa\xd4\xc3\x6b".unpack("h*h3").should == ["ab55aa4d3cb6", ""] + end + + it "decodes the number of nibbles specified by the count modifier" do + [ ["\xab", "h0", [""]], + ["\x00", "h1", ["0"]], + ["\x01", "h2", ["10"]], + ["\x01\x23", "h3", ["103"]], + ["\x01\x23", "h4", ["1032"]], + ["\x01\x23\x45", "h5", ["10325"]] + ].should be_computed_by(:unpack) + end + + it "decodes all the nibbles when passed the '*' modifier" do + [ ["", [""]], + ["\xab", ["ba"]], + ["\xac\xef", ["cafe"]], + ].should be_computed_by(:unpack, "h*") + end + + it "adds an empty string for each element requested beyond the end of the String" do + [ ["", ["", "", ""]], + ["\x01", ["1", "", ""]], + ["\x01\x80", ["1", "0", ""]] + ].should be_computed_by(:unpack, "hhh") + end + + it "ignores NULL bytes between directives" do + "\x01\x10".unpack("h\x00h").should == ["1", "0"] + end + + it "ignores spaces between directives" do + "\x01\x10".unpack("h h").should == ["1", "0"] + end +end diff --git a/spec/ruby/core/string/unpack/i_spec.rb b/spec/ruby/core/string/unpack/i_spec.rb new file mode 100644 index 0000000000..f3183afe99 --- /dev/null +++ b/spec/ruby/core/string/unpack/i_spec.rb @@ -0,0 +1,152 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'I'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'I<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'I<_' + it_behaves_like :string_unpack_32bit_le, 'I_<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I<_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'I<!' + it_behaves_like :string_unpack_32bit_le, 'I!<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I<!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I!<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'I>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'I>_' + it_behaves_like :string_unpack_32bit_be, 'I_>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I>_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'I>!' + it_behaves_like :string_unpack_32bit_be, 'I!>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I>!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I!>' + end +end + +describe "String#unpack with format 'i'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'i<' + it_behaves_like :string_unpack_32bit_le_signed, 'i<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'i<_' + it_behaves_like :string_unpack_32bit_le, 'i_<' + it_behaves_like :string_unpack_32bit_le_signed, 'i<_' + it_behaves_like :string_unpack_32bit_le_signed, 'i_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'i<!' + it_behaves_like :string_unpack_32bit_le, 'i!<' + it_behaves_like :string_unpack_32bit_le_signed, 'i<!' + it_behaves_like :string_unpack_32bit_le_signed, 'i!<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'i>' + it_behaves_like :string_unpack_32bit_be_signed, 'i>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'i>_' + it_behaves_like :string_unpack_32bit_be, 'i_>' + it_behaves_like :string_unpack_32bit_be_signed, 'i>_' + it_behaves_like :string_unpack_32bit_be_signed, 'i_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'i>!' + it_behaves_like :string_unpack_32bit_be, 'i!>' + it_behaves_like :string_unpack_32bit_be_signed, 'i>!' + it_behaves_like :string_unpack_32bit_be_signed, 'i!>' + end +end + +little_endian do + describe "String#unpack with format 'I'" do + it_behaves_like :string_unpack_basic, 'I' + it_behaves_like :string_unpack_32bit_le, 'I' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I' + end + + describe "String#unpack with format 'I' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'I_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I_' + end + + describe "String#unpack with format 'I' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'I!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I!' + end + + describe "String#unpack with format 'i'" do + it_behaves_like :string_unpack_basic, 'i' + it_behaves_like :string_unpack_32bit_le, 'i' + it_behaves_like :string_unpack_32bit_le_signed, 'i' + end + + describe "String#unpack with format 'i' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'i_' + it_behaves_like :string_unpack_32bit_le_signed, 'i_' + end + + describe "String#unpack with format 'i' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'i!' + it_behaves_like :string_unpack_32bit_le_signed, 'i!' + end +end + +big_endian do + describe "String#unpack with format 'I'" do + it_behaves_like :string_unpack_basic, 'I' + it_behaves_like :string_unpack_32bit_be, 'I' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I' + end + + describe "String#unpack with format 'I' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'I_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I_' + end + + describe "String#unpack with format 'I' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'I!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I!' + end + + describe "String#unpack with format 'i'" do + it_behaves_like :string_unpack_basic, 'i' + it_behaves_like :string_unpack_32bit_be, 'i' + it_behaves_like :string_unpack_32bit_be_signed, 'i' + end + + describe "String#unpack with format 'i' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'i_' + it_behaves_like :string_unpack_32bit_be_signed, 'i_' + end + + describe "String#unpack with format 'i' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'i!' + it_behaves_like :string_unpack_32bit_be_signed, 'i!' + end +end diff --git a/spec/ruby/core/string/unpack/j_spec.rb b/spec/ruby/core/string/unpack/j_spec.rb new file mode 100644 index 0000000000..49c460aeb3 --- /dev/null +++ b/spec/ruby/core/string/unpack/j_spec.rb @@ -0,0 +1,277 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +ruby_version_is '2.3' do + # To handle the special case of x64-mingw32 + pointer_size = RUBY_PLATFORM =~ /\bx64\b/ ? 64 : 1.size * 8 + + if pointer_size == 64 then + little_endian do + describe "String#unpack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_64bit_le, 'J_' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_64bit_le, 'J!' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J!' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_64bit_le, 'j_' + it_behaves_like :string_unpack_64bit_le_signed, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_64bit_le, 'j!' + it_behaves_like :string_unpack_64bit_le_signed, 'j!' + end + end + end + + big_endian do + describe "String#unpack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_64bit_be, 'J_' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_64bit_be, 'J!' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J!' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_64bit_be, 'j_' + it_behaves_like :string_unpack_64bit_be_signed, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_64bit_be, 'j!' + it_behaves_like :string_unpack_64bit_be_signed, 'j!' + end + end + end + + describe "String#unpack with format 'J'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_64bit_le, 'J<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_64bit_be, 'J>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J>' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_64bit_le, 'J<_' + it_behaves_like :string_unpack_64bit_le, 'J_<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J<_' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_64bit_le, 'J<!' + it_behaves_like :string_unpack_64bit_le, 'J!<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J<!' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J!<' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_64bit_be, 'J>_' + it_behaves_like :string_unpack_64bit_be, 'J_>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J>_' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_64bit_be, 'J>!' + it_behaves_like :string_unpack_64bit_be, 'J!>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J>!' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J!>' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_64bit_le, 'j<' + it_behaves_like :string_unpack_64bit_le_signed, 'j<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_64bit_be, 'j>' + it_behaves_like :string_unpack_64bit_be_signed, 'j>' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_64bit_le, 'j<_' + it_behaves_like :string_unpack_64bit_le, 'j_<' + it_behaves_like :string_unpack_64bit_le_signed, 'j<_' + it_behaves_like :string_unpack_64bit_le_signed, 'j_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_64bit_le, 'j<!' + it_behaves_like :string_unpack_64bit_le, 'j!<' + it_behaves_like :string_unpack_64bit_le_signed, 'j<!' + it_behaves_like :string_unpack_64bit_le_signed, 'j!<' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_64bit_be, 'j>_' + it_behaves_like :string_unpack_64bit_be, 'j_>' + it_behaves_like :string_unpack_64bit_be_signed, 'j>_' + it_behaves_like :string_unpack_64bit_be_signed, 'j_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_64bit_be, 'j>!' + it_behaves_like :string_unpack_64bit_be, 'j!>' + it_behaves_like :string_unpack_64bit_be_signed, 'j>!' + it_behaves_like :string_unpack_64bit_be_signed, 'j!>' + end + end + end + + if pointer_size == 32 then + little_endian do + describe "String#unpack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'J_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'J!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J!' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'j_' + it_behaves_like :string_unpack_32bit_le_signed, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'j!' + it_behaves_like :string_unpack_32bit_le_signed, 'j!' + end + end + end + + big_endian do + describe "String#unpack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'J_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'J!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J!' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'j_' + it_behaves_like :string_unpack_32bit_be_signed, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'j!' + it_behaves_like :string_unpack_32bit_be_signed, 'j!' + end + end + end + + describe "String#unpack with format 'J'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'J<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'J>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J>' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'J<_' + it_behaves_like :string_unpack_32bit_le, 'J_<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J<_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'J<!' + it_behaves_like :string_unpack_32bit_le, 'J!<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J<!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J!<' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'J>_' + it_behaves_like :string_unpack_32bit_be, 'J_>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J>_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'J>!' + it_behaves_like :string_unpack_32bit_be, 'J!>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J>!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J!>' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'j<' + it_behaves_like :string_unpack_32bit_le_signed, 'j<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'j>' + it_behaves_like :string_unpack_32bit_be_signed, 'j>' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'j<_' + it_behaves_like :string_unpack_32bit_le, 'j_<' + it_behaves_like :string_unpack_32bit_le_signed, 'j<_' + it_behaves_like :string_unpack_32bit_le_signed, 'j_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'j<!' + it_behaves_like :string_unpack_32bit_le, 'j!<' + it_behaves_like :string_unpack_32bit_le_signed, 'j<!' + it_behaves_like :string_unpack_32bit_le_signed, 'j!<' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'j>_' + it_behaves_like :string_unpack_32bit_be, 'j_>' + it_behaves_like :string_unpack_32bit_be_signed, 'j>_' + it_behaves_like :string_unpack_32bit_be_signed, 'j_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'j>!' + it_behaves_like :string_unpack_32bit_be, 'j!>' + it_behaves_like :string_unpack_32bit_be_signed, 'j>!' + it_behaves_like :string_unpack_32bit_be_signed, 'j!>' + end + end + end +end diff --git a/spec/ruby/core/string/unpack/l_spec.rb b/spec/ruby/core/string/unpack/l_spec.rb new file mode 100644 index 0000000000..11f0648fc7 --- /dev/null +++ b/spec/ruby/core/string/unpack/l_spec.rb @@ -0,0 +1,265 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'L'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'L<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'L>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L>' + end + + guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'L<_' + it_behaves_like :string_unpack_32bit_le, 'L_<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L<_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'L<!' + it_behaves_like :string_unpack_32bit_le, 'L!<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L<!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L!<' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'L>_' + it_behaves_like :string_unpack_32bit_be, 'L_>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L>_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'L>!' + it_behaves_like :string_unpack_32bit_be, 'L!>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L>!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L!>' + end + end + + guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_64bit_le, 'L<_' + it_behaves_like :string_unpack_64bit_le, 'L_<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L<_' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_64bit_le, 'L<!' + it_behaves_like :string_unpack_64bit_le, 'L!<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L<!' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L!<' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_64bit_be, 'L>_' + it_behaves_like :string_unpack_64bit_be, 'L_>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L>_' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_64bit_be, 'L>!' + it_behaves_like :string_unpack_64bit_be, 'L!>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L>!' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L!>' + end + end +end + +describe "String#unpack with format 'l'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'l<' + it_behaves_like :string_unpack_32bit_le_signed, 'l<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'l>' + it_behaves_like :string_unpack_32bit_be_signed, 'l>' + end + + guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'l<_' + it_behaves_like :string_unpack_32bit_le, 'l_<' + it_behaves_like :string_unpack_32bit_le_signed, 'l<_' + it_behaves_like :string_unpack_32bit_le_signed, 'l_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'l<!' + it_behaves_like :string_unpack_32bit_le, 'l!<' + it_behaves_like :string_unpack_32bit_le_signed, 'l<!' + it_behaves_like :string_unpack_32bit_le_signed, 'l!<' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'l>_' + it_behaves_like :string_unpack_32bit_be, 'l_>' + it_behaves_like :string_unpack_32bit_be_signed, 'l>_' + it_behaves_like :string_unpack_32bit_be_signed, 'l_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'l>!' + it_behaves_like :string_unpack_32bit_be, 'l!>' + it_behaves_like :string_unpack_32bit_be_signed, 'l>!' + it_behaves_like :string_unpack_32bit_be_signed, 'l!>' + end + end + + guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_64bit_le, 'l<_' + it_behaves_like :string_unpack_64bit_le, 'l_<' + it_behaves_like :string_unpack_64bit_le_signed, 'l<_' + it_behaves_like :string_unpack_64bit_le_signed, 'l_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_64bit_le, 'l<!' + it_behaves_like :string_unpack_64bit_le, 'l!<' + it_behaves_like :string_unpack_64bit_le_signed, 'l<!' + it_behaves_like :string_unpack_64bit_le_signed, 'l!<' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_64bit_be, 'l>_' + it_behaves_like :string_unpack_64bit_be, 'l_>' + it_behaves_like :string_unpack_64bit_be_signed, 'l>_' + it_behaves_like :string_unpack_64bit_be_signed, 'l_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_64bit_be, 'l>!' + it_behaves_like :string_unpack_64bit_be, 'l!>' + it_behaves_like :string_unpack_64bit_be_signed, 'l>!' + it_behaves_like :string_unpack_64bit_be_signed, 'l!>' + end + end +end + +little_endian do + describe "String#unpack with format 'L'" do + it_behaves_like :string_unpack_basic, 'L' + it_behaves_like :string_unpack_32bit_le, 'L' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L' + end + + describe "String#unpack with format 'l'" do + it_behaves_like :string_unpack_basic, 'l' + it_behaves_like :string_unpack_32bit_le, 'l' + it_behaves_like :string_unpack_32bit_le_signed, 'l' + end + + guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'L_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'L!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'l_' + it_behaves_like :string_unpack_32bit_le_signed, 'l' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'l!' + it_behaves_like :string_unpack_32bit_le_signed, 'l' + end + end + + guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_64bit_le, 'L_' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_64bit_le, 'L!' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_64bit_le, 'l_' + it_behaves_like :string_unpack_64bit_le_signed, 'l_' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_64bit_le, 'l!' + it_behaves_like :string_unpack_64bit_le_signed, 'l!' + end + end +end + +big_endian do + describe "String#unpack with format 'L'" do + it_behaves_like :string_unpack_basic, 'L' + it_behaves_like :string_unpack_32bit_be, 'L' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L' + end + + describe "String#unpack with format 'l'" do + it_behaves_like :string_unpack_basic, 'l' + it_behaves_like :string_unpack_32bit_be, 'l' + it_behaves_like :string_unpack_32bit_be_signed, 'l' + end + + guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'L_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'L!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'l_' + it_behaves_like :string_unpack_32bit_be_signed, 'l' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'l!' + it_behaves_like :string_unpack_32bit_be_signed, 'l' + end + end + + guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_64bit_be, 'L_' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_64bit_be, 'L!' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_64bit_be, 'l_' + it_behaves_like :string_unpack_64bit_be_signed, 'l_' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_64bit_be, 'l!' + it_behaves_like :string_unpack_64bit_be_signed, 'l!' + end + end + +end diff --git a/spec/ruby/core/string/unpack/m_spec.rb b/spec/ruby/core/string/unpack/m_spec.rb new file mode 100644 index 0000000000..104f282fed --- /dev/null +++ b/spec/ruby/core/string/unpack/m_spec.rb @@ -0,0 +1,170 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'M'" do + it_behaves_like :string_unpack_basic, 'M' + it_behaves_like :string_unpack_no_platform, 'M' + + it "decodes an empty string" do + "".unpack("M").should == [""] + end + + it "decodes the complete string ignoring newlines when given a single directive" do + "a=\nb=\nc=\n".unpack("M").should == ["abc"] + end + + it "appends empty string to the array for directives exceeding the input size" do + "a=\nb=\nc=\n".unpack("MMM").should == ["abc", "", ""] + end + + it "ignores the count or '*' modifier and decodes the entire string" do + [ ["a=\nb=\nc=\n", "M238", ["abc"]], + ["a=\nb=\nc=\n", "M*", ["abc"]] + ].should be_computed_by(:unpack) + end + + it "decodes the '=' character" do + "=3D=\n".unpack("M").should == ["="] + end + + it "decodes an embedded space character" do + "a b=\n".unpack("M").should == ["a b"] + end + + it "decodes a space at the end of the pre-encoded string" do + "a =\n".unpack("M").should == ["a "] + end + + it "decodes an embedded tab character" do + "a\tb=\n".unpack("M").should == ["a\tb"] + end + + it "decodes a tab character at the end of the pre-encoded string" do + "a\t=\n".unpack("M").should == ["a\t"] + end + + it "decodes an embedded newline" do + "a\nb=\n".unpack("M").should == ["a\nb"] + end + + it "decodes pre-encoded byte values 33..60" do + [ ["!\"\#$%&'()*+,-./=\n", ["!\"\#$%&'()*+,-./"]], + ["0123456789=\n", ["0123456789"]], + [":;<=\n", [":;<"]] + ].should be_computed_by(:unpack, "M") + end + + it "decodes pre-encoded byte values 62..126" do + [ [">?@=\n", [">?@"]], + ["ABCDEFGHIJKLMNOPQRSTUVWXYZ=\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]], + ["[\\]^_`=\n", ["[\\]^_`"]], + ["abcdefghijklmnopqrstuvwxyz=\n", ["abcdefghijklmnopqrstuvwxyz"]], + ["{|}~=\n", ["{|}~"]] + ].should be_computed_by(:unpack, "M") + end + + it "decodes pre-encoded byte values 0..31 except tab and newline" do + [ ["=00=01=02=03=04=05=06=\n", ["\x00\x01\x02\x03\x04\x05\x06"]], + ["=07=08=0B=0C=0D=\n", ["\a\b\v\f\r"]], + ["=0E=0F=10=11=12=13=14=\n", ["\x0e\x0f\x10\x11\x12\x13\x14"]], + ["=15=16=17=18=19=1A=\n", ["\x15\x16\x17\x18\x19\x1a"]], + ["=1B=\n", ["\e"]], + ["=1C=1D=1E=1F=\n", ["\x1c\x1d\x1e\x1f"]] + ].should be_computed_by(:unpack, "M") + end + + it "decodes pre-encoded byte values 127..255" do + [ ["=7F=80=81=82=83=84=85=86=\n", ["\x7f\x80\x81\x82\x83\x84\x85\x86"]], + ["=87=88=89=8A=8B=8C=8D=8E=\n", ["\x87\x88\x89\x8a\x8b\x8c\x8d\x8e"]], + ["=8F=90=91=92=93=94=95=96=\n", ["\x8f\x90\x91\x92\x93\x94\x95\x96"]], + ["=97=98=99=9A=9B=9C=9D=9E=\n", ["\x97\x98\x99\x9a\x9b\x9c\x9d\x9e"]], + ["=9F=A0=A1=A2=A3=A4=A5=A6=\n", ["\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6"]], + ["=A7=A8=A9=AA=AB=AC=AD=AE=\n", ["\xa7\xa8\xa9\xaa\xab\xac\xad\xae"]], + ["=AF=B0=B1=B2=B3=B4=B5=B6=\n", ["\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6"]], + ["=B7=B8=B9=BA=BB=BC=BD=BE=\n", ["\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe"]], + ["=BF=C0=C1=C2=C3=C4=C5=C6=\n", ["\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6"]], + ["=C7=C8=C9=CA=CB=CC=CD=CE=\n", ["\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce"]], + ["=CF=D0=D1=D2=D3=D4=D5=D6=\n", ["\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6"]], + ["=D7=D8=D9=DA=DB=DC=DD=DE=\n", ["\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde"]], + ["=DF=E0=E1=E2=E3=E4=E5=E6=\n", ["\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6"]], + ["=E7=E8=E9=EA=EB=EC=ED=EE=\n", ["\xe7\xe8\xe9\xea\xeb\xec\xed\xee"]], + ["=EF=F0=F1=F2=F3=F4=F5=F6=\n", ["\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6"]], + ["=F7=F8=F9=FA=FB=FC=FD=FE=\n", ["\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe"]], + ["=FF=\n", ["\xff"]] + ].should be_computed_by(:unpack, "M") + end +end + +describe "String#unpack with format 'm'" do + it_behaves_like :string_unpack_basic, 'm' + it_behaves_like :string_unpack_no_platform, 'm' + + it "decodes an empty string" do + "".unpack("m").should == [""] + end + + it "decodes the complete string ignoring newlines when given a single directive" do + "YWJj\nREVG\n".unpack("m").should == ["abcDEF"] + end + + it "ignores the count or '*' modifier and decodes the entire string" do + [ ["YWJj\nREVG\n", "m238", ["abcDEF"]], + ["YWJj\nREVG\n", "m*", ["abcDEF"]] + ].should be_computed_by(:unpack) + end + + it "appends empty string to the array for directives exceeding the input size" do + "YWJj\nREVG\n".unpack("mmm").should == ["abcDEF", "", ""] + end + + it "decodes all pre-encoded ascii byte values" do + [ ["AAECAwQFBg==\n", ["\x00\x01\x02\x03\x04\x05\x06"]], + ["BwgJCgsMDQ==\n", ["\a\b\t\n\v\f\r"]], + ["Dg8QERITFBUW\n", ["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"]], + ["FxgZGhscHR4f\n", ["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"]], + ["ISIjJCUmJygpKissLS4v\n", ["!\"\#$%&'()*+,-./"]], + ["MDEyMzQ1Njc4OQ==\n", ["0123456789"]], + ["Ojs8PT4/QA==\n", [":;<=>?@"]], + ["QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]], + ["W1xdXl9g\n", ["[\\]^_`"]], + ["YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=\n", ["abcdefghijklmnopqrstuvwxyz"]], + ["e3x9fg==\n", ["{|}~"]], + ["f8KAwoHCgsKD\n", ["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"]], + ["woTChcKGwofC\n", ["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"]], + ["iMKJworCi8KM\n", ["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"]], + ["wo3CjsKPwpDC\n", ["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"]], + ["kcKSwpPClMKV\n", ["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"]], + ["wpbCl8KYwpnC\n", ["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"]], + ["msKbwpzCncKe\n", ["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"]], + ["wp/CoMKhwqLC\n", ["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"]], + ["o8KkwqXCpsKn\n", ["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"]], + ["wqjCqcKqwqvC\n", ["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"]], + ["rMKtwq7Cr8Kw\n", ["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"]], + ["wrHCssKzwrTC\n", ["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"]], + ["tcK2wrfCuMK5\n", ["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"]], + ["wrrCu8K8wr3C\n", ["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"]], + ["vsK/w4DDgcOC\n", ["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"]], + ["w4PDhMOFw4bD\n", ["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"]], + ["h8OIw4nDisOL\n", ["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"]], + ["w4zDjcOOw4/D\n", ["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"]], + ["kMORw5LDk8OU\n", ["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"]], + ["w5XDlsOXw5jD\n", ["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"]], + ["mcOaw5vDnMOd\n", ["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"]], + ["w57Dn8Ogw6HD\n", ["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"]], + ["osOjw6TDpcOm\n", ["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"]], + ["w6fDqMOpw6rD\n", ["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"]], + ["q8Osw63DrsOv\n", ["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"]], + ["w7DDscOyw7PD\n", ["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"]], + ["tMO1w7bDt8O4\n", ["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"]], + ["w7nDusO7w7zD\n", ["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"]], + ["vcO+w78=\n", ["\xbd\xc3\xbe\xc3\xbf"]] + ].should be_computed_by(:unpack, "m") + end + + it "produces binary strings" do + "".unpack("m").first.encoding.should == Encoding::BINARY + "Ojs8PT4/QA==\n".unpack("m").first.encoding.should == Encoding::BINARY + end +end diff --git a/spec/ruby/core/string/unpack/n_spec.rb b/spec/ruby/core/string/unpack/n_spec.rb new file mode 100644 index 0000000000..6e85346338 --- /dev/null +++ b/spec/ruby/core/string/unpack/n_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'N'" do + it_behaves_like :string_unpack_basic, 'N' + it_behaves_like :string_unpack_32bit_be, 'N' + it_behaves_like :string_unpack_32bit_be_unsigned, 'N' + it_behaves_like :string_unpack_no_platform, 'N' +end + +describe "String#unpack with format 'n'" do + it_behaves_like :string_unpack_basic, 'n' + it_behaves_like :string_unpack_16bit_be, 'n' + it_behaves_like :string_unpack_16bit_be_unsigned, 'n' + it_behaves_like :string_unpack_no_platform, 'n' +end diff --git a/spec/ruby/core/string/unpack/p_spec.rb b/spec/ruby/core/string/unpack/p_spec.rb new file mode 100644 index 0000000000..7c9a502a15 --- /dev/null +++ b/spec/ruby/core/string/unpack/p_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'P'" do + it_behaves_like :string_unpack_basic, 'P' + + it "returns a random object after consuming a size-of a machine word bytes" do + str = "\0" * 1.size + str.unpack("P").should be_kind_of(Object) + end +end + +describe "String#unpack with format 'p'" do + it_behaves_like :string_unpack_basic, 'p' + + it "returns a random object after consuming a size-of a machine word bytes" do + str = "\0" * 1.size + str.unpack("p").should be_kind_of(Object) + end +end diff --git a/spec/ruby/core/string/unpack/percent_spec.rb b/spec/ruby/core/string/unpack/percent_spec.rb new file mode 100644 index 0000000000..38cf81b037 --- /dev/null +++ b/spec/ruby/core/string/unpack/percent_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "String#unpack with format '%'" do + it "raises an Argument Error" do + lambda { "abc".unpack("%") }.should raise_error(ArgumentError) + end +end diff --git a/spec/ruby/core/string/unpack/q_spec.rb b/spec/ruby/core/string/unpack/q_spec.rb new file mode 100644 index 0000000000..91e65a9405 --- /dev/null +++ b/spec/ruby/core/string/unpack/q_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'Q'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_64bit_le, 'Q<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'Q<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_64bit_be, 'Q>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'Q>' + end +end + +describe "String#unpack with format 'q'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_64bit_le, 'q<' + it_behaves_like :string_unpack_64bit_le_signed, 'q<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_64bit_be, 'q>' + it_behaves_like :string_unpack_64bit_be_signed, 'q>' + end +end + +describe "String#unpack with format 'Q'" do + it_behaves_like :string_unpack_basic, 'Q' +end + +describe "String#unpack with format 'q'" do + it_behaves_like :string_unpack_basic, 'q' +end + +little_endian do + describe "String#unpack with format 'Q'" do + it_behaves_like :string_unpack_64bit_le, 'Q' + it_behaves_like :string_unpack_64bit_le_extra, 'Q' + it_behaves_like :string_unpack_64bit_le_unsigned, 'Q' + end + + describe "String#unpack with format 'q'" do + it_behaves_like :string_unpack_64bit_le, 'q' + it_behaves_like :string_unpack_64bit_le_extra, 'q' + it_behaves_like :string_unpack_64bit_le_signed, 'q' + end +end + +big_endian do + describe "String#unpack with format 'Q'" do + it_behaves_like :string_unpack_64bit_be, 'Q' + it_behaves_like :string_unpack_64bit_be_extra, 'Q' + it_behaves_like :string_unpack_64bit_be_unsigned, 'Q' + end + + describe "String#unpack with format 'q'" do + it_behaves_like :string_unpack_64bit_be, 'q' + it_behaves_like :string_unpack_64bit_be_extra, 'q' + it_behaves_like :string_unpack_64bit_be_signed, 'q' + end +end diff --git a/spec/ruby/core/string/unpack/s_spec.rb b/spec/ruby/core/string/unpack/s_spec.rb new file mode 100644 index 0000000000..c6b079b0a6 --- /dev/null +++ b/spec/ruby/core/string/unpack/s_spec.rb @@ -0,0 +1,152 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'S'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_16bit_le, 'S<' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_16bit_le, 'S<_' + it_behaves_like :string_unpack_16bit_le, 'S_<' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S_<' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S<_' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_16bit_le, 'S<!' + it_behaves_like :string_unpack_16bit_le, 'S!<' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S!<' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S<!' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_16bit_be, 'S>' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_16bit_be, 'S>_' + it_behaves_like :string_unpack_16bit_be, 'S_>' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S>_' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_16bit_be, 'S>!' + it_behaves_like :string_unpack_16bit_be, 'S!>' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S>!' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S!>' + end +end + +describe "String#unpack with format 's'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_16bit_le, 's<' + it_behaves_like :string_unpack_16bit_le_signed, 's<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_16bit_le, 's<_' + it_behaves_like :string_unpack_16bit_le, 's_<' + it_behaves_like :string_unpack_16bit_le_signed, 's<_' + it_behaves_like :string_unpack_16bit_le_signed, 's_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_16bit_le, 's<!' + it_behaves_like :string_unpack_16bit_le, 's!<' + it_behaves_like :string_unpack_16bit_le_signed, 's<!' + it_behaves_like :string_unpack_16bit_le_signed, 's!<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_16bit_be, 's>' + it_behaves_like :string_unpack_16bit_be_signed, 's>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_16bit_be, 's>_' + it_behaves_like :string_unpack_16bit_be, 's_>' + it_behaves_like :string_unpack_16bit_be_signed, 's>_' + it_behaves_like :string_unpack_16bit_be_signed, 's_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_16bit_be, 's>!' + it_behaves_like :string_unpack_16bit_be, 's!>' + it_behaves_like :string_unpack_16bit_be_signed, 's>!' + it_behaves_like :string_unpack_16bit_be_signed, 's!>' + end +end + +little_endian do + describe "String#unpack with format 'S'" do + it_behaves_like :string_unpack_basic, 'S' + it_behaves_like :string_unpack_16bit_le, 'S' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S' + end + + describe "String#unpack with format 'S' with modifier '_'" do + it_behaves_like :string_unpack_16bit_le, 'S_' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S_' + end + + describe "String#unpack with format 'S' with modifier '!'" do + it_behaves_like :string_unpack_16bit_le, 'S!' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S!' + end + + describe "String#unpack with format 's'" do + it_behaves_like :string_unpack_basic, 's' + it_behaves_like :string_unpack_16bit_le, 's' + it_behaves_like :string_unpack_16bit_le_signed, 's' + end + + describe "String#unpack with format 's' with modifier '_'" do + it_behaves_like :string_unpack_16bit_le, 's_' + it_behaves_like :string_unpack_16bit_le_signed, 's_' + end + + describe "String#unpack with format 's' with modifier '!'" do + it_behaves_like :string_unpack_16bit_le, 's!' + it_behaves_like :string_unpack_16bit_le_signed, 's!' + end +end + +big_endian do + describe "String#unpack with format 'S'" do + it_behaves_like :string_unpack_basic, 'S' + it_behaves_like :string_unpack_16bit_be, 'S' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S' + end + + describe "String#unpack with format 'S' with modifier '_'" do + it_behaves_like :string_unpack_16bit_be, 'S_' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S_' + end + + describe "String#unpack with format 'S' with modifier '!'" do + it_behaves_like :string_unpack_16bit_be, 'S!' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S!' + end + + describe "String#unpack with format 's'" do + it_behaves_like :string_unpack_basic, 's' + it_behaves_like :string_unpack_16bit_be, 's' + it_behaves_like :string_unpack_16bit_be_signed, 's' + end + + describe "String#unpack with format 's' with modifier '_'" do + it_behaves_like :string_unpack_16bit_be, 's_' + it_behaves_like :string_unpack_16bit_be_signed, 's_' + end + + describe "String#unpack with format 's' with modifier '!'" do + it_behaves_like :string_unpack_16bit_be, 's!' + it_behaves_like :string_unpack_16bit_be_signed, 's!' + end +end diff --git a/spec/ruby/core/string/unpack/shared/basic.rb b/spec/ruby/core/string/unpack/shared/basic.rb new file mode 100644 index 0000000000..0ecbf615af --- /dev/null +++ b/spec/ruby/core/string/unpack/shared/basic.rb @@ -0,0 +1,29 @@ +describe :string_unpack_basic, shared: true do + it "ignores whitespace in the format string" do + "abc".unpack("a \t\n\v\f\r"+unpack_format).should be_an_instance_of(Array) + end + + it "calls #to_str to coerce the directives string" do + d = mock("unpack directive") + d.should_receive(:to_str).and_return("a"+unpack_format) + "abc".unpack(d).should be_an_instance_of(Array) + end + + it "raises a TypeError when passed nil" do + lambda { "abc".unpack(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed an Integer" do + lambda { "abc".unpack(1) }.should raise_error(TypeError) + end +end + +describe :string_unpack_no_platform, shared: true do + it "raises an ArgumentError when the format modifier is '_'" do + lambda { "abcdefgh".unpack(unpack_format("_")) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the format modifier is '!'" do + lambda { "abcdefgh".unpack(unpack_format("!")) }.should raise_error(ArgumentError) + end +end diff --git a/spec/ruby/core/string/unpack/shared/float.rb b/spec/ruby/core/string/unpack/shared/float.rb new file mode 100644 index 0000000000..208dc357af --- /dev/null +++ b/spec/ruby/core/string/unpack/shared/float.rb @@ -0,0 +1,271 @@ +# -*- encoding: ascii-8bit -*- + +describe :string_unpack_float_le, shared: true do + it "decodes one float for a single format character" do + "\x8f\xc2\xb5?".unpack(unpack_format).should == [1.4199999570846558] + end + + it "decodes a negative float" do + "\xcd\xcc\x08\xc2".unpack(unpack_format).should == [-34.200000762939453] + end + + it "decodes two floats for two format characters" do + array = "\x9a\x999@33\xb3?".unpack(unpack_format(nil, 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + + it "decodes the number of floats requested by the count modifier" do + array = "\x9a\x999@33\xb3?33\x03A".unpack(unpack_format(3)) + array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137] + end + + it "decodes the remaining floats when passed the '*' modifier" do + array = "\x9a\x999@33\xb3?33\x03A".unpack(unpack_format("*")) + array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137] + end + + it "decodes the remaining floats when passed the '*' modifier after another directive" do + array = "\x9a\x99\xa9@33\x13A".unpack(unpack_format()+unpack_format('*')) + array.should == [5.300000190734863, 9.199999809265137] + end + + it "does not decode a float when fewer bytes than a float remain and the '*' modifier is passed" do + [ ["\xff", []], + ["\xff\x00", []], + ["\xff\x00\xff", []] + ].should be_computed_by(:unpack, unpack_format("*")) + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["abc", [nil, nil, nil]], + ["\x8f\xc2\xb5?abc", [1.4199999570846558, nil, nil]], + ["\x9a\x999@33\xb3?abc", [2.9000000953674316, 1.399999976158142, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "decodes positive Infinity" do + "\x00\x00\x80\x7f".unpack(unpack_format).should == [infinity_value] + end + + it "decodes negative Infinity" do + "\x00\x00\x80\xff".unpack(unpack_format).should == [-infinity_value] + end + + it "decodes NaN" do + # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884 + [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true + end + + it "ignores NULL bytes between directives" do + array = "\x9a\x999@33\xb3?".unpack(unpack_format("\000", 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + + it "ignores spaces between directives" do + array = "\x9a\x999@33\xb3?".unpack(unpack_format(' ', 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end +end + +describe :string_unpack_float_be, shared: true do + it "decodes one float for a single format character" do + "?\xb5\xc2\x8f".unpack(unpack_format).should == [1.4199999570846558] + end + + it "decodes a negative float" do + "\xc2\x08\xcc\xcd".unpack(unpack_format).should == [-34.200000762939453] + end + + it "decodes two floats for two format characters" do + array = "@9\x99\x9a?\xb333".unpack(unpack_format(nil, 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + + it "decodes the number of floats requested by the count modifier" do + array = "@9\x99\x9a?\xb333A\x0333".unpack(unpack_format(3)) + array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137] + end + + it "decodes the remaining floats when passed the '*' modifier" do + array = "@9\x99\x9a?\xb333A\x0333".unpack(unpack_format("*")) + array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137] + end + + it "decodes the remaining floats when passed the '*' modifier after another directive" do + array = "@\xa9\x99\x9aA\x1333".unpack(unpack_format()+unpack_format('*')) + array.should == [5.300000190734863, 9.199999809265137] + end + + it "does not decode a float when fewer bytes than a float remain and the '*' modifier is passed" do + [ ["\xff", []], + ["\xff\x00", []], + ["\xff\x00\xff", []] + ].should be_computed_by(:unpack, unpack_format("*")) + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["abc", [nil, nil, nil]], + ["?\xb5\xc2\x8fabc", [1.4199999570846558, nil, nil]], + ["@9\x99\x9a?\xb333abc", [2.9000000953674316, 1.399999976158142, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "decodes positive Infinity" do + "\x7f\x80\x00\x00".unpack(unpack_format).should == [infinity_value] + end + + it "decodes negative Infinity" do + "\xff\x80\x00\x00".unpack(unpack_format).should == [-infinity_value] + end + + it "decodes NaN" do + # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884 + [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true + end + + it "ignores NULL bytes between directives" do + array = "@9\x99\x9a?\xb333".unpack(unpack_format("\000", 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + + it "ignores spaces between directives" do + array = "@9\x99\x9a?\xb333".unpack(unpack_format(' ', 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end +end + +describe :string_unpack_double_le, shared: true do + it "decodes one double for a single format character" do + "\xb8\x1e\x85\xebQ\xb8\xf6?".unpack(unpack_format).should == [1.42] + end + + it "decodes a negative double" do + "\x9a\x99\x99\x99\x99\x19A\xc0".unpack(unpack_format).should == [-34.2] + end + + it "decodes two doubles for two format characters" do + "333333\x07@ffffff\xf6?".unpack(unpack_format(nil, 2)).should == [2.9, 1.4] + end + + it "decodes the number of doubles requested by the count modifier" do + array = "333333\x07@ffffff\xf6?ffffff\x20@".unpack(unpack_format(3)) + array.should == [2.9, 1.4, 8.2] + end + + it "decodes the remaining doubles when passed the '*' modifier" do + array = "333333\x07@ffffff\xf6?ffffff\x20@".unpack(unpack_format("*")) + array.should == [2.9, 1.4, 8.2] + end + + it "decodes the remaining doubles when passed the '*' modifier after another directive" do + array = "333333\x15@ffffff\x22@".unpack(unpack_format()+unpack_format('*')) + array.should == [5.3, 9.2] + end + + it "does not decode a double when fewer bytes than a double remain and the '*' modifier is passed" do + [ ["\xff", []], + ["\xff\x00", []], + ["\xff\x00\xff", []], + ["\xff\x00\xff\x00", []], + ["\xff\x00\xff\x00\xff", []], + ["\xff\x00\xff\x00\xff\x00", []], + ["\xff\x00\xff\x00\xff\x00\xff", []] + ].should be_computed_by(:unpack, unpack_format("*")) + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["\xff\x00\xff\x00\xff\x00\xff", [nil, nil, nil]], + ["\xb8\x1e\x85\xebQ\xb8\xf6?abc", [1.42, nil, nil]], + ["333333\x07@ffffff\xf6?abcd", [2.9, 1.4, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "decodes positive Infinity" do + "\x00\x00\x00\x00\x00\x00\xf0\x7f".unpack(unpack_format).should == [infinity_value] + end + + it "decodes negative Infinity" do + "\x00\x00\x00\x00\x00\x00\xf0\xff".unpack(unpack_format).should == [-infinity_value] + end + + it "decodes NaN" do + # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884 + [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true + end + + it "ignores NULL bytes between directives" do + "333333\x07@ffffff\xf6?".unpack(unpack_format("\000", 2)).should == [2.9, 1.4] + end + + it "ignores spaces between directives" do + "333333\x07@ffffff\xf6?".unpack(unpack_format(' ', 2)).should == [2.9, 1.4] + end +end + +describe :string_unpack_double_be, shared: true do + it "decodes one double for a single format character" do + "?\xf6\xb8Q\xeb\x85\x1e\xb8".unpack(unpack_format).should == [1.42] + end + + it "decodes a negative double" do + "\xc0A\x19\x99\x99\x99\x99\x9a".unpack(unpack_format).should == [-34.2] + end + + it "decodes two doubles for two format characters" do + "@\x07333333?\xf6ffffff".unpack(unpack_format(nil, 2)).should == [2.9, 1.4] + end + + it "decodes the number of doubles requested by the count modifier" do + array = "@\x07333333?\xf6ffffff@\x20ffffff".unpack(unpack_format(3)) + array.should == [2.9, 1.4, 8.2] + end + + it "decodes the remaining doubles when passed the '*' modifier" do + array = "@\x07333333?\xf6ffffff@\x20ffffff".unpack(unpack_format("*")) + array.should == [2.9, 1.4, 8.2] + end + + it "decodes the remaining doubles when passed the '*' modifier after another directive" do + array = "@\x15333333@\x22ffffff".unpack(unpack_format()+unpack_format('*')) + array.should == [5.3, 9.2] + end + + it "does not decode a double when fewer bytes than a double remain and the '*' modifier is passed" do + [ ["\xff", []], + ["\xff\x00", []], + ["\xff\x00\xff", []], + ["\xff\x00\xff\x00", []], + ["\xff\x00\xff\x00\xff", []], + ["\xff\x00\xff\x00\xff\x00", []], + ["\xff\x00\xff\x00\xff\x00\xff", []] + ].should be_computed_by(:unpack, unpack_format("*")) + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["abcdefg", [nil, nil, nil]], + ["?\xf6\xb8Q\xeb\x85\x1e\xb8abc", [1.42, nil, nil]], + ["@\x07333333?\xf6ffffffabcd", [2.9, 1.4, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "decodes positive Infinity" do + "\x7f\xf0\x00\x00\x00\x00\x00\x00".unpack(unpack_format).should == [infinity_value] + end + + it "decodes negative Infinity" do + "\xff\xf0\x00\x00\x00\x00\x00\x00".unpack(unpack_format).should == [-infinity_value] + end + + it "decodes NaN" do + # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884 + [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true + end + + it "ignores NULL bytes between directives" do + "@\x07333333?\xf6ffffff".unpack(unpack_format("\000", 2)).should == [2.9, 1.4] + end + + it "ignores spaces between directives" do + "@\x07333333?\xf6ffffff".unpack(unpack_format(' ', 2)).should == [2.9, 1.4] + end +end diff --git a/spec/ruby/core/string/unpack/shared/integer.rb b/spec/ruby/core/string/unpack/shared/integer.rb new file mode 100644 index 0000000000..03dfb5c682 --- /dev/null +++ b/spec/ruby/core/string/unpack/shared/integer.rb @@ -0,0 +1,339 @@ +# -*- encoding: ascii-8bit -*- + +describe :string_unpack_16bit_le, shared: true do + it "decodes one short for a single format character" do + "ab".unpack(unpack_format).should == [25185] + end + + it "decodes two shorts for two format characters" do + "abcd".unpack(unpack_format(nil, 2)).should == [25185, 25699] + end + + it "decodes the number of shorts requested by the count modifier" do + "abcdef".unpack(unpack_format(3)).should == [25185, 25699, 26213] + end + + it "decodes the remaining shorts when passed the '*' modifier" do + "abcd".unpack(unpack_format('*')).should == [25185, 25699] + end + + it "decodes the remaining shorts when passed the '*' modifier after another directive" do + "abcd".unpack(unpack_format()+unpack_format('*')).should == [25185, 25699] + end + + it "does not decode a short when fewer bytes than a short remain and the '*' modifier is passed" do + "\xff".unpack(unpack_format('*')).should == [] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["abc", [25185, nil, nil]], + ["abcd", [25185, 25699, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "abcd".unpack(unpack_format("\000", 2)).should == [25185, 25699] + end + + it "ignores spaces between directives" do + "abcd".unpack(unpack_format(' ', 2)).should == [25185, 25699] + end +end + +describe :string_unpack_16bit_le_signed, shared: true do + it "decodes a short with most significant bit set as a negative number" do + "\x00\xff".unpack(unpack_format()).should == [-256] + end +end + +describe :string_unpack_16bit_le_unsigned, shared: true do + it "decodes a short with most significant bit set as a positive number" do + "\x00\xff".unpack(unpack_format()).should == [65280] + end +end + +describe :string_unpack_16bit_be, shared: true do + it "decodes one short for a single format character" do + "ba".unpack(unpack_format).should == [25185] + end + + it "decodes two shorts for two format characters" do + "badc".unpack(unpack_format(nil, 2)).should == [25185, 25699] + end + + it "decodes the number of shorts requested by the count modifier" do + "badcfe".unpack(unpack_format(3)).should == [25185, 25699, 26213] + end + + it "decodes the remaining shorts when passed the '*' modifier" do + "badc".unpack(unpack_format('*')).should == [25185, 25699] + end + + it "decodes the remaining shorts when passed the '*' modifier after another directive" do + "badc".unpack(unpack_format()+unpack_format('*')).should == [25185, 25699] + end + + it "does not decode a short when fewer bytes than a short remain and the '*' modifier is passed" do + "\xff".unpack(unpack_format('*')).should == [] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["bac", [25185, nil, nil]], + ["badc", [25185, 25699, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "badc".unpack(unpack_format("\000", 2)).should == [25185, 25699] + end + + it "ignores spaces between directives" do + "badc".unpack(unpack_format(' ', 2)).should == [25185, 25699] + end +end + +describe :string_unpack_16bit_be_signed, shared: true do + it "decodes a short with most significant bit set as a negative number" do + "\xff\x00".unpack(unpack_format()).should == [-256] + end +end + +describe :string_unpack_16bit_be_unsigned, shared: true do + it "decodes a short with most significant bit set as a positive number" do + "\xff\x00".unpack(unpack_format()).should == [65280] + end +end + +describe :string_unpack_32bit_le, shared: true do + it "decodes one int for a single format character" do + "abcd".unpack(unpack_format).should == [1684234849] + end + + it "decodes two ints for two format characters" do + "abghefcd".unpack(unpack_format(nil, 2)).should == [1751605857, 1684235877] + end + + it "decodes the number of ints requested by the count modifier" do + "abcedfgh".unpack(unpack_format(2)).should == [1701012065, 1751606884] + end + + it "decodes the remaining ints when passed the '*' modifier" do + "acbdegfh".unpack(unpack_format('*')).should == [1684169569, 1751541605] + end + + it "decodes the remaining ints when passed the '*' modifier after another directive" do + "abcdefgh".unpack(unpack_format()+unpack_format('*')).should == [1684234849, 1751606885] + end + + it "does not decode an int when fewer bytes than an int remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*')).should == [] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["abcde", [1684234849, nil, nil]], + ["abcdefg", [1684234849, nil, nil]], + ["abcdefgh", [1684234849, 1751606885, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "abcdefgh".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885] + end + + it "ignores spaces between directives" do + "abcdefgh".unpack(unpack_format(' ', 2)).should == [1684234849, 1751606885] + end +end + +describe :string_unpack_32bit_le_signed, shared: true do + it "decodes an int with most significant bit set as a negative number" do + "\x00\xaa\x00\xff".unpack(unpack_format()).should == [-16733696] + end +end + +describe :string_unpack_32bit_le_unsigned, shared: true do + it "decodes an int with most significant bit set as a positive number" do + "\x00\xaa\x00\xff".unpack(unpack_format()).should == [4278233600] + end +end + +describe :string_unpack_32bit_be, shared: true do + it "decodes one int for a single format character" do + "dcba".unpack(unpack_format).should == [1684234849] + end + + it "decodes two ints for two format characters" do + "hgbadcfe".unpack(unpack_format(nil, 2)).should == [1751605857, 1684235877] + end + + it "decodes the number of ints requested by the count modifier" do + "ecbahgfd".unpack(unpack_format(2)).should == [1701012065, 1751606884] + end + + it "decodes the remaining ints when passed the '*' modifier" do + "dbcahfge".unpack(unpack_format('*')).should == [1684169569, 1751541605] + end + + it "decodes the remaining ints when passed the '*' modifier after another directive" do + "dcbahgfe".unpack(unpack_format()+unpack_format('*')).should == [1684234849, 1751606885] + end + + it "does not decode an int when fewer bytes than an int remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*')).should == [] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["dcbae", [1684234849, nil, nil]], + ["dcbaefg", [1684234849, nil, nil]], + ["dcbahgfe", [1684234849, 1751606885, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "dcbahgfe".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885] + end + + it "ignores spaces between directives" do + "dcbahgfe".unpack(unpack_format(' ', 2)).should == [1684234849, 1751606885] + end +end + +describe :string_unpack_32bit_be_signed, shared: true do + it "decodes an int with most significant bit set as a negative number" do + "\xff\x00\xaa\x00".unpack(unpack_format()).should == [-16733696] + end +end + +describe :string_unpack_32bit_be_unsigned, shared: true do + it "decodes an int with most significant bit set as a positive number" do + "\xff\x00\xaa\x00".unpack(unpack_format()).should == [4278233600] + end +end + +describe :string_unpack_64bit_le, shared: true do + it "decodes one long for a single format character" do + "abcdefgh".unpack(unpack_format).should == [7523094288207667809] + end + + it "decodes two longs for two format characters" do + array = "abghefcdghefabcd".unpack(unpack_format(nil, 2)) + array.should == [7233738012216484449, 7233733596956420199] + end + + it "decodes the number of longs requested by the count modifier" do + array = "abcedfghefcdghef".unpack(unpack_format(2)) + array.should == [7523094283929477729, 7378418357791581797] + end + + it "decodes the remaining longs when passed the '*' modifier" do + array = "acbdegfhdegfhacb".unpack(unpack_format('*')) + array.should == [7522813912742519649, 7089617339433837924] + end + + it "decodes the remaining longs when passed the '*' modifier after another directive" do + array = "bcahfgedhfgedbca".unpack(unpack_format()+unpack_format('*')) + array.should == [7234302065976107874, 7017560827710891624] + end + + it "does not decode a long when fewer bytes than a long remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*')).should == [] + end + + it "ignores NULL bytes between directives" do + array = "abcdefghabghefcd".unpack(unpack_format("\000", 2)) + array.should == [7523094288207667809, 7233738012216484449] + end + + it "ignores spaces between directives" do + array = "abcdefghabghefcd".unpack(unpack_format(' ', 2)) + array.should == [7523094288207667809, 7233738012216484449] + end +end + +describe :string_unpack_64bit_le_extra, shared: true do + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["abcdefgh", [7523094288207667809, nil, nil]], + ["abcdefghcdefab", [7523094288207667809, nil, nil]], + ["abcdefghcdefabde", [7523094288207667809, 7306072665971057763, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end +end + +describe :string_unpack_64bit_le_signed, shared: true do + it "decodes a long with most significant bit set as a negative number" do + "\x00\xcc\x00\xbb\x00\xaa\x00\xff".unpack(unpack_format()).should == [-71870673923814400] + end +end + +describe :string_unpack_64bit_le_unsigned, shared: true do + it "decodes a long with most significant bit set as a positive number" do + "\x00\xcc\x00\xbb\x00\xaa\x00\xff".unpack(unpack_format()).should == [18374873399785737216] + end +end + +describe :string_unpack_64bit_be, shared: true do + it "decodes one long for a single format character" do + "hgfedcba".unpack(unpack_format).should == [7523094288207667809] + end + + it "decodes two longs for two format characters" do + array = "dcfehgbadcbafehg".unpack(unpack_format(nil, 2)) + array.should == [7233738012216484449, 7233733596956420199] + end + + it "decodes the number of longs requested by the count modifier" do + array = "hgfdecbafehgdcfe".unpack(unpack_format(2)) + array.should == [7523094283929477729, 7378418357791581797] + end + + it "decodes the remaining longs when passed the '*' modifier" do + array = "hfgedbcabcahfged".unpack(unpack_format('*')) + array.should == [7522813912742519649, 7089617339433837924] + end + + it "decodes the remaining longs when passed the '*' modifier after another directive" do + array = "degfhacbacbdegfh".unpack(unpack_format()+unpack_format('*')) + array.should == [7234302065976107874, 7017560827710891624] + end + + it "does not decode a long when fewer bytes than a long remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*')).should == [] + end + + it "ignores NULL bytes between directives" do + array = "hgfedcbadcfehgba".unpack(unpack_format("\000", 2)) + array.should == [7523094288207667809, 7233738012216484449] + end + + it "ignores spaces between directives" do + array = "hgfedcbadcfehgba".unpack(unpack_format(' ', 2)) + array.should == [7523094288207667809, 7233738012216484449] + end +end + +describe :string_unpack_64bit_be_extra, shared: true do + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["hgfedcba", [7523094288207667809, nil, nil]], + ["hgfedcbacdefab", [7523094288207667809, nil, nil]], + ["hgfedcbaedbafedc", [7523094288207667809, 7306072665971057763, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end +end + +describe :string_unpack_64bit_be_signed, shared: true do + it "decodes a long with most significant bit set as a negative number" do + "\xff\x00\xaa\x00\xbb\x00\xcc\x00".unpack(unpack_format()).should == [-71870673923814400] + end +end + +describe :string_unpack_64bit_be_unsigned, shared: true do + it "decodes a long with most significant bit set as a positive number" do + "\xff\x00\xaa\x00\xbb\x00\xcc\x00".unpack(unpack_format()).should == [18374873399785737216] + end +end diff --git a/spec/ruby/core/string/unpack/shared/string.rb b/spec/ruby/core/string/unpack/shared/string.rb new file mode 100644 index 0000000000..9d85eedf26 --- /dev/null +++ b/spec/ruby/core/string/unpack/shared/string.rb @@ -0,0 +1,51 @@ +describe :string_unpack_string, shared: true do + it "returns an empty string if the input is empty" do + "".unpack(unpack_format).should == [""] + end + + it "returns empty strings for repeated formats if the input is empty" do + "".unpack(unpack_format(nil, 3)).should == ["", "", ""] + end + + it "returns an empty string and does not decode any bytes when the count modifier is zero" do + "abc".unpack(unpack_format(0)+unpack_format).should == ["", "a"] + end + + it "implicitly has a count of one when no count is specified" do + "abc".unpack(unpack_format).should == ["a"] + end + + it "decodes the number of bytes specified by the count modifier" do + "abc".unpack(unpack_format(3)).should == ["abc"] + end + + it "decodes the number of bytes specified by the count modifier including whitespace bytes" do + [ ["a bc", ["a b", "c"]], + ["a\fbc", ["a\fb", "c"]], + ["a\nbc", ["a\nb", "c"]], + ["a\rbc", ["a\rb", "c"]], + ["a\tbc", ["a\tb", "c"]], + ["a\vbc", ["a\vb", "c"]] + ].should be_computed_by(:unpack, unpack_format(3)+unpack_format) + end + + it "decodes past whitespace bytes when passed the '*' modifier" do + [ ["a b c", ["a b c"]], + ["a\fb c", ["a\fb c"]], + ["a\nb c", ["a\nb c"]], + ["a\rb c", ["a\rb c"]], + ["a\tb c", ["a\tb c"]], + ["a\vb c", ["a\vb c"]], + ].should be_computed_by(:unpack, unpack_format("*")) + end +end + +describe :string_unpack_Aa, shared: true do + it "decodes the number of bytes specified by the count modifier including NULL bytes" do + "a\x00bc".unpack(unpack_format(3)+unpack_format).should == ["a\x00b", "c"] + end + + it "decodes past NULL bytes when passed the '*' modifier" do + "a\x00b c".unpack(unpack_format("*")).should == ["a\x00b c"] + end +end diff --git a/spec/ruby/core/string/unpack/shared/unicode.rb b/spec/ruby/core/string/unpack/shared/unicode.rb new file mode 100644 index 0000000000..a2b4e142b2 --- /dev/null +++ b/spec/ruby/core/string/unpack/shared/unicode.rb @@ -0,0 +1,60 @@ +# -*- encoding: utf-8 -*- + +describe :string_unpack_unicode, shared: true do + it "decodes Unicode codepoints as ASCII values" do + [ ["\x00", [0]], + ["\x01", [1]], + ["\x08", [8]], + ["\x0f", [15]], + ["\x18", [24]], + ["\x1f", [31]], + ["\x7f", [127]], + ["\xc2\x80", [128]], + ["\xc2\x81", [129]], + ["\xc3\xbf", [255]] + ].should be_computed_by(:unpack, "U") + end + + it "decodes the number of characters specified by the count modifier" do + [ ["\xc2\x80\xc2\x81\xc2\x82\xc2\x83", "U1", [0x80]], + ["\xc2\x80\xc2\x81\xc2\x82\xc2\x83", "U2", [0x80, 0x81]], + ["\xc2\x80\xc2\x81\xc2\x82\xc2\x83", "U3", [0x80, 0x81, 0x82]] + ].should be_computed_by(:unpack) + end + + it "implicitly has a count of one when no count modifier is passed" do + "\xc2\x80\xc2\x81\xc2\x82\xc2\x83".unpack("U1").should == [0x80] + end + + it "decodes all remaining characters when passed the '*' modifier" do + "\xc2\x80\xc2\x81\xc2\x82\xc2\x83".unpack("U*").should == [0x80, 0x81, 0x82, 0x83] + end + + it "decodes UTF-8 BMP codepoints" do + [ ["\xc2\x80", [0x80]], + ["\xdf\xbf", [0x7ff]], + ["\xe0\xa0\x80", [0x800]], + ["\xef\xbf\xbf", [0xffff]] + ].should be_computed_by(:unpack, "U") + end + + it "decodes UTF-8 max codepoints" do + [ ["\xf0\x90\x80\x80", [0x10000]], + ["\xf3\xbf\xbf\xbf", [0xfffff]], + ["\xf4\x80\x80\x80", [0x100000]], + ["\xf4\x8f\xbf\xbf", [0x10ffff]] + ].should be_computed_by(:unpack, "U") + end + + it "does not decode any items for directives exceeding the input string size" do + "\xc2\x80".unpack("UUUU").should == [0x80] + end + + it "ignores NULL bytes between directives" do + "\x01\x02".unpack("U\x00U").should == [1, 2] + end + + it "ignores spaces between directives" do + "\x01\x02".unpack("U U").should == [1, 2] + end +end diff --git a/spec/ruby/core/string/unpack/u_spec.rb b/spec/ruby/core/string/unpack/u_spec.rb new file mode 100644 index 0000000000..0765da8d96 --- /dev/null +++ b/spec/ruby/core/string/unpack/u_spec.rb @@ -0,0 +1,94 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/unicode', __FILE__) + +describe "String#unpack with format 'U'" do + it_behaves_like :string_unpack_basic, 'U' + it_behaves_like :string_unpack_no_platform, 'U' + it_behaves_like :string_unpack_unicode, 'U' + + it "raises ArgumentError on a malformed byte sequence" do + lambda { "\xE3".unpack('U') }.should raise_error(ArgumentError) + end + + it "raises ArgumentError on a malformed byte sequence and doesn't continue when used with the * modifier" do + lambda { "\xE3".unpack('U*') }.should raise_error(ArgumentError) + end +end + +describe "String#unpack with format 'u'" do + it_behaves_like :string_unpack_basic, 'u' + it_behaves_like :string_unpack_no_platform, 'u' + + it "decodes an empty string as an empty string" do + "".unpack("u").should == [""] + end + + it "decodes into raw (ascii) string values" do + str = "".unpack("u")[0] + str.encoding.name.should == 'ASCII-8BIT' + + str = "1".force_encoding('UTF-8').unpack("u")[0] + str.encoding.name.should == 'ASCII-8BIT' + end + + it "decodes the complete string ignoring newlines when given a single directive" do + "#86)C\n#1$5&\n".unpack("u").should == ["abcDEF"] + end + + it "appends empty string to the array for directives exceeding the input size" do + "#86)C\n#1$5&\n".unpack("uuu").should == ["abcDEF", "", ""] + end + + it "ignores the count or '*' modifier and decodes the entire string" do + [ ["#86)C\n#1$5&\n", "u238", ["abcDEF"]], + ["#86)C\n#1$5&\n", "u*", ["abcDEF"]] + ].should be_computed_by(:unpack) + end + + it "decodes all ascii characters" do + [ ["'``$\"`P0%!@``\n", ["\x00\x01\x02\x03\x04\x05\x06"]], + ["'!P@)\"@L,#0``\n", ["\a\b\t\n\v\f\r"]], + [")\#@\\0$1(3%!46\n", ["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"]], + [")%Q@9&AL<'1X?\n", ["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"]], + ["/(2(C)\"4F)R@I*BLL+2XO\n", ["!\"\#$%&'()*+,-./"]], + ["*,\#$R,S0U-C<X.0``\n", ["0123456789"]], + ["'.CL\\/3X_0```\n", [":;<=>?@"]], + [":04)#1$5&1TA)2DM,34Y/4%%24U155E=865H`\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]], + ["&6UQ=7E]@\n", ["[\\]^_`"]], + [":86)C9&5F9VAI:FML;6YO<'%R<W1U=G=X>7H`\n", ["abcdefghijklmnopqrstuvwxyz"]], + ["$>WQ]?@``\n", ["{|}~"]], + [")?\\*`PH'\"@L*#\n", ["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"]], + [")PH3\"A<*&PH?\"\n", ["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"]], + [")B,*)PHK\"B\\*,\n", ["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"]], + [")PHW\"CL*/PI#\"\n", ["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"]], + [")D<*2PI/\"E,*5\n", ["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"]], + [")PI;\"E\\*8PIG\"\n", ["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"]], + [")FL*;PIS\"G<*>\n", ["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"]], + [")PI_\"H,*APJ+\"\n", ["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"]], + [")H\\*DPJ7\"IL*G\n", ["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"]], + [")PJC\"J<*JPJO\"\n", ["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"]], + [")K,*MPJ[\"K\\*P\n", ["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"]], + [")PK'\"LL*SPK3\"\n", ["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"]], + [")M<*VPK?\"N,*Y\n", ["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"]], + [")PKK\"N\\*\\PKW\"\n", ["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"]], + [")OL*_PX#\#@<.\"\n", ["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"]], + [")PX/#A,.%PX;#\n", ["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"]], + [")A\\.(PXG#BL.+\n", ["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"]], + [")PXS#C<..PX_#\n", ["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"]], + [")D,.1PY+#D\\.4\n", ["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"]], + [")PY7#EL.7PYC#\n", ["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"]], + [")F<.:PYO#G,.=\n", ["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"]], + [")PY[#G\\.@PZ'#\n", ["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"]], + [")HL.CPZ3#I<.F\n", ["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"]], + [")PZ?#J,.IPZK#\n", ["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"]], + [")J\\.LPZW#KL.O\n", ["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"]], + [")P[##L<.RP[/#\n", ["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"]], + [")M,.UP[;#M\\.X\n", ["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"]], + [")P[G#NL.[P[S#\n", ["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"]], + ["%O<.^P[\\`\n", ["\xbd\xc3\xbe\xc3\xbf"]] + ].should be_computed_by(:unpack, "u") + end +end diff --git a/spec/ruby/core/string/unpack/v_spec.rb b/spec/ruby/core/string/unpack/v_spec.rb new file mode 100644 index 0000000000..33cf23c68b --- /dev/null +++ b/spec/ruby/core/string/unpack/v_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'V'" do + it_behaves_like :string_unpack_basic, 'V' + it_behaves_like :string_unpack_32bit_le, 'V' + it_behaves_like :string_unpack_32bit_le_unsigned, 'V' + it_behaves_like :string_unpack_no_platform, 'V' +end + +describe "String#unpack with format 'v'" do + it_behaves_like :string_unpack_basic, 'v' + it_behaves_like :string_unpack_16bit_le, 'v' + it_behaves_like :string_unpack_16bit_le_unsigned, 'v' + it_behaves_like :string_unpack_no_platform, 'v' +end diff --git a/spec/ruby/core/string/unpack/w_spec.rb b/spec/ruby/core/string/unpack/w_spec.rb new file mode 100644 index 0000000000..22f5980a46 --- /dev/null +++ b/spec/ruby/core/string/unpack/w_spec.rb @@ -0,0 +1,25 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with directive 'w'" do + it_behaves_like :string_unpack_basic, 'w' + it_behaves_like :string_unpack_no_platform, 'w' + + it "decodes a BER-compressed integer" do + [ ["\x00", [0]], + ["\x01", [1]], + ["\xce\x0f", [9999]], + ["\x84\x80\x80\x80\x80\x80\x80\x80\x80\x00", [2**65]] + ].should be_computed_by(:unpack, "w") + end + + it "ignores NULL bytes between directives" do + "\x01\x02\x03".unpack("w\x00w").should == [1, 2] + end + + it "ignores spaces between directives" do + "\x01\x02\x03".unpack("w w").should == [1, 2] + end +end diff --git a/spec/ruby/core/string/unpack/x_spec.rb b/spec/ruby/core/string/unpack/x_spec.rb new file mode 100644 index 0000000000..e765472413 --- /dev/null +++ b/spec/ruby/core/string/unpack/x_spec.rb @@ -0,0 +1,62 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'X'" do + it_behaves_like :string_unpack_basic, 'X' + it_behaves_like :string_unpack_no_platform, 'X' + + it "moves the read index back by the number of bytes specified by count" do + "\x01\x02\x03\x04".unpack("C3X2C").should == [1, 2, 3, 2] + end + + it "does not change the read index when passed a count of zero" do + "\x01\x02\x03\x04".unpack("C3X0C").should == [1, 2, 3, 4] + end + + it "implicitly has a count of one when count is not specified" do + "\x01\x02\x03\x04".unpack("C3XC").should == [1, 2, 3, 3] + end + + it "moves the read index back by the remaining bytes when passed the '*' modifier" do + "abcd".unpack("C3X*C").should == [97, 98, 99, 99] + end + + it "raises an ArgumentError when passed the '*' modifier if the remaining bytes exceed the bytes from the index to the start of the String" do + lambda { "abcd".unpack("CX*C") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the count exceeds the bytes from current index to the start of the String" do + lambda { "\x01\x02\x03\x04".unpack("C3X4C") }.should raise_error(ArgumentError) + end +end + +describe "String#unpack with format 'x'" do + it_behaves_like :string_unpack_basic, 'x' + it_behaves_like :string_unpack_no_platform, 'x' + + it "moves the read index forward by the number of bytes specified by count" do + "\x01\x02\x03\x04".unpack("Cx2C").should == [1, 4] + end + + it "implicitly has a count of one when count is not specified" do + "\x01\x02\x03\x04".unpack("CxC").should == [1, 3] + end + + it "does not change the read index when passed a count of zero" do + "\x01\x02\x03\x04".unpack("Cx0C").should == [1, 2] + end + + it "moves the read index to the end of the string when passed the '*' modifier" do + "\x01\x02\x03\x04".unpack("Cx*C").should == [1, nil] + end + + it "positions the read index one beyond the last readable byte in the String" do + "\x01\x02\x03\x04".unpack("C2x2C").should == [1, 2, nil] + end + + it "raises an ArgumentError if the count exceeds the size of the String" do + lambda { "\x01\x02\x03\x04".unpack("C2x3C") }.should raise_error(ArgumentError) + end +end diff --git a/spec/ruby/core/string/unpack/z_spec.rb b/spec/ruby/core/string/unpack/z_spec.rb new file mode 100644 index 0000000000..7c3d167ac2 --- /dev/null +++ b/spec/ruby/core/string/unpack/z_spec.rb @@ -0,0 +1,21 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/string', __FILE__) + +describe "String#unpack with format 'Z'" do + it_behaves_like :string_unpack_basic, 'Z' + it_behaves_like :string_unpack_no_platform, 'Z' + it_behaves_like :string_unpack_string, 'Z' + + it "stops decoding at NULL bytes when passed the '*' modifier" do + "a\x00\x00 b \x00c".unpack('Z*Z*Z*Z*').should == ["a", "", " b ", "c"] + end + + it "decodes the number of bytes specified by the count modifier and truncates the decoded string at the first NULL byte" do + [ ["a\x00 \x00b c", ["a", " "]], + ["\x00a\x00 bc \x00", ["", "c"]] + ].should be_computed_by(:unpack, "Z5Z") + end +end |