From c0fb725f9a1be57086c42a0ba15365585b89d112 Mon Sep 17 00:00:00 2001 From: Lamont Granquist Date: Fri, 13 Jun 2014 18:18:00 -0700 Subject: additional parser specs --- spec/ffi_yajl/parser_spec.rb | 387 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 337 insertions(+), 50 deletions(-) diff --git a/spec/ffi_yajl/parser_spec.rb b/spec/ffi_yajl/parser_spec.rb index e4b1854..721209d 100644 --- a/spec/ffi_yajl/parser_spec.rb +++ b/spec/ffi_yajl/parser_spec.rb @@ -4,79 +4,366 @@ require 'spec_helper' describe "FFI_Yajl::Parser" do - let(:parser) { FFI_Yajl::Parser.new } + shared_examples_for "correct json parsing" do + context "when json has 23456789012E666" do + let(:json) { '{"key": 23456789012E666}' } - it "returns nil for an empty string (compatibility with yajl-ruby)" do - json = '' - expect( parser.parse(json) ).to be_nil - end + it "should return infinity" do + infinity = (1.0/0) + expect(parser).to eq({"key" => infinity}) + end + end - it "throws an exception when trailing braces are missing" do - json = '{{"foo": 1234}' - expect { parser.parse(json) }.to raise_error(FFI_Yajl::ParseError) - end + context "when json has comments" do + let(:json) { '{"key": /* this is a comment */ "value"}' } - it "throws an exception when trailing brackets are missing" do - json = '[["foo", "bar"]' - expect { parser.parse(json) }.to raise_error(FFI_Yajl::ParseError) - end + context "when allow_comments is false" do + let(:options) { { :allow_comments => false } } - it "throws an exception when it has an extra brace" do - json = '{{"foo": 1234}}}' - expect { parser.parse(json) }.to raise_error(FFI_Yajl::ParseError) - end + it "should not parse" do + expect{parser}.to raise_error(FFI_Yajl::ParseError) + end + end - it "throws an exception when it has an extra bracket" do - json = '[["foo", "bar"]]]' - expect { parser.parse(json) }.to raise_error(FFI_Yajl::ParseError) - end + context "when allow_comments is true" do + let(:options) { { :allow_comments => true } } + + it "should parse" do + expect(parser).to eq({}) # FIXME + end + end + end + + context "when json is invalid UTF8" do + let(:json) { "[\"#{"\201\203"}\"]" } + + context "when :check_utf8 is set to true" do + let(:options) { { :check_utf8 => true } } + + it "should not parse" do + expect{parser}.to raise_error(FFI_Yajl::ParseError) + end + end + + context "when :check_utf8 is set to false" do + let(:options) { { :check_utf8 => false } } + + it "should parse" do + expect(parser).to eq({}) # FIXME + end + end + end + + context "when JSON is a StringIO" do + let(:json) { StringIO.new('{"key": 1234}') } + + it "should parse" do + expect(parser).to eq({"key" => 1234}) + end + end + + context "when parsing a JSON string" do + let(:json) { '{"key": 1234}' } + + it "should parse correctly" do + expect(parser).to eq({"key" => 1234}) + end + + context "when symbolize_keys is true" do + let(:options) { { :symbolize_keys => true } } + + it "should symbolize keys correctly" do + expect(parser).to eq({:key => 1234}) + end + end - context "when parsing floats" do - it "parses simple floating point values" do - json = '{"foo": 3.14159265358979}' - expect(parser.parse(json)).to eql( "foo" => 3.14159265358979 ) + context "when passing a block" do + it "should parse correctly" do + output = nil + parser do |obj| + output = obj + end + expect(output).to eq({"key" => 1234}) + end + end + + if RUBY_VERSION.to_f >= 1.9 + context "when Encoding.default_internal is nil" do + before do + @saved_encoding = Encoding.default_internal + Encoding.default_internal = nil + end + after do + Encoding.default_internal = @saved_encoding + end + it "encodes keys to UTF-8" do + expect(parser.keys.first.encoding).to eql(Encoding.find('utf-8')) + end + it "encodes values to UTF-8" do + expect(parser.values.first.encoding).to eql(Encoding.find('utf-8')) + end + end + + %w{utf-8 us-ascii}.each do |encoding| + context "when Encoding.default_internal is #{encoding}" do + before do + @saved_encoding = Encoding.default_internal + Encoding.default_internal = nil + end + after do + Encoding.default_internal = @saved_encoding + end + it "encodes keys to #{encoding}" do + expect(parser.keys.first.encoding).to eql(Encoding.find(encoding)) + end + it "encodes values to #{encoding}" do + expect(parser.values.first.encoding).to eql(Encoding.find(encoding)) + end + end + end + end end - it "parses simple negative floating point values" do - json = '{"foo":-2.00231930436153}' - expect(parser.parse(json)).to eql( "foo" => -2.00231930436153 ) + context "when a parsed key has utf-8 multibyte characters" do + let(:json) { '{"日本語": 1234}' } + + it "should parse correctly" do + expect(parser).to eq({"日本語" => 1234}) + end + + context "when symbolize_keys is true" do + let(:options) { { :symbolize_keys => true } } + + it "should symbolize keys correctly" do + expect(parser).to eq({:"日本語" => 1234}) + end + + if RUBY_VERSION.to_f >= 1.9 + it "should parse non-ascii symbols in UTF-8" do + expect(parser.keys.fetch(0).encoding).to eq(Encoding::UTF_8) + end + end + end end - it "parses floats with negative exponents and a large E" do - json = '{"foo": 1.602176565E-19}' - expect(parser.parse(json)).to eql( "foo" => 1.602176565e-19 ) + context "when parsing 2147483649" do + let(:json) { "{\"id\": 2147483649}" } + + it "should parse corectly" do + expect(parser).to eql({"id" => 2147483649}) + end end - it "parses floats with negative exponents and a small e" do - json = '{"foo": 6.6260689633e-34 }' - expect(parser.parse(json)).to eql( "foo" => 6.6260689633e-34 ) + context "when parsing 5687389800" do + let(:json) { "{\"id\": 5687389800}" } + + it "should parse corectly" do + expect(parser).to eql({"id" => 5687389800}) + end end - it "parses floats with positive exponents and a large E" do - json = '{"foo": 6.0221413E+23}' - expect(parser.parse(json)).to eql( "foo" => 6.0221413e+23 ) + context "when parsing 1046289770033519442869495707521600000000" do + let(:json) { "{\"id\": 1046289770033519442869495707521600000000}" } + + it "should parse corectly" do + expect(parser).to eql({"id" => 1046289770033519442869495707521600000000}) + end end - it "parses floats with positive exponents and a small e" do - json = '{"foo": 8.9875517873681764e+9 }' - expect(parser.parse(json)).to eql( "foo" => 8.9875517873681764e+9 ) + # NOTE: we are choosing to be compatible with yajl-ruby here vs. JSON + # gem and libyajl C behavior (which is to throw an exception in this case) + context "when the JSON is empty string" do + let(:json) { '' } + + it "returns nil" do + expect(parser).to be_nil + end end - it "parses floats with an exponent without a sign and a large E" do - json = '{"foo": 2.99792458E8 }' - expect(parser.parse(json)).to eql( "foo" => 2.99792458e+8 ) + # NOTE: this fixes yajl-ruby being too permissive + context "when dealing with too much or too little input" do + context "when trailing braces are missing" do + let(:json) { '{{"foo": 1234}' } + + it "raises an exception" do + expect { parser }.to raise_error(FFI_Yajl::ParseError) + end + end + + context "when trailing brackets are missing" do + let(:json) { '[["foo", "bar"]' } + + it "raises an exception" do + expect { parser }.to raise_error(FFI_Yajl::ParseError) + end + end + + context "when an extra brace is present" do + let(:json) { '{{"foo": 1234}}}' } + + it "raises an exception" do + expect { parser }.to raise_error(FFI_Yajl::ParseError) + end + end + + context "when an extra bracket is present" do + let(:json) { '[["foo", "bar"]]]' } + + it "raises an exception" do + expect { parser }.to raise_error(FFI_Yajl::ParseError) + end + end end - it "parses floats with an exponent without a sign and a small e" do - json = '{"foo": 1.0973731568539e7 }' - expect(parser.parse(json)).to eql( "foo" => 1.0973731568539e+7 ) + context "when parsing heavy metal umlauts in keys" do + let(:json) { '{"München": "Bayern"}' } + + it "correctly parses" do + expect(parser).to eql( "München" => "Bayern" ) + end + end + + context "when parsing floats" do + context "parses simple floating point values" do + let(:json) { '{"foo": 3.14159265358979}' } + + it "correctly parses" do + expect(parser).to eql( "foo" => 3.14159265358979 ) + end + end + + context "parses simple negative floating point values" do + let(:json) { '{"foo":-2.00231930436153}' } + + it "correctly parses" do + expect(parser).to eql( "foo" => -2.00231930436153 ) + end + end + + context "parses floats with negative exponents and a large E" do + let(:json) { '{"foo": 1.602176565E-19}' } + + it "correctly parses" do + expect(parser).to eql( "foo" => 1.602176565e-19 ) + end + end + + context "parses floats with negative exponents and a small e" do + let(:json) { '{"foo": 6.6260689633e-34 }' } + + it "correctly parses" do + expect(parser).to eql( "foo" => 6.6260689633e-34 ) + end + end + + context "parses floats with positive exponents and a large E" do + let(:json) { '{"foo": 6.0221413E+23}' } + + it "correctly parses" do + expect(parser).to eql( "foo" => 6.0221413e+23 ) + end + end + + context "parses floats with positive exponents and a small e" do + let(:json) { '{"foo": 8.9875517873681764e+9 }' } + + it "correctly parses" do + expect(parser).to eql( "foo" => 8.9875517873681764e+9 ) + end + end + + context "parses floats with an exponent without a sign and a large E" do + let(:json) { '{"foo": 2.99792458E8 }' } + + it "correctly parses" do + expect(parser).to eql( "foo" => 2.99792458e+8 ) + end + end + + context "parses floats with an exponent without a sign and a small e" do + let(:json) { '{"foo": 1.0973731568539e7 }' } + + it "correctly parses" do + expect(parser).to eql( "foo" => 1.0973731568539e+7 ) + end + end + end + + context "when parsing big floats" do + let(:json) { '[0.' + '1' * 2**23 + ']' } + + it "parses" do + expect{ parser }.not_to raise_error + end + end + + context "when parsing long hash keys with symbolize_keys option" do + let(:json) { '{"' + 'a' * 2**23 + '": 0}' } + let(:options) { { :symbolize_keys => true } } + + it "parses" do + expect{ parser }.not_to raise_error + end end end - context "when parsing unicode in hash keys" do - it "handles heavy metal umlauts in keys" do - json = '{"München": "Bayern"}' - expect(parser.parse(json)).to eql( "München" => "Bayern" ) + context "when options are set to empty hash" do + let(:options) { {} } + + context "when using a parsing object" do + let(:parser) { FFI_Yajl::Parser.new(options).parse(json) } + + it_behaves_like "correct json parsing" + end + + context "when using the class method" do + let(:parser) { FFI_Yajl::Parser.parse(json, options) } + + it_behaves_like "correct json parsing" + end + end + + context "when options are set to nil" do + let(:options) { nil } + + context "when using a parsing object" do + let(:parser) { FFI_Yajl::Parser.new(options).parse(json) } + + it_behaves_like "correct json parsing" + end + + context "when using the class method" do + let(:parser) { FFI_Yajl::Parser.parse(json, options) } + + it_behaves_like "correct json parsing" + end + end + + context "when options default to nothing" do + let(:options) { nil } + + context "when using a parsing object" do + let(:parser) do + if options.nil? + FFI_Yajl::Parser.new.parse(json) + else + FFI_Yajl::Parser.new(options).parse(json) + end + end + + it_behaves_like "correct json parsing" + end + + context "when using the class method" do + let(:parser) do + if options.nil? + FFI_Yajl::Parser.parse(json) + else + FFI_Yajl::Parser.parse(json, options) + end + end + + it_behaves_like "correct json parsing" end end end -- cgit v1.2.1