diff --git a/t/mojo/json.t b/t/mojo/json.t index 095f811dd0..723f569ec7 100644 --- a/t/mojo/json.t +++ b/t/mojo/json.t @@ -21,88 +21,93 @@ use Mojo::JSON qw(decode_json encode_json false from_json j to_json true); use Mojo::Util qw(encode); use Scalar::Util qw(dualvar); -# Decode array -my $array = decode_json '[]'; -is_deeply $array, [], 'decode []'; -$array = decode_json '[ [ ]]'; -is_deeply $array, [[]], 'decode [ [ ]]'; - -# Decode number -$array = decode_json '[0]'; -is_deeply $array, [0], 'decode [0]'; -$array = decode_json '[1]'; -is_deeply $array, [1], 'decode [1]'; -$array = decode_json '[ "-122.026020" ]'; -is_deeply $array, ['-122.026020'], 'decode [ -122.026020 ]'; -$array = decode_json '[ -122.026020 ]'; -is_deeply $array, ['-122.02602'], 'decode [ -122.026020 ]'; -$array = decode_json '[0.0]'; -cmp_ok $array->[0], '==', 0, 'value is 0'; -$array = decode_json '[0e0]'; -cmp_ok $array->[0], '==', 0, 'value is 0'; -$array = decode_json '[1,-2]'; -is_deeply $array, [1, -2], 'decode [1,-2]'; -$array = decode_json '["10e12" , [2 ]]'; -is_deeply $array, ['10e12', [2]], 'decode ["10e12" , [2 ]]'; -$array = decode_json '[10e12 , [2 ]]'; -is_deeply $array, [10000000000000, [2]], 'decode [10e12 , [2 ]]'; -$array = decode_json '[37.7668 , [ 20 ]] '; -is_deeply $array, [37.7668, [20]], 'decode [37.7668 , [ 20 ]] '; -$array = decode_json '[1e3]'; -cmp_ok $array->[0], '==', 1e3, 'value is 1e3'; -my $value = decode_json '0'; -cmp_ok $value, '==', 0, 'decode 0'; -$value = decode_json '23.3'; -cmp_ok $value, '==', 23.3, 'decode 23.3'; - -# Decode name -$array = decode_json '[true]'; -is_deeply $array, [Mojo::JSON->true], 'decode [true]'; -$array = decode_json '[null]'; -is_deeply $array, [undef], 'decode [null]'; -$array = decode_json '[true, false]'; -is_deeply $array, [true, false], 'decode [true, false]'; -$value = decode_json 'true'; -is $value, Mojo::JSON->true, 'decode true'; -$value = decode_json 'false'; -is $value, Mojo::JSON->false, 'decode false'; -$value = decode_json 'null'; -is $value, undef, 'decode null'; - -# Decode string -$array = decode_json '[" "]'; -is_deeply $array, [' '], 'decode [" "]'; -$array = decode_json '["hello world!"]'; -is_deeply $array, ['hello world!'], 'decode ["hello world!"]'; -$array = decode_json '["hello\nworld!"]'; -is_deeply $array, ["hello\nworld!"], 'decode ["hello\nworld!"]'; -$array = decode_json '["hello\t\"world!"]'; -is_deeply $array, ["hello\t\"world!"], 'decode ["hello\t\"world!"]'; -$array = decode_json '["hello\u0152world\u0152!"]'; -is_deeply $array, ["hello\x{0152}world\x{0152}!"], 'decode ["hello\u0152world\u0152!"]'; -$array = decode_json '["0."]'; -is_deeply $array, ['0.'], 'decode ["0."]'; -$array = decode_json '[" 0"]'; -is_deeply $array, [' 0'], 'decode [" 0"]'; -$array = decode_json '["1"]'; -is_deeply $array, ['1'], 'decode ["1"]'; -$array = decode_json '["\u0007\b\/\f\r"]'; -is_deeply $array, ["\a\b/\f\r"], 'decode ["\u0007\b\/\f\r"]'; -$value = decode_json '""'; -is $value, '', 'decode ""'; -$value = decode_json '"hell\no"'; -is $value, "hell\no", 'decode "hell\no"'; - -# Decode object -my $hash = decode_json '{}'; -is_deeply $hash, {}, 'decode {}'; -$hash = decode_json '{"foo": "bar"}'; -is_deeply $hash, {foo => 'bar'}, 'decode {"foo": "bar"}'; -$hash = decode_json '{"foo": [23, "bar"]}'; -is_deeply $hash, {foo => [qw(23 bar)]}, 'decode {"foo": [23, "bar"]}'; - -# Decode full spec example -$hash = decode_json < sub { + my $array = decode_json '[]'; + is_deeply $array, [], 'decode []'; + $array = decode_json '[ [ ]]'; + is_deeply $array, [[]], 'decode [ [ ]]'; +}; + +subtest 'Decode number' => sub { + my $array = decode_json '[0]'; + is_deeply $array, [0], 'decode [0]'; + $array = decode_json '[1]'; + is_deeply $array, [1], 'decode [1]'; + $array = decode_json '[ "-122.026020" ]'; + is_deeply $array, ['-122.026020'], 'decode [ -122.026020 ]'; + $array = decode_json '[ -122.026020 ]'; + is_deeply $array, ['-122.02602'], 'decode [ -122.026020 ]'; + $array = decode_json '[0.0]'; + cmp_ok $array->[0], '==', 0, 'value is 0'; + $array = decode_json '[0e0]'; + cmp_ok $array->[0], '==', 0, 'value is 0'; + $array = decode_json '[1,-2]'; + is_deeply $array, [1, -2], 'decode [1,-2]'; + $array = decode_json '["10e12" , [2 ]]'; + is_deeply $array, ['10e12', [2]], 'decode ["10e12" , [2 ]]'; + $array = decode_json '[10e12 , [2 ]]'; + is_deeply $array, [10000000000000, [2]], 'decode [10e12 , [2 ]]'; + $array = decode_json '[37.7668 , [ 20 ]] '; + is_deeply $array, [37.7668, [20]], 'decode [37.7668 , [ 20 ]] '; + $array = decode_json '[1e3]'; + cmp_ok $array->[0], '==', 1e3, 'value is 1e3'; + my $value = decode_json '0'; + cmp_ok $value, '==', 0, 'decode 0'; + $value = decode_json '23.3'; + cmp_ok $value, '==', 23.3, 'decode 23.3'; +}; + +subtest 'Decode name' => sub { + my $array = decode_json '[true]'; + is_deeply $array, [Mojo::JSON->true], 'decode [true]'; + $array = decode_json '[null]'; + is_deeply $array, [undef], 'decode [null]'; + $array = decode_json '[true, false]'; + is_deeply $array, [true, false], 'decode [true, false]'; + my $value = decode_json 'true'; + is $value, Mojo::JSON->true, 'decode true'; + $value = decode_json 'false'; + is $value, Mojo::JSON->false, 'decode false'; + $value = decode_json 'null'; + is $value, undef, 'decode null'; +}; + +subtest 'Decode string' => sub { + my $array = decode_json '[" "]'; + is_deeply $array, [' '], 'decode [" "]'; + $array = decode_json '["hello world!"]'; + is_deeply $array, ['hello world!'], 'decode ["hello world!"]'; + $array = decode_json '["hello\nworld!"]'; + is_deeply $array, ["hello\nworld!"], 'decode ["hello\nworld!"]'; + $array = decode_json '["hello\t\"world!"]'; + is_deeply $array, ["hello\t\"world!"], 'decode ["hello\t\"world!"]'; + $array = decode_json '["hello\u0152world\u0152!"]'; + is_deeply $array, ["hello\x{0152}world\x{0152}!"], 'decode ["hello\u0152world\u0152!"]'; + $array = decode_json '["0."]'; + is_deeply $array, ['0.'], 'decode ["0."]'; + $array = decode_json '[" 0"]'; + is_deeply $array, [' 0'], 'decode [" 0"]'; + $array = decode_json '["1"]'; + is_deeply $array, ['1'], 'decode ["1"]'; + $array = decode_json '["\u0007\b\/\f\r"]'; + is_deeply $array, ["\a\b/\f\r"], 'decode ["\u0007\b\/\f\r"]'; + my $value = decode_json '""'; + is $value, '', 'decode ""'; + $value = decode_json '"hell\no"'; + is $value, "hell\no", 'decode "hell\no"'; +}; + +subtest 'Decode object' => sub { + my $hash = decode_json '{}'; + is_deeply $hash, {}, 'decode {}'; + $hash = decode_json '{"foo": "bar"}'; + is_deeply $hash, {foo => 'bar'}, 'decode {"foo": "bar"}'; + $hash = decode_json '{"foo": [23, "bar"]}'; + is_deeply $hash, {foo => [qw(23 bar)]}, 'decode {"foo": [23, "bar"]}'; +}; + +subtest 'Decode full spec example' => sub { + my $hash = decode_json <{Image}{Width}, 800, 'right value'; -is $hash->{Image}{Height}, 600, 'right value'; -is $hash->{Image}{Title}, 'View from 15th Floor', 'right value'; -is $hash->{Image}{Thumbnail}{Url}, 'http://www.example.com/image/481989943', 'right value'; -is $hash->{Image}{Thumbnail}{Height}, 125, 'right value'; -is $hash->{Image}{Thumbnail}{Width}, 100, 'right value'; -is $hash->{Image}{IDs}[0], 116, 'right value'; -is $hash->{Image}{IDs}[1], 943, 'right value'; -is $hash->{Image}{IDs}[2], 234, 'right value'; -is $hash->{Image}{IDs}[3], 38793, 'right value'; - -# Encode array -my $bytes = encode_json []; -is $bytes, '[]', 'encode []'; -$bytes = encode_json [[]]; -is $bytes, '[[]]', 'encode [[]]'; -$bytes = encode_json [[], []]; -is $bytes, '[[],[]]', 'encode [[], []]'; -$bytes = encode_json [[], [[]], []]; -is $bytes, '[[],[[]],[]]', 'encode [[], [[]], []]'; - -# Encode string -$bytes = encode_json ['foo']; -is $bytes, '["foo"]', 'encode [\'foo\']'; -$bytes = encode_json ["hello\nworld!"]; -is $bytes, '["hello\nworld!"]', 'encode ["hello\nworld!"]'; -$bytes = encode_json ["hello\t\"world!"]; -is $bytes, '["hello\t\"world!"]', 'encode ["hello\t\"world!"]'; -$bytes = encode_json ["hello\x{0003}\x{0152}world\x{0152}!"]; -is b($bytes)->decode('UTF-8'), "[\"hello\\u0003\x{0152}world\x{0152}!\"]", - 'encode ["hello\x{0003}\x{0152}world\x{0152}!"]'; -$bytes = encode_json ["123abc"]; -is $bytes, '["123abc"]', 'encode ["123abc"]'; -$bytes = encode_json ["\x00\x1f \a\b/\f\r"]; -is $bytes, '["\\u0000\\u001F \\u0007\\b\/\f\r"]', 'encode ["\x00\x1f \a\b/\f\r"]'; -$bytes = encode_json ''; -is $bytes, '""', 'encode ""'; -$bytes = encode_json "hell\no"; -is $bytes, '"hell\no"', 'encode "hell\no"'; - -# Encode object -$bytes = encode_json {}; -is $bytes, '{}', 'encode {}'; -$bytes = encode_json {foo => {}}; -is $bytes, '{"foo":{}}', 'encode {foo => {}}'; -$bytes = encode_json {foo => 'bar'}; -is $bytes, '{"foo":"bar"}', 'encode {foo => \'bar\'}'; -$bytes = encode_json {foo => []}; -is $bytes, '{"foo":[]}', 'encode {foo => []}'; -$bytes = encode_json {foo => ['bar']}; -is $bytes, '{"foo":["bar"]}', 'encode {foo => [\'bar\']}'; -$bytes = encode_json {foo => 'bar', baz => 'yada'}; -is $bytes, '{"baz":"yada","foo":"bar"}', 'encode {foo => \'bar\', baz => \'yada\'}'; - -# Encode name -$bytes = encode_json [Mojo::JSON->true]; -is $bytes, '[true]', 'encode [Mojo::JSON->true]'; -$bytes = encode_json [undef]; -is $bytes, '[null]', 'encode [undef]'; -$bytes = encode_json [Mojo::JSON->true, Mojo::JSON->false]; -is $bytes, '[true,false]', 'encode [Mojo::JSON->true, Mojo::JSON->false]'; -$bytes = encode_json(Mojo::JSON->true); -is $bytes, 'true', 'encode Mojo::JSON->true'; -$bytes = encode_json(Mojo::JSON->false); -is $bytes, 'false', 'encode Mojo::JSON->false'; -$bytes = encode_json undef; -is $bytes, 'null', 'encode undef'; - -# Encode number -$bytes = encode_json [1]; -is $bytes, '[1]', 'encode [1]'; -$bytes = encode_json ["1"]; -is $bytes, '["1"]', 'encode ["1"]'; -$bytes = encode_json ['-122.026020']; -is $bytes, '["-122.026020"]', 'encode [\'-122.026020\']'; -$bytes = encode_json [-122.026020]; -is $bytes, '[-122.02602]', 'encode [-122.026020]'; -$bytes = encode_json [1, -2]; -is $bytes, '[1,-2]', 'encode [1, -2]'; -$bytes = encode_json ['10e12', [2]]; -is $bytes, '["10e12",[2]]', 'encode [\'10e12\', [2]]'; -$bytes = encode_json [10e12, [2]]; -is $bytes, '[10000000000000,[2]]', 'encode [10e12, [2]]'; -$bytes = encode_json [37.7668, [20]]; -is $bytes, '[37.7668,[20]]', 'encode [37.7668, [20]]'; -$bytes = encode_json 0; -is $bytes, '0', 'encode 0'; -$bytes = encode_json 23.3; -is $bytes, '23.3', 'encode 23.3'; - -# Faihu roundtrip -$bytes = j(["\x{10346}"]); -is b($bytes)->decode('UTF-8'), "[\"\x{10346}\"]", 'encode ["\x{10346}"]'; -$array = j($bytes); -is_deeply $array, ["\x{10346}"], 'successful roundtrip'; - -# Decode faihu surrogate pair -$array = decode_json '["\\ud800\\udf46"]'; -is_deeply $array, ["\x{10346}"], 'decode [\"\\ud800\\udf46\"]'; - -# Complicated roundtrips -$bytes = '{"":""}'; -$hash = decode_json $bytes; -is_deeply $hash, {'' => ''}, 'decode {"":""}'; -is encode_json($hash), $bytes, 're-encode'; -$bytes = '[null,false,true,"",0,1]'; -$array = decode_json $bytes; -is_deeply $array, [undef, Mojo::JSON->false, Mojo::JSON->true, '', 0, 1], 'decode [null,false,true,"",0,1]'; -is encode_json($array), $bytes, 're-encode'; -$array = [undef, 0, 1, '', Mojo::JSON->true, Mojo::JSON->false]; -$bytes = encode_json($array); -ok $bytes, 'defined value'; -is_deeply decode_json($bytes), $array, 'successful roundtrip'; - -# Real world roundtrip -$bytes = encode_json({foo => 'c:\progra~1\mozill~1\firefox.exe'}); -is $bytes, '{"foo":"c:\\\\progra~1\\\\mozill~1\\\\firefox.exe"}', - 'encode {foo => \'c:\progra~1\mozill~1\firefox.exe\'}'; -$hash = decode_json $bytes; -is_deeply $hash, {foo => 'c:\progra~1\mozill~1\firefox.exe'}, 'successful roundtrip'; - -# Huge string -$bytes = encode_json(['a' x 32768]); -is_deeply decode_json($bytes), ['a' x 32768], 'successful roundtrip'; - -# Slash -$bytes = encode_json ['123']; -is $bytes, '["123<\/script>"]', 'escaped slash'; -is_deeply decode_json($bytes), ['123'], 'successful roundtrip'; - -# JSON without UTF-8 encoding -is_deeply from_json('["♥"]'), ['♥'], 'characters decoded'; -is to_json(['♥']), '["♥"]', 'characters encoded'; -is_deeply from_json(to_json(["\xe9"])), ["\xe9"], 'successful roundtrip'; - -# Blessed reference -$bytes = encode_json [b('test')]; -is_deeply decode_json($bytes), ['test'], 'successful roundtrip'; - -# Blessed reference with TO_JSON method -$bytes = encode_json(JSONTest->new); -is_deeply decode_json($bytes), {}, 'successful roundtrip'; -$bytes = encode_json(JSONTest->new(something => {just => 'works'}, else => {not => 'working'})); -is_deeply decode_json($bytes), {just => 'works'}, 'successful roundtrip'; - -# Unknown reference -is_deeply encode_json(sub { }), 'null', 'unknown reference'; - -# Boolean shortcut -is encode_json({true => \1}), '{"true":true}', 'encode {true => \1}'; -is encode_json({false => \0}), '{"false":false}', 'encode {false => \0}'; - -# Booleans in different contexts -ok true, 'true'; -is true, 1, 'right string value'; -is true + 0, 1, 'right numeric value'; -ok !false, 'false'; -is false, 0, 'right string value'; -is false + 0, 0, 'right numeric value'; - -# Upgraded numbers -my $num = 3; -my $str = "$num"; -is encode_json({test => [$num, $str]}), '{"test":[3,"3"]}', 'upgraded number detected'; -$num = 3.21; -$str = "$num"; -is encode_json({test => [$num, $str]}), '{"test":[3.21,"3.21"]}', 'upgraded number detected'; -$str = '0 but true'; -$num = 1 + $str; -is encode_json({test => [$num, $str]}), '{"test":[1,"0 but true"]}', 'upgraded number detected'; - -# Upgraded string -$str = "bar"; -{ no warnings 'numeric'; $num = 23 + $str } -is encode_json({test => [$num, $str]}), '{"test":[23,"bar"]}', 'upgraded string detected'; - -# dualvar -my $dual = dualvar 23, 'twenty three'; -is encode_json([$dual]), '["twenty three"]', 'dualvar stringified'; - -# Other reference types -is encode_json([JSONTest2->new]), "[\"works!\"]", 'object stringified'; - -# Ensure numbers and strings are not upgraded -my $mixed = [3, 'three', '3', 0, "0"]; -is encode_json($mixed), '[3,"three","3",0,"0"]', 'all have been detected correctly'; -is encode_json($mixed), '[3,"three","3",0,"0"]', 'all have been detected correctly again'; - -# "inf" and "nan" -like encode_json({test => 9**9**9}), qr/^{"test":".*"}$/, 'encode "inf" as string'; -like encode_json({test => -sin(9**9**9)}), qr/^{"test":".*"}$/, 'encode "nan" as string'; - -# "null" -is j('null'), undef, 'decode null'; - -# Errors -eval { decode_json 'test' }; -like $@, qr/Malformed JSON: Expected string, array, object/, 'right error'; -like $@, qr/object, number, boolean or null at line 0, offset 0/, 'right error'; -eval { decode_json b('["\\ud800"]')->encode }; -like $@, qr/Malformed JSON: Missing low-surrogate at line 1, offset 8/, 'right error'; -eval { decode_json b('["\\udf46"]')->encode }; -like $@, qr/Malformed JSON: Missing high-surrogate at line 1, offset 8/, 'right error'; -eval { decode_json '[[]' }; -like $@, qr/Malformed JSON: Expected comma or right square bracket/, 'right error'; -like $@, qr/bracket while parsing array at line 1, offset 3/, 'right error'; -eval { decode_json '{{}' }; -like $@, qr/Malformed JSON: Expected string while parsing object at line 1, offset 1/, 'right error'; -eval { decode_json "[\"foo\x00]" }; -like $@, qr/Malformed JSON: Unexpected character or invalid escape/, 'right error'; -like $@, qr/escape while parsing string at line 1, offset 5/, 'right error'; -eval { decode_json '{"foo":"bar"{' }; -like $@, qr/Malformed JSON: Expected comma or right curly bracket/, 'right error'; -like $@, qr/bracket while parsing object at line 1, offset 12/, 'right error'; -eval { decode_json '{"foo""bar"}' }; -like $@, qr/Malformed JSON: Expected colon while parsing object at line 1, offset 6/, 'right error'; -eval { decode_json '[[]...' }; -like $@, qr/Malformed JSON: Expected comma or right square bracket/, 'right error'; -like $@, qr/bracket while parsing array at line 1, offset 3/, 'right error'; -eval { decode_json '{{}...' }; -like $@, qr/Malformed JSON: Expected string while parsing object at line 1, offset 1/, 'right error'; -eval { decode_json '[nan]' }; -like $@, qr/Malformed JSON: Expected string, array, object, number/, 'right error'; -like $@, qr/number, boolean or null at line 1, offset 1/, 'right error'; -eval { decode_json '["foo]' }; -like $@, qr/Malformed JSON: Unterminated string at line 1, offset 6/, 'right error'; -eval { decode_json '{"foo":"bar"}lala' }; -like $@, qr/Malformed JSON: Unexpected data at line 1, offset 13/, 'right error'; -eval { decode_json '' }; -like $@, qr/Missing or empty input at offset 0/, 'right error'; -eval { decode_json "[\"foo\",\n\"bar\"]lala" }; -like $@, qr/Malformed JSON: Unexpected data at line 2, offset 6/, 'right error'; -eval { decode_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" }; -like $@, qr/Malformed JSON: Unexpected data at line 3, offset 8/, 'right error'; -eval { decode_json '["♥"]' }; -like $@, qr/Input is not UTF-8 encoded/, 'right error'; -eval { decode_json encode('Shift_JIS', 'やった') }; -like $@, qr/Input is not UTF-8 encoded/, 'right error'; -is j('{'), undef, 'syntax error'; -eval { decode_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" }; -like $@, qr/JSON: Unexpected data at line 3, offset 8 at.*json\.t/, 'right error'; -eval { from_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" }; -like $@, qr/JSON: Unexpected data at line 3, offset 8 at.*json\.t/, 'right error'; + is $hash->{Image}{Width}, 800, 'right value'; + is $hash->{Image}{Height}, 600, 'right value'; + is $hash->{Image}{Title}, 'View from 15th Floor', 'right value'; + is $hash->{Image}{Thumbnail}{Url}, 'http://www.example.com/image/481989943', 'right value'; + is $hash->{Image}{Thumbnail}{Height}, 125, 'right value'; + is $hash->{Image}{Thumbnail}{Width}, 100, 'right value'; + is $hash->{Image}{IDs}[0], 116, 'right value'; + is $hash->{Image}{IDs}[1], 943, 'right value'; + is $hash->{Image}{IDs}[2], 234, 'right value'; + is $hash->{Image}{IDs}[3], 38793, 'right value'; +}; + +subtest 'Encode array' => sub { + my $bytes = encode_json []; + is $bytes, '[]', 'encode []'; + $bytes = encode_json [[]]; + is $bytes, '[[]]', 'encode [[]]'; + $bytes = encode_json [[], []]; + is $bytes, '[[],[]]', 'encode [[], []]'; + $bytes = encode_json [[], [[]], []]; + is $bytes, '[[],[[]],[]]', 'encode [[], [[]], []]'; +}; + +subtest 'Encode string' => sub { + my $bytes = encode_json ['foo']; + is $bytes, '["foo"]', 'encode [\'foo\']'; + $bytes = encode_json ["hello\nworld!"]; + is $bytes, '["hello\nworld!"]', 'encode ["hello\nworld!"]'; + $bytes = encode_json ["hello\t\"world!"]; + is $bytes, '["hello\t\"world!"]', 'encode ["hello\t\"world!"]'; + $bytes = encode_json ["hello\x{0003}\x{0152}world\x{0152}!"]; + is b($bytes)->decode('UTF-8'), "[\"hello\\u0003\x{0152}world\x{0152}!\"]", + 'encode ["hello\x{0003}\x{0152}world\x{0152}!"]'; + $bytes = encode_json ["123abc"]; + is $bytes, '["123abc"]', 'encode ["123abc"]'; + $bytes = encode_json ["\x00\x1f \a\b/\f\r"]; + is $bytes, '["\\u0000\\u001F \\u0007\\b\/\f\r"]', 'encode ["\x00\x1f \a\b/\f\r"]'; + $bytes = encode_json ''; + is $bytes, '""', 'encode ""'; + $bytes = encode_json "hell\no"; + is $bytes, '"hell\no"', 'encode "hell\no"'; +}; + +subtest 'Encode object' => sub { + my $bytes = encode_json {}; + is $bytes, '{}', 'encode {}'; + $bytes = encode_json {foo => {}}; + is $bytes, '{"foo":{}}', 'encode {foo => {}}'; + $bytes = encode_json {foo => 'bar'}; + is $bytes, '{"foo":"bar"}', 'encode {foo => \'bar\'}'; + $bytes = encode_json {foo => []}; + is $bytes, '{"foo":[]}', 'encode {foo => []}'; + $bytes = encode_json {foo => ['bar']}; + is $bytes, '{"foo":["bar"]}', 'encode {foo => [\'bar\']}'; + $bytes = encode_json {foo => 'bar', baz => 'yada'}; + is $bytes, '{"baz":"yada","foo":"bar"}', 'encode {foo => \'bar\', baz => \'yada\'}'; +}; + +subtest 'Encode name' => sub { + my $bytes = encode_json [Mojo::JSON->true]; + is $bytes, '[true]', 'encode [Mojo::JSON->true]'; + $bytes = encode_json [undef]; + is $bytes, '[null]', 'encode [undef]'; + $bytes = encode_json [Mojo::JSON->true, Mojo::JSON->false]; + is $bytes, '[true,false]', 'encode [Mojo::JSON->true, Mojo::JSON->false]'; + $bytes = encode_json(Mojo::JSON->true); + is $bytes, 'true', 'encode Mojo::JSON->true'; + $bytes = encode_json(Mojo::JSON->false); + is $bytes, 'false', 'encode Mojo::JSON->false'; + $bytes = encode_json undef; + is $bytes, 'null', 'encode undef'; +}; + +subtest 'Encode number' => sub { + my $bytes = encode_json [1]; + is $bytes, '[1]', 'encode [1]'; + $bytes = encode_json ["1"]; + is $bytes, '["1"]', 'encode ["1"]'; + $bytes = encode_json ['-122.026020']; + is $bytes, '["-122.026020"]', 'encode [\'-122.026020\']'; + $bytes = encode_json [-122.026020]; + is $bytes, '[-122.02602]', 'encode [-122.026020]'; + $bytes = encode_json [1, -2]; + is $bytes, '[1,-2]', 'encode [1, -2]'; + $bytes = encode_json ['10e12', [2]]; + is $bytes, '["10e12",[2]]', 'encode [\'10e12\', [2]]'; + $bytes = encode_json [10e12, [2]]; + is $bytes, '[10000000000000,[2]]', 'encode [10e12, [2]]'; + $bytes = encode_json [37.7668, [20]]; + is $bytes, '[37.7668,[20]]', 'encode [37.7668, [20]]'; + $bytes = encode_json 0; + is $bytes, '0', 'encode 0'; + $bytes = encode_json 23.3; + is $bytes, '23.3', 'encode 23.3'; +}; + +subtest 'Faihu roundtrip' => sub { + my $bytes = j(["\x{10346}"]); + is b($bytes)->decode('UTF-8'), "[\"\x{10346}\"]", 'encode ["\x{10346}"]'; + my $array = j($bytes); + is_deeply $array, ["\x{10346}"], 'successful roundtrip'; +}; + +subtest 'Decode faihu surrogate pair' => sub { + my $array = decode_json '["\\ud800\\udf46"]'; + is_deeply $array, ["\x{10346}"], 'decode [\"\\ud800\\udf46\"]'; +}; + +subtest 'Complicated roundtrips' => sub { + my $bytes = '{"":""}'; + my $hash = decode_json $bytes; + is_deeply $hash, {'' => ''}, 'decode {"":""}'; + is encode_json($hash), $bytes, 're-encode'; + $bytes = '[null,false,true,"",0,1]'; + my $array = decode_json $bytes; + is_deeply $array, [undef, Mojo::JSON->false, Mojo::JSON->true, '', 0, 1], 'decode [null,false,true,"",0,1]'; + is encode_json($array), $bytes, 're-encode'; + $array = [undef, 0, 1, '', Mojo::JSON->true, Mojo::JSON->false]; + $bytes = encode_json($array); + ok $bytes, 'defined value'; + is_deeply decode_json($bytes), $array, 'successful roundtrip'; +}; + +subtest 'Real world roundtrip' => sub { + my $bytes = encode_json({foo => 'c:\progra~1\mozill~1\firefox.exe'}); + is $bytes, '{"foo":"c:\\\\progra~1\\\\mozill~1\\\\firefox.exe"}', + 'encode {foo => \'c:\progra~1\mozill~1\firefox.exe\'}'; + my $hash = decode_json $bytes; + is_deeply $hash, {foo => 'c:\progra~1\mozill~1\firefox.exe'}, 'successful roundtrip'; +}; + +subtest 'Huge string' => sub { + my $bytes = encode_json(['a' x 32768]); + is_deeply decode_json($bytes), ['a' x 32768], 'successful roundtrip'; +}; + +subtest 'Slash' => sub { + my $bytes = encode_json ['123']; + is $bytes, '["123<\/script>"]', 'escaped slash'; + is_deeply decode_json($bytes), ['123'], 'successful roundtrip'; +}; + +subtest 'JSON without UTF-8 encoding' => sub { + is_deeply from_json('["♥"]'), ['♥'], 'characters decoded'; + is to_json(['♥']), '["♥"]', 'characters encoded'; + is_deeply from_json(to_json(["\xe9"])), ["\xe9"], 'successful roundtrip'; +}; + +subtest 'Blessed reference' => sub { + my $bytes = encode_json [b('test')]; + is_deeply decode_json($bytes), ['test'], 'successful roundtrip'; +}; + +subtest 'Blessed reference with TO_JSON method' => sub { + my $bytes = encode_json(JSONTest->new); + is_deeply decode_json($bytes), {}, 'successful roundtrip'; + $bytes = encode_json(JSONTest->new(something => {just => 'works'}, else => {not => 'working'})); + is_deeply decode_json($bytes), {just => 'works'}, 'successful roundtrip'; +}; + +subtest 'Unknown reference' => sub { + is_deeply encode_json(sub { }), 'null', 'unknown reference'; +}; + +subtest 'Boolean shortcut' => sub { + is encode_json({true => \1}), '{"true":true}', 'encode {true => \1}'; + is encode_json({false => \0}), '{"false":false}', 'encode {false => \0}'; +}; + +subtest 'Booleans in different contexts' => sub { + ok true, 'true'; + is true, 1, 'right string value'; + is true + 0, 1, 'right numeric value'; + ok !false, 'false'; + is false, 0, 'right string value'; + is false + 0, 0, 'right numeric value'; +}; + +subtest 'Upgraded numbers' => sub { + my $num = 3; + my $str = "$num"; + is encode_json({test => [$num, $str]}), '{"test":[3,"3"]}', 'upgraded number detected'; + $num = 3.21; + $str = "$num"; + is encode_json({test => [$num, $str]}), '{"test":[3.21,"3.21"]}', 'upgraded number detected'; + $str = '0 but true'; + $num = 1 + $str; + is encode_json({test => [$num, $str]}), '{"test":[1,"0 but true"]}', 'upgraded number detected'; +}; + +subtest 'Upgraded string' => sub { + my $str = "bar"; + my $num; + { no warnings 'numeric'; $num = 23 + $str } + is encode_json({test => [$num, $str]}), '{"test":[23,"bar"]}', 'upgraded string detected'; +}; + +subtest 'dualvar' => sub { + my $dual = dualvar 23, 'twenty three'; + is encode_json([$dual]), '["twenty three"]', 'dualvar stringified'; +}; + +subtest 'Other reference types' => sub { + is encode_json([JSONTest2->new]), "[\"works!\"]", 'object stringified'; +}; + +subtest 'Ensure numbers and strings are not upgraded' => sub { + my $mixed = [3, 'three', '3', 0, "0"]; + is encode_json($mixed), '[3,"three","3",0,"0"]', 'all have been detected correctly'; + is encode_json($mixed), '[3,"three","3",0,"0"]', 'all have been detected correctly again'; +}; + +subtest '"inf" and "nan"' => sub { + like encode_json({test => 9**9**9}), qr/^{"test":".*"}$/, 'encode "inf" as string'; + like encode_json({test => -sin(9**9**9)}), qr/^{"test":".*"}$/, 'encode "nan" as string'; +}; + +subtest '"null"' => sub { + is j('null'), undef, 'decode null'; +}; + +subtest 'Errors' => sub { + eval { decode_json 'test' }; + like $@, qr/Malformed JSON: Expected string, array, object/, 'right error'; + like $@, qr/object, number, boolean or null at line 0, offset 0/, 'right error'; + eval { decode_json b('["\\ud800"]')->encode }; + like $@, qr/Malformed JSON: Missing low-surrogate at line 1, offset 8/, 'right error'; + eval { decode_json b('["\\udf46"]')->encode }; + like $@, qr/Malformed JSON: Missing high-surrogate at line 1, offset 8/, 'right error'; + eval { decode_json '[[]' }; + like $@, qr/Malformed JSON: Expected comma or right square bracket/, 'right error'; + like $@, qr/bracket while parsing array at line 1, offset 3/, 'right error'; + eval { decode_json '{{}' }; + like $@, qr/Malformed JSON: Expected string while parsing object at line 1, offset 1/, 'right error'; + eval { decode_json "[\"foo\x00]" }; + like $@, qr/Malformed JSON: Unexpected character or invalid escape/, 'right error'; + like $@, qr/escape while parsing string at line 1, offset 5/, 'right error'; + eval { decode_json '{"foo":"bar"{' }; + like $@, qr/Malformed JSON: Expected comma or right curly bracket/, 'right error'; + like $@, qr/bracket while parsing object at line 1, offset 12/, 'right error'; + eval { decode_json '{"foo""bar"}' }; + like $@, qr/Malformed JSON: Expected colon while parsing object at line 1, offset 6/, 'right error'; + eval { decode_json '[[]...' }; + like $@, qr/Malformed JSON: Expected comma or right square bracket/, 'right error'; + like $@, qr/bracket while parsing array at line 1, offset 3/, 'right error'; + eval { decode_json '{{}...' }; + like $@, qr/Malformed JSON: Expected string while parsing object at line 1, offset 1/, 'right error'; + eval { decode_json '[nan]' }; + like $@, qr/Malformed JSON: Expected string, array, object, number/, 'right error'; + like $@, qr/number, boolean or null at line 1, offset 1/, 'right error'; + eval { decode_json '["foo]' }; + like $@, qr/Malformed JSON: Unterminated string at line 1, offset 6/, 'right error'; + eval { decode_json '{"foo":"bar"}lala' }; + like $@, qr/Malformed JSON: Unexpected data at line 1, offset 13/, 'right error'; + eval { decode_json '' }; + like $@, qr/Missing or empty input at offset 0/, 'right error'; + eval { decode_json "[\"foo\",\n\"bar\"]lala" }; + like $@, qr/Malformed JSON: Unexpected data at line 2, offset 6/, 'right error'; + eval { decode_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" }; + like $@, qr/Malformed JSON: Unexpected data at line 3, offset 8/, 'right error'; + eval { decode_json '["♥"]' }; + like $@, qr/Input is not UTF-8 encoded/, 'right error'; + eval { decode_json encode('Shift_JIS', 'やった') }; + like $@, qr/Input is not UTF-8 encoded/, 'right error'; + is j('{'), undef, 'syntax error'; + eval { decode_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" }; + like $@, qr/JSON: Unexpected data at line 3, offset 8 at.*json\.t/, 'right error'; + eval { from_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" }; + like $@, qr/JSON: Unexpected data at line 3, offset 8 at.*json\.t/, 'right error'; +}; done_testing(); diff --git a/t/mojo/json_xs.t b/t/mojo/json_xs.t index 4ac88195f1..c6ffeecfea 100644 --- a/t/mojo/json_xs.t +++ b/t/mojo/json_xs.t @@ -19,54 +19,64 @@ package main; use Mojo::ByteStream; use Mojo::Util qw(decode encode); -# Basics -my $array = decode_json '[]'; -is_deeply $array, [], 'decode_json'; -my $bytes = encode_json []; -is $bytes, '[]', 'encode_json'; -$array = from_json '[]'; -is_deeply $array, [], 'from_json'; -my $chars = to_json []; -is $chars, '[]', 'to_json'; -$array = j('[]'); -is_deeply $array, [], 'j() decode'; -$bytes = j([]); -is $bytes, '[]', 'j() encode'; -is encode_json([true]), '[true]', 'true'; -is encode_json([false]), '[false]', 'false'; - -# "utf8" -is_deeply decode_json(encode('UTF-8', '["♥"]')), ['♥'], 'bytes decoded'; -is encode_json(['♥']), encode('UTF-8', '["♥"]'), 'bytes encoded'; -is_deeply from_json('["♥"]'), ['♥'], 'characters decoded'; -is to_json(['♥']), '["♥"]', 'characters encoded'; - -# "canonical" -is_deeply encode_json({a => 1, b => 2, c => 3}), '{"a":1,"b":2,"c":3}', 'canonical object'; - -# "allow_nonref" -is_deeply encode_json(true), 'true', 'bare true'; - -# "allow_unknown" -is_deeply encode_json(sub { }), 'null', 'unknown reference'; - -# "allow_blessed" -is_deeply encode_json(Mojo::ByteStream->new('test')), '"test"', 'blessed reference'; - -# "convert_blessed" -$bytes = encode_json(JSONTest->new); -is_deeply decode_json($bytes), {}, 'successful roundtrip'; -$bytes = encode_json(JSONTest->new(something => {just => 'works'}, else => {not => 'working'})); -is_deeply decode_json($bytes), {just => 'works'}, 'successful roundtrip'; - -# "stringify_infnan" -like encode_json({test => 9**9**9}), qr/^{"test":".*"}$/, 'encode "inf" as string'; -like encode_json({test => -sin(9**9**9)}), qr/^{"test":".*"}$/, 'encode "nan" as string'; - -# "escape_slash" -is_deeply encode_json('/test/123'), '"\/test\/123"', 'escaped slash'; - -# "allow_dupkeys" -is_deeply decode_json('{"test":1,"test":2}'), {test => 2}, 'no duplicate keys error'; +subtest 'Basics' => sub { + my $array = decode_json '[]'; + is_deeply $array, [], 'decode_json'; + my $bytes = encode_json []; + is $bytes, '[]', 'encode_json'; + $array = from_json '[]'; + is_deeply $array, [], 'from_json'; + my $chars = to_json []; + is $chars, '[]', 'to_json'; + $array = j('[]'); + is_deeply $array, [], 'j() decode'; + $bytes = j([]); + is $bytes, '[]', 'j() encode'; + is encode_json([true]), '[true]', 'true'; + is encode_json([false]), '[false]', 'false'; +}; + +subtest '"utf8"' => sub { + is_deeply decode_json(encode('UTF-8', '["♥"]')), ['♥'], 'bytes decoded'; + is encode_json(['♥']), encode('UTF-8', '["♥"]'), 'bytes encoded'; + is_deeply from_json('["♥"]'), ['♥'], 'characters decoded'; + is to_json(['♥']), '["♥"]', 'characters encoded'; +}; + +subtest '"canonical"' => sub { + is_deeply encode_json({a => 1, b => 2, c => 3}), '{"a":1,"b":2,"c":3}', 'canonical object'; +}; + +subtest '"allow_nonref"' => sub { + is_deeply encode_json(true), 'true', 'bare true'; +}; + +subtest '"allow_unknown"' => sub { + is_deeply encode_json(sub { }), 'null', 'unknown reference'; +}; + +subtest '"allow_blessed"' => sub { + is_deeply encode_json(Mojo::ByteStream->new('test')), '"test"', 'blessed reference'; +}; + +subtest '"convert_blessed"' => sub { + my $bytes = encode_json(JSONTest->new); + is_deeply decode_json($bytes), {}, 'successful roundtrip'; + $bytes = encode_json(JSONTest->new(something => {just => 'works'}, else => {not => 'working'})); + is_deeply decode_json($bytes), {just => 'works'}, 'successful roundtrip'; +}; + +subtest '"stringify_infnan"' => sub { + like encode_json({test => 9**9**9}), qr/^{"test":".*"}$/, 'encode "inf" as string'; + like encode_json({test => -sin(9**9**9)}), qr/^{"test":".*"}$/, 'encode "nan" as string'; +}; + +subtest '"escape_slash"' => sub { + is_deeply encode_json('/test/123'), '"\/test\/123"', 'escaped slash'; +}; + +subtest '"allow_dupkeys"' => sub { + is_deeply decode_json('{"test":1,"test":2}'), {test => 2}, 'no duplicate keys error'; +}; done_testing(); diff --git a/t/mojo/url.t b/t/mojo/url.t index ac29e9b839..9f79d2f796 100644 --- a/t/mojo/url.t +++ b/t/mojo/url.t @@ -3,710 +3,748 @@ use Mojo::Base -strict; use Test::More; use Mojo::URL; -# Simple -my $url = Mojo::URL->new('HtTp://Example.Com'); -is $url->scheme, 'HtTp', 'right scheme'; -is $url->protocol, 'http', 'right protocol'; -is $url->host, 'Example.Com', 'right host'; -is $url->ihost, 'Example.Com', 'right internationalized host'; -is "$url", 'http://Example.Com', 'right format'; - -# Advanced -$url = Mojo::URL->new('https://sri:foobar@example.com:8080/x/index.html?monkey=biz&foo=1#/!%?@3'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'https', 'right scheme'; -is $url->protocol, 'https', 'right protocol'; -is $url->userinfo, 'sri:foobar', 'right userinfo'; -is $url->username, 'sri', 'right username'; -is $url->password, 'foobar', 'right password'; -is $url->host, 'example.com', 'right host'; -is $url->port, '8080', 'right port'; -is $url->path, '/x/index.html', 'right path'; -is $url->query, 'monkey=biz&foo=1', 'right query'; -is $url->path_query, '/x/index.html?monkey=biz&foo=1', 'right path and query'; -is $url->fragment, '/!%?@3', 'right fragment'; -is "$url", 'https://example.com:8080/x/index.html?monkey=biz&foo=1#/!%25?@3', 'right format'; -$url->path('/index.xml'); -is "$url", 'https://example.com:8080/index.xml?monkey=biz&foo=1#/!%25?@3', 'right format'; - -# Advanced userinfo and fragment roundtrip -$url = Mojo::URL->new('ws://AZaz09-._~!$&\'()*+,;=:@localhost#AZaz09-._~!$&\'()*+,;=:@/?'); -is $url->scheme, 'ws', 'right scheme'; -is $url->userinfo, 'AZaz09-._~!$&\'()*+,;=:', 'right userinfo'; -is $url->username, 'AZaz09-._~!$&\'()*+,;=', 'right username'; -is $url->password, '', 'right password'; -is $url->host, 'localhost', 'right host'; -is $url->fragment, 'AZaz09-._~!$&\'()*+,;=:@/?', 'right fragment'; -is "$url", 'ws://localhost#AZaz09-._~!$&\'()*+,;=:@/?', 'right format'; -is $url->to_unsafe_string, 'ws://AZaz09-._~!$&\'()*+,;=:@localhost#AZaz09-._~!$&\'()*+,;=:@/?', 'right format'; - -# Parameters -$url = Mojo::URL->new('http://sri:foobar@example.com:8080?_monkey=biz%3B&_monkey=23#23'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, 'sri:foobar', 'right userinfo'; -is $url->host, 'example.com', 'right host'; -is $url->port, '8080', 'right port'; -is $url->path, '', 'no path'; -is $url->query, '_monkey=biz%3B&_monkey=23', 'right query'; -is_deeply $url->query->to_hash, {_monkey => ['biz;', 23]}, 'right structure'; -is $url->fragment, '23', 'right fragment'; -is "$url", 'http://example.com:8080?_monkey=biz%3B&_monkey=23#23', 'right format'; -$url->query(monkey => 'foo'); -is "$url", 'http://example.com:8080?monkey=foo#23', 'right format'; -$url->query({monkey => 'bar'}); -is "$url", 'http://example.com:8080?monkey=bar#23', 'right format'; -$url->query([foo => 'bar']); -is "$url", 'http://example.com:8080?monkey=bar&foo=bar#23', 'right format'; -$url->query('foo'); -is "$url", 'http://example.com:8080?foo#23', 'right format'; -$url->query('foo=bar'); -is "$url", 'http://example.com:8080?foo=bar#23', 'right format'; -$url->query({foo => undef}); -is "$url", 'http://example.com:8080#23', 'right format'; -$url->query([foo => 23, bar => 24, baz => 25]); -is "$url", 'http://example.com:8080?foo=23&bar=24&baz=25#23', 'right format'; -$url->query({foo => 26, bar => undef, baz => undef}); -is "$url", 'http://example.com:8080?foo=26#23', 'right format'; -$url->query(c => 3); -is "$url", 'http://example.com:8080?c=3#23', 'right format'; -$url->query(Mojo::Parameters->new('a=1&b=2')); -is_deeply $url->query->to_hash, {a => 1, b => 2}, 'right structure'; -is "$url", 'http://example.com:8080?a=1&b=2#23', 'right format'; -$url->query(Mojo::Parameters->new('%E5=%E4')->charset(undef)); -is_deeply $url->query->to_hash, {"\xe5" => "\xe4"}, 'right structure'; -is "$url", 'http://example.com:8080?%E5=%E4#23', 'right format'; - -# Query string -$url = Mojo::URL->new('wss://sri:foo:bar@example.com:8080?_monkeybiz%3B&_monkey;23#23'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'wss', 'right scheme'; -is $url->userinfo, 'sri:foo:bar', 'right userinfo'; -is $url->username, 'sri', 'right username'; -is $url->password, 'foo:bar', 'right password'; -is $url->host, 'example.com', 'right host'; -is $url->port, '8080', 'right port'; -is $url->path, '', 'no path'; -is $url->query, '_monkeybiz%3B&_monkey;23', 'right query'; -is_deeply $url->query->pairs, ['_monkeybiz;', '', '_monkey;23', ''], 'right structure'; -is $url->query, '_monkeybiz%3B=&_monkey%3B23=', 'right query'; -is $url->fragment, '23', 'right fragment'; -is "$url", 'wss://example.com:8080?_monkeybiz%3B=&_monkey%3B23=#23', 'right format'; -$url = Mojo::URL->new('https://example.com/0?0#0'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'https', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->username, undef, 'no username'; -is $url->password, undef, 'no password'; -is $url->host, 'example.com', 'right host'; -is $url->port, undef, 'no port'; -is $url->host_port, 'example.com', 'right host and port'; -is $url->path, '/0', 'no path'; -is $url->query, '0', 'right query'; -is $url->fragment, '0', 'right fragment'; -is "$url", 'https://example.com/0?0#0', 'right format'; - -# No authority -$url = Mojo::URL->new('DATA:image/png;base64,helloworld123'); -is $url->scheme, 'DATA', 'right scheme'; -is $url->protocol, 'data', 'right protocol'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, undef, 'no host'; -is $url->port, undef, 'no port'; -is $url->path, 'image/png;base64,helloworld123', 'right path'; -is $url->query, '', 'no query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'data:image/png;base64,helloworld123', 'right format'; -$url = $url->clone; -is $url->scheme, 'DATA', 'right scheme'; -is $url->protocol, 'data', 'right protocol'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, undef, 'no host'; -is $url->port, undef, 'no port'; -is $url->path, 'image/png;base64,helloworld123', 'right path'; -is $url->query, '', 'no query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'data:image/png;base64,helloworld123', 'right format'; -$url = Mojo::URL->new->parse('mailto:sri@example.com'); -is $url->scheme, 'mailto', 'right scheme'; -is $url->protocol, 'mailto', 'right protocol'; -is $url->path, 'sri@example.com', 'right path'; -is "$url", 'mailto:sri@example.com', 'right format'; -$url = Mojo::URL->new->parse('foo:/test/123?foo=bar#baz'); -is $url->scheme, 'foo', 'right scheme'; -is $url->protocol, 'foo', 'right protocol'; -is $url->path, '/test/123', 'right path'; -is $url->query, 'foo=bar', 'right query'; -is $url->fragment, 'baz', 'right fragment'; -is "$url", 'foo:/test/123?foo=bar#baz', 'right format'; -is $url->scheme('Bar')->to_string, 'bar:/test/123?foo=bar#baz', 'right format'; -is $url->scheme, 'Bar', 'right scheme'; -is $url->protocol, 'bar', 'right protocol'; -is $url->host, undef, 'no host'; -is $url->path, '/test/123', 'right path'; -is $url->query, 'foo=bar', 'right query'; -is $url->fragment, 'baz', 'right fragment'; -is "$url", 'bar:/test/123?foo=bar#baz', 'right format'; -$url = Mojo::URL->new->parse('file:///foo/bar'); -is $url->scheme, 'file', 'right scheme'; -is $url->protocol, 'file', 'right protocol'; -is $url->path, '/foo/bar', 'right path'; -is "$url", 'file:///foo/bar', 'right format'; -$url = $url->clone; -is $url->scheme, 'file', 'right scheme'; -is $url->protocol, 'file', 'right protocol'; -is $url->path, '/foo/bar', 'right path'; -is "$url", 'file:///foo/bar', 'right format'; -$url = Mojo::URL->new->parse('foo:0'); -is $url->scheme, 'foo', 'right scheme'; -is $url->protocol, 'foo', 'right protocol'; -is $url->path, '0', 'right path'; -is "$url", 'foo:0', 'right format'; - -# Relative -is(Mojo::URL->new->to_abs, '', 'no result'); -$url = Mojo::URL->new('foo?foo=bar#23'); -is $url->path_query, 'foo?foo=bar', 'right path and query'; -ok !$url->is_abs, 'is not absolute'; -is "$url", 'foo?foo=bar#23', 'right relative version'; -$url = Mojo::URL->new('/foo?foo=bar#23'); -is $url->path_query, '/foo?foo=bar', 'right path and query'; -ok !$url->is_abs, 'is not absolute'; -is "$url", '/foo?foo=bar#23', 'right relative version'; - -# Relative without scheme -$url = Mojo::URL->new('//localhost/23/'); -ok !$url->is_abs, 'is not absolute'; -is $url->scheme, undef, 'no scheme'; -is $url->protocol, '', 'no protocol'; -is $url->host, 'localhost', 'right host'; -is $url->path, '/23/', 'right path'; -is "$url", '//localhost/23/', 'right relative version'; -is $url->to_abs(Mojo::URL->new('http://')), 'http://localhost/23/', 'right absolute version'; -is $url->to_abs(Mojo::URL->new('https://')), 'https://localhost/23/', 'right absolute version'; -is $url->to_abs(Mojo::URL->new('http://mojolicious.org')), 'http://localhost/23/', 'right absolute version'; -is $url->to_abs(Mojo::URL->new('http://mojolicious.org:8080')), 'http://localhost/23/', 'right absolute version'; -$url = Mojo::URL->new('///bar/23/'); -ok !$url->is_abs, 'is not absolute'; -is $url->host, '', 'no host'; -is $url->path, '/bar/23/', 'right path'; -is "$url", '///bar/23/', 'right relative version'; -$url = Mojo::URL->new('////bar//23/'); -ok !$url->is_abs, 'is not absolute'; -is $url->host, '', 'no host'; -is $url->path, '//bar//23/', 'right path'; -is "$url", '////bar//23/', 'right relative version'; - -# Relative path -$url = Mojo::URL->new('http://example.com/foo/?foo=bar#23'); -$url->path('bar'); -is "$url", 'http://example.com/foo/bar?foo=bar#23', 'right path'; -$url = Mojo::URL->new('http://example.com?foo=bar#23'); -$url->path('bar'); -is "$url", 'http://example.com/bar?foo=bar#23', 'right path'; -$url = Mojo::URL->new('http://example.com/foo?foo=bar#23'); -$url->path('bar'); -is "$url", 'http://example.com/bar?foo=bar#23', 'right path'; -$url = Mojo::URL->new('http://example.com/foo/bar?foo=bar#23'); -$url->path('yada/baz'); -is "$url", 'http://example.com/foo/yada/baz?foo=bar#23', 'right path'; -$url = Mojo::URL->new('http://example.com/foo/bar?foo=bar#23'); -$url->path('../baz'); -is "$url", 'http://example.com/foo/../baz?foo=bar#23', 'right path'; -$url->path->canonicalize; -is "$url", 'http://example.com/baz?foo=bar#23', 'right absolute path'; - -# Absolute (base without trailing slash) -$url = Mojo::URL->new('/foo?foo=bar#23'); -$url->base->parse('http://example.com/bar'); -ok !$url->is_abs, 'not absolute'; -is $url->to_abs, 'http://example.com/foo?foo=bar#23', 'right absolute version'; -$url = Mojo::URL->new('../cages/birds.gif'); -$url->base->parse('http://www.aviary.com/products/intro.html'); -ok !$url->is_abs, 'not absolute'; -is $url->to_abs, 'http://www.aviary.com/cages/birds.gif', 'right absolute version'; -$url = Mojo::URL->new('.././cages/./birds.gif'); -$url->base->parse('http://www.aviary.com/./products/./intro.html'); -ok !$url->is_abs, 'not absolute'; -is $url->to_abs, 'http://www.aviary.com/cages/birds.gif', 'right absolute version'; - -# Absolute with path -$url = Mojo::URL->new('../foo?foo=bar#23'); -$url->base->parse('http://example.com/bar/baz/'); -ok !$url->is_abs, 'not absolute'; -is $url->to_abs, 'http://example.com/bar/foo?foo=bar#23', 'right absolute version'; -is $url->to_abs->base, 'http://example.com/bar/baz/', 'right base'; - -# Absolute with query -$url = Mojo::URL->new('?foo=bar#23'); -$url->base->parse('http://example.com/bar/baz/'); -is $url->to_abs, 'http://example.com/bar/baz/?foo=bar#23', 'right absolute version'; - -# Clone (advanced) -$url = Mojo::URL->new('ws://sri:foobar@example.com:8080/test/index.html?monkey=biz&foo=1#23'); -my $clone = $url->clone; -ok $clone->is_abs, 'is absolute'; -is $clone->scheme, 'ws', 'right scheme'; -is $clone->userinfo, 'sri:foobar', 'right userinfo'; -is $clone->host, 'example.com', 'right host'; -is $clone->port, '8080', 'right port'; -is $clone->path, '/test/index.html', 'right path'; -is $clone->query, 'monkey=biz&foo=1', 'right query'; -is $clone->fragment, '23', 'right fragment'; -is "$clone", 'ws://example.com:8080/test/index.html?monkey=biz&foo=1#23', 'right format'; -$clone->path('/index.xml'); -is "$clone", 'ws://example.com:8080/index.xml?monkey=biz&foo=1#23', 'right format'; - -# Clone (with base) -$url = Mojo::URL->new('/test/index.html'); -$url->base->parse('http://127.0.0.1'); -is "$url", '/test/index.html', 'right format'; -$clone = $url->clone; -is "$url", '/test/index.html', 'right format'; -ok !$clone->is_abs, 'not absolute'; -is $clone->scheme, undef, 'no scheme'; -is $clone->host, undef, 'no host'; -is $clone->base->scheme, 'http', 'right base scheme'; -is $clone->base->host, '127.0.0.1', 'right base host'; -is $clone->path, '/test/index.html', 'right path'; -is $clone->to_abs->to_string, 'http://127.0.0.1/test/index.html', 'right absolute version'; - -# Clone (with base path) -$url = Mojo::URL->new('test/index.html'); -$url->base->parse('http://127.0.0.1/foo/'); -is "$url", 'test/index.html', 'right format'; -$clone = $url->clone; -is "$url", 'test/index.html', 'right format'; -ok !$clone->is_abs, 'not absolute'; -is $clone->scheme, undef, 'no scheme'; -is $clone->host, undef, 'no host'; -is $clone->base->scheme, 'http', 'right base scheme'; -is $clone->base->host, '127.0.0.1', 'right base host'; -is $clone->path, 'test/index.html', 'right path'; -is $clone->to_abs->to_string, 'http://127.0.0.1/foo/test/index.html', 'right absolute version'; - -# IPv6 -$url = Mojo::URL->new('wss://[::1]:3000/'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'wss', 'right scheme'; -is $url->host, '[::1]', 'right host'; -is $url->port, 3000, 'right port'; -is $url->path, '/', 'right path'; -is "$url", 'wss://[::1]:3000/', 'right format'; - -# Escaped host -$url = Mojo::URL->new('http+unix://%2FUsers%2Fsri%2Ftest.sock/index.html'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'http+unix', 'right scheme'; -is $url->host, '/Users/sri/test.sock', 'right host'; -is $url->port, undef, 'no port'; -is $url->host_port, '/Users/sri/test.sock', 'right host and port'; -is $url->path, '/index.html', 'right path'; -is "$url", 'http+unix://%2FUsers%2Fsri%2Ftest.sock/index.html', 'right format'; - -# IDNA -$url = Mojo::URL->new('http://bücher.ch:3000/foo'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'http', 'right scheme'; -is $url->host, 'bücher.ch', 'right host'; -is $url->ihost, 'xn--bcher-kva.ch', 'right internationalized host'; -is $url->port, 3000, 'right port'; -is $url->host_port, 'xn--bcher-kva.ch:3000', 'right host and port'; -is $url->path, '/foo', 'right path'; -is $url->path_query, '/foo', 'right path and query'; -is "$url", 'http://xn--bcher-kva.ch:3000/foo', 'right format'; -$url = Mojo::URL->new('http://bücher.bücher.ch:3000/foo'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'http', 'right scheme'; -is $url->host, 'bücher.bücher.ch', 'right host'; -is $url->ihost, 'xn--bcher-kva.xn--bcher-kva.ch', 'right internationalized host'; -is $url->port, 3000, 'right port'; -is $url->path, '/foo', 'right path'; -is "$url", 'http://xn--bcher-kva.xn--bcher-kva.ch:3000/foo', 'right format'; -$url = Mojo::URL->new('http://bücher.bücher.bücher.ch:3000/foo'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'http', 'right scheme'; -is $url->host, 'bücher.bücher.bücher.ch', 'right host'; -is $url->ihost, 'xn--bcher-kva.xn--bcher-kva.xn--bcher-kva.ch', 'right internationalized host'; -is $url->port, 3000, 'right port'; -is $url->path, '/foo', 'right path'; -is "$url", 'http://xn--bcher-kva.xn--bcher-kva.xn--bcher-kva.ch:3000/foo', 'right format'; -$url = Mojo::URL->new->scheme('http')->ihost('xn--n3h.xn--n3h.net'); -is $url->scheme, 'http', 'right scheme'; -is $url->host, '☃.☃.net', 'right host'; -is $url->ihost, 'xn--n3h.xn--n3h.net', 'right internationalized host'; -is "$url", 'http://xn--n3h.xn--n3h.net', 'right format'; - -# IDNA (escaped userinfo and host) -$url = Mojo::URL->new('https://%E2%99%A5:%E2%99%A5@kr%E4ih.com:3000'); -is $url->userinfo, '♥:♥', 'right userinfo'; -is $url->username, '♥', 'right username'; -is $url->password, '♥', 'right password'; -is $url->host, "kr\xe4ih.com", 'right host'; -is $url->ihost, 'xn--krih-moa.com', 'right internationalized host'; -is $url->port, 3000, 'right port'; -is "$url", 'https://xn--krih-moa.com:3000', 'right format'; - -# IDNA (snowman) -$url = Mojo::URL->new('http://☃:☃@☃.☃.de/☃?☃#☃'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, '☃:☃', 'right userinfo'; -is $url->host, '☃.☃.de', 'right host'; -is $url->ihost, 'xn--n3h.xn--n3h.de', 'right internationalized host'; -is $url->path, '/%E2%98%83', 'right path'; -is $url->query, '%E2%98%83', 'right query'; -is $url->fragment, '☃', 'right fragment'; -is "$url", 'http://xn--n3h.xn--n3h.de/%E2%98%83?%E2%98%83#%E2%98%83', 'right format'; -is $url->to_unsafe_string, 'http://%E2%98%83:%E2%98%83@xn--n3h.xn--n3h.de/%E2%98%83?%E2%98%83#%E2%98%83', - 'right format'; - -# IRI/IDNA -$url = Mojo::URL->new('http://☃.net/♥/?q=♥☃'); -is $url->path->parts->[0], '♥', 'right path part'; -is $url->path, '/%E2%99%A5/', 'right path'; -is $url->query, 'q=%E2%99%A5%E2%98%83', 'right query'; -is $url->query->param('q'), '♥☃', 'right query value'; -$url = Mojo::URL->new('http://☃.Net/♥/♥/?♥=☃'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'http', 'right scheme'; -is $url->host, '☃.Net', 'right host'; -is $url->ihost, 'xn--n3h.Net', 'right internationalized host'; -is $url->path, '/%E2%99%A5/%E2%99%A5/', 'right path'; -is_deeply $url->path->parts, ['♥', '♥'], 'right structure'; -is $url->query->param('♥'), '☃', 'right query value'; -is "$url", 'http://xn--n3h.Net/%E2%99%A5/%E2%99%A5/?%E2%99%A5=%E2%98%83', 'right format'; -$url = Mojo::URL->new('http://xn--n3h.net/%E2%99%A5/%E2%99%A5/?%E2%99%A5=%E2%98%83'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'http', 'right scheme'; -is $url->host, 'xn--n3h.net', 'right host'; -is $url->ihost, 'xn--n3h.net', 'right internationalized host'; -is $url->path, '/%E2%99%A5/%E2%99%A5/', 'right path'; -is_deeply $url->path->parts, ['♥', '♥'], 'right structure'; -is $url->query->param('♥'), '☃', 'right query value'; -is "$url", 'http://xn--n3h.net/%E2%99%A5/%E2%99%A5/?%E2%99%A5=%E2%98%83', 'right format'; - -# Already absolute -$url = Mojo::URL->new('http://foo.com/'); -is $url->to_abs, 'http://foo.com/', 'right absolute version'; - -# "0" -$url = Mojo::URL->new('http://0@foo.com#0'); -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, '0', 'right userinfo'; -is $url->username, '0', 'right username'; -is $url->password, undef, 'no password'; -is $url->host, 'foo.com', 'right host'; -is $url->fragment, '0', 'right fragment'; -is "$url", 'http://foo.com#0', 'right format'; -is $url->to_unsafe_string, 'http://0@foo.com#0', 'right format'; - -# Empty path elements -$url = Mojo::URL->new('http://example.com/foo//bar/23/'); -ok $url->is_abs, 'is absolute'; -is $url->path, '/foo//bar/23/', 'right path'; -is "$url", 'http://example.com/foo//bar/23/', 'right format'; -$url = Mojo::URL->new('http://example.com//foo//bar/23/'); -ok $url->is_abs, 'is absolute'; -is $url->path, '//foo//bar/23/', 'right path'; -is "$url", 'http://example.com//foo//bar/23/', 'right format'; -$url = Mojo::URL->new('http://example.com/foo///bar/23/'); -ok $url->is_abs, 'is absolute'; -is $url->path, '/foo///bar/23/', 'right path'; -is "$url", 'http://example.com/foo///bar/23/', 'right format'; - -# Merge relative path -$url = Mojo::URL->new('http://foo.bar/baz?yada'); -is $url->base, '', 'no base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz', 'right path'; -is $url->query, 'yada', 'right query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/baz?yada', 'right absolute URL'; -$url = Mojo::URL->new('zzz?Zzz')->base($url)->to_abs; -is $url->base, 'http://foo.bar/baz?yada', 'right base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/zzz', 'right path'; -is $url->query, 'Zzz', 'right query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/zzz?Zzz', 'right absolute URL'; - -# Merge relative path with directory -$url = Mojo::URL->new('http://foo.bar/baz/index.html?yada'); -is $url->base, '', 'no base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/index.html', 'right path'; -is $url->query, 'yada', 'right query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/baz/index.html?yada', 'right absolute URL'; -$url = Mojo::URL->new('zzz?Zzz')->base($url)->to_abs; -is $url->base, 'http://foo.bar/baz/index.html?yada', 'right base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/zzz', 'right path'; -is $url->query, 'Zzz', 'right query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/baz/zzz?Zzz', 'right absolute URL'; - -# Merge absolute path -$url = Mojo::URL->new('http://foo.bar/baz/index.html?yada'); -is $url->base, '', 'no base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/index.html', 'right path'; -is $url->query, 'yada', 'right query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/baz/index.html?yada', 'right absolute URL'; -$url = Mojo::URL->new('/zzz?Zzz')->base($url)->to_abs; -is $url->base, 'http://foo.bar/baz/index.html?yada', 'right base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/zzz', 'right path'; -is $url->query, 'Zzz', 'right query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/zzz?Zzz', 'right absolute URL'; - -# Merge absolute path without query -$url = Mojo::URL->new('http://foo.bar/baz/index.html?yada'); -is $url->base, '', 'no base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/index.html', 'right path'; -is $url->query, 'yada', 'right query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/baz/index.html?yada', 'right absolute URL'; -$url = Mojo::URL->new('/zzz')->base($url)->to_abs; -is $url->base, 'http://foo.bar/baz/index.html?yada', 'right base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/zzz', 'right path'; -is $url->query, '', 'no query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/zzz', 'right absolute URL'; - -# Merge absolute path with fragment -$url = Mojo::URL->new('http://foo.bar/baz/index.html?yada#test1'); -is $url->base, '', 'no base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/index.html', 'right path'; -is $url->query, 'yada', 'right query'; -is $url->fragment, 'test1', 'right fragment'; -is "$url", 'http://foo.bar/baz/index.html?yada#test1', 'right absolute URL'; -$url = Mojo::URL->new('/zzz#test2')->base($url)->to_abs; -is $url->base, 'http://foo.bar/baz/index.html?yada#test1', 'right base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/zzz', 'right path'; -is $url->query, '', 'no query'; -is $url->fragment, 'test2', 'right fragment'; -is "$url", 'http://foo.bar/zzz#test2', 'right absolute URL'; - -# Merge relative path with fragment -$url = Mojo::URL->new('http://foo.bar/baz/index.html?yada#test1'); -is $url->base, '', 'no base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/index.html', 'right path'; -is $url->query, 'yada', 'right query'; -is $url->fragment, 'test1', 'right fragment'; -is "$url", 'http://foo.bar/baz/index.html?yada#test1', 'right absolute URL'; -$url = Mojo::URL->new('zzz#test2')->base($url)->to_abs; -is $url->base, 'http://foo.bar/baz/index.html?yada#test1', 'right base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/zzz', 'right path'; -is $url->query, '', 'no query'; -is $url->fragment, 'test2', 'right fragment'; -is "$url", 'http://foo.bar/baz/zzz#test2', 'right absolute URL'; - -# Merge absolute path without fragment -$url = Mojo::URL->new('http://foo.bar/baz/index.html?yada#test1'); -is $url->base, '', 'no base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/index.html', 'right path'; -is $url->query, 'yada', 'right query'; -is $url->fragment, 'test1', 'right fragment'; -is "$url", 'http://foo.bar/baz/index.html?yada#test1', 'right absolute URL'; -$url = Mojo::URL->new('/zzz')->base($url)->to_abs; -is $url->base, 'http://foo.bar/baz/index.html?yada#test1', 'right base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/zzz', 'right path'; -is $url->query, '', 'no query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/zzz', 'right absolute URL'; - -# Merge relative path without fragment -$url = Mojo::URL->new('http://foo.bar/baz/index.html?yada#test1'); -is $url->base, '', 'no base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/index.html', 'right path'; -is $url->query, 'yada', 'right query'; -is $url->fragment, 'test1', 'right fragment'; -is "$url", 'http://foo.bar/baz/index.html?yada#test1', 'right absolute URL'; -$url = Mojo::URL->new('zzz')->base($url)->to_abs; -is $url->base, 'http://foo.bar/baz/index.html?yada#test1', 'right base'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'foo.bar', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/baz/zzz', 'right path'; -is $url->query, '', 'no query'; -is $url->fragment, undef, 'no fragment'; -is "$url", 'http://foo.bar/baz/zzz', 'right absolute URL'; - -# Hosts -$url = Mojo::URL->new('http://mojolicious.org'); -is $url->host, 'mojolicious.org', 'right host'; -$url = Mojo::URL->new('http://[::1]'); -is $url->host, '[::1]', 'right host'; -$url = Mojo::URL->new('http://127.0.0.1'); -is $url->host, '127.0.0.1', 'right host'; -$url = Mojo::URL->new('http://0::127.0.0.1'); -is $url->host, '0::127.0.0.1', 'right host'; -$url = Mojo::URL->new('http://[0::127.0.0.1]'); -is $url->host, '[0::127.0.0.1]', 'right host'; -$url = Mojo::URL->new('http://mojolicious.org:3000'); -is $url->host, 'mojolicious.org', 'right host'; -$url = Mojo::URL->new('http://[::1]:3000'); -is $url->host, '[::1]', 'right host'; -$url = Mojo::URL->new('http://127.0.0.1:3000'); -is $url->host, '127.0.0.1', 'right host'; -$url = Mojo::URL->new('http://0::127.0.0.1:3000'); -is $url->host, '0::127.0.0.1', 'right host'; -$url = Mojo::URL->new('http://[0::127.0.0.1]:3000'); -is $url->host, '[0::127.0.0.1]', 'right host'; -$url = Mojo::URL->new('http://foo.1.1.1.1.de/'); -is $url->host, 'foo.1.1.1.1.de', 'right host'; -$url = Mojo::URL->new('http://1.1.1.1.1.1/'); -is $url->host, '1.1.1.1.1.1', 'right host'; - -# Heavily escaped path and empty fragment -$url = Mojo::URL->new('http://example.com/mojo%2Fg%2B%2B-4%2E2_4%2E2%2E3-2ubuntu7_i386%2Edeb#'); -ok $url->is_abs, 'is absolute'; -is $url->scheme, 'http', 'right scheme'; -is $url->userinfo, undef, 'no userinfo'; -is $url->host, 'example.com', 'right host'; -is $url->port, undef, 'no port'; -is $url->path, '/mojo%2Fg%2B%2B-4%2E2_4%2E2%2E3-2ubuntu7_i386%2Edeb', 'right path'; -is $url->query, '', 'no query'; -is $url->fragment, '', 'right fragment'; -is "$url", 'http://example.com/mojo%2Fg%2B%2B-4%2E2_4%2E2%2E3-2ubuntu7_i386%2Edeb#', 'right format'; -$url->path->canonicalize; -is "$url", 'http://example.com/mojo/g++-4.2_4.2.3-2ubuntu7_i386.deb#', 'right format'; - -# "%" in path -$url = Mojo::URL->new('http://mojolicious.org/100%_fun'); -is $url->path->parts->[0], '100%_fun', 'right part'; -is $url->path, '/100%25_fun', 'right path'; -is "$url", 'http://mojolicious.org/100%25_fun', 'right format'; -$url = Mojo::URL->new('http://mojolicious.org/100%fun'); -is $url->path->parts->[0], '100%fun', 'right part'; -is $url->path, '/100%25fun', 'right path'; -is "$url", 'http://mojolicious.org/100%25fun', 'right format'; -$url = Mojo::URL->new('http://mojolicious.org/100%25_fun'); -is $url->path->parts->[0], '100%_fun', 'right part'; -is $url->path, '/100%25_fun', 'right path'; -is "$url", 'http://mojolicious.org/100%25_fun', 'right format'; - -# Trailing dot -$url = Mojo::URL->new('http://☃.net./♥'); -is $url->ihost, 'xn--n3h.net.', 'right internationalized host'; -is $url->host, '☃.net.', 'right host'; -is "$url", 'http://xn--n3h.net./%E2%99%A5', 'right format'; - -# No charset -$url = Mojo::URL->new; -$url->path->charset(undef); -$url->query->charset(undef); -$url->parse('HTTP://FOO.BAR/%E4/?%E5=%E4'); -is $url->scheme, 'HTTP', 'right scheme'; -is $url->protocol, 'http', 'right protocol'; -is $url->host, 'FOO.BAR', 'right host'; -is $url->ihost, 'FOO.BAR', 'right internationalized host'; -is $url->path, '/%E4/', 'right path'; -is_deeply $url->path->parts, ["\xe4"], 'right structure'; -ok $url->path->leading_slash, 'has leading slash'; -ok $url->path->trailing_slash, 'has trailing slash'; -is $url->query, '%E5=%E4', 'right query'; -is $url->query->param("\xe5"), "\xe4", 'right value'; -is "$url", 'http://FOO.BAR/%E4/?%E5=%E4', 'right format'; - -# Resolve RFC 1808 examples -my $base = Mojo::URL->new('http://a/b/c/d?q#f'); -$url = Mojo::URL->new('g'); -is $url->to_abs($base), 'http://a/b/c/g', 'right absolute version'; -$url = Mojo::URL->new('./g'); -is $url->to_abs($base), 'http://a/b/c/g', 'right absolute version'; -$url = Mojo::URL->new('g/'); -is $url->to_abs($base), 'http://a/b/c/g/', 'right absolute version'; -$url = Mojo::URL->new('//g'); -is $url->to_abs($base), 'http://g', 'right absolute version'; -$url = Mojo::URL->new('?y'); -is $url->to_abs($base), 'http://a/b/c/d?y', 'right absolute version'; -$url = Mojo::URL->new('g?y'); -is $url->to_abs($base), 'http://a/b/c/g?y', 'right absolute version'; -$url = Mojo::URL->new('g?y/./x'); -is $url->to_abs($base), 'http://a/b/c/g?y/./x', 'right absolute version'; -$url = Mojo::URL->new('#s'); -is $url->to_abs($base), 'http://a/b/c/d?q#s', 'right absolute version'; -$url = Mojo::URL->new('g#s'); -is $url->to_abs($base), 'http://a/b/c/g#s', 'right absolute version'; -$url = Mojo::URL->new('g#s/./x'); -is $url->to_abs($base), 'http://a/b/c/g#s/./x', 'right absolute version'; -$url = Mojo::URL->new('g?y#s'); -is $url->to_abs($base), 'http://a/b/c/g?y#s', 'right absolute version'; -$url = Mojo::URL->new('.'); -is $url->to_abs($base), 'http://a/b/c', 'right absolute version'; -$url = Mojo::URL->new('./'); -is $url->to_abs($base), 'http://a/b/c/', 'right absolute version'; -$url = Mojo::URL->new('..'); -is $url->to_abs($base), 'http://a/b', 'right absolute version'; -$url = Mojo::URL->new('../'); -is $url->to_abs($base), 'http://a/b/', 'right absolute version'; -$url = Mojo::URL->new('../g'); -is $url->to_abs($base), 'http://a/b/g', 'right absolute version'; -$url = Mojo::URL->new('../..'); -is $url->to_abs($base), 'http://a/', 'right absolute version'; -$url = Mojo::URL->new('../../'); -is $url->to_abs($base), 'http://a/', 'right absolute version'; -$url = Mojo::URL->new('../../g'); -is $url->to_abs($base), 'http://a/g', 'right absolute version'; +subtest 'Simple' => sub { + my $url = Mojo::URL->new('HtTp://Example.Com'); + is $url->scheme, 'HtTp', 'right scheme'; + is $url->protocol, 'http', 'right protocol'; + is $url->host, 'Example.Com', 'right host'; + is $url->ihost, 'Example.Com', 'right internationalized host'; + is "$url", 'http://Example.Com', 'right format'; +}; + +subtest 'Advanced' => sub { + my $url = Mojo::URL->new('https://sri:foobar@example.com:8080/x/index.html?monkey=biz&foo=1#/!%?@3'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'https', 'right scheme'; + is $url->protocol, 'https', 'right protocol'; + is $url->userinfo, 'sri:foobar', 'right userinfo'; + is $url->username, 'sri', 'right username'; + is $url->password, 'foobar', 'right password'; + is $url->host, 'example.com', 'right host'; + is $url->port, '8080', 'right port'; + is $url->path, '/x/index.html', 'right path'; + is $url->query, 'monkey=biz&foo=1', 'right query'; + is $url->path_query, '/x/index.html?monkey=biz&foo=1', 'right path and query'; + is $url->fragment, '/!%?@3', 'right fragment'; + is "$url", 'https://example.com:8080/x/index.html?monkey=biz&foo=1#/!%25?@3', 'right format'; + $url->path('/index.xml'); + is "$url", 'https://example.com:8080/index.xml?monkey=biz&foo=1#/!%25?@3', 'right format'; +}; + +subtest 'Advanced userinfo and fragment roundtrip' => sub { + my $url = Mojo::URL->new('ws://AZaz09-._~!$&\'()*+,;=:@localhost#AZaz09-._~!$&\'()*+,;=:@/?'); + is $url->scheme, 'ws', 'right scheme'; + is $url->userinfo, 'AZaz09-._~!$&\'()*+,;=:', 'right userinfo'; + is $url->username, 'AZaz09-._~!$&\'()*+,;=', 'right username'; + is $url->password, '', 'right password'; + is $url->host, 'localhost', 'right host'; + is $url->fragment, 'AZaz09-._~!$&\'()*+,;=:@/?', 'right fragment'; + is "$url", 'ws://localhost#AZaz09-._~!$&\'()*+,;=:@/?', 'right format'; + is $url->to_unsafe_string, 'ws://AZaz09-._~!$&\'()*+,;=:@localhost#AZaz09-._~!$&\'()*+,;=:@/?', 'right format'; +}; + +subtest 'Parameters' => sub { + my $url = Mojo::URL->new('http://sri:foobar@example.com:8080?_monkey=biz%3B&_monkey=23#23'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, 'sri:foobar', 'right userinfo'; + is $url->host, 'example.com', 'right host'; + is $url->port, '8080', 'right port'; + is $url->path, '', 'no path'; + is $url->query, '_monkey=biz%3B&_monkey=23', 'right query'; + is_deeply $url->query->to_hash, {_monkey => ['biz;', 23]}, 'right structure'; + is $url->fragment, '23', 'right fragment'; + is "$url", 'http://example.com:8080?_monkey=biz%3B&_monkey=23#23', 'right format'; + $url->query(monkey => 'foo'); + is "$url", 'http://example.com:8080?monkey=foo#23', 'right format'; + $url->query({monkey => 'bar'}); + is "$url", 'http://example.com:8080?monkey=bar#23', 'right format'; + $url->query([foo => 'bar']); + is "$url", 'http://example.com:8080?monkey=bar&foo=bar#23', 'right format'; + $url->query('foo'); + is "$url", 'http://example.com:8080?foo#23', 'right format'; + $url->query('foo=bar'); + is "$url", 'http://example.com:8080?foo=bar#23', 'right format'; + $url->query({foo => undef}); + is "$url", 'http://example.com:8080#23', 'right format'; + $url->query([foo => 23, bar => 24, baz => 25]); + is "$url", 'http://example.com:8080?foo=23&bar=24&baz=25#23', 'right format'; + $url->query({foo => 26, bar => undef, baz => undef}); + is "$url", 'http://example.com:8080?foo=26#23', 'right format'; + $url->query(c => 3); + is "$url", 'http://example.com:8080?c=3#23', 'right format'; + $url->query(Mojo::Parameters->new('a=1&b=2')); + is_deeply $url->query->to_hash, {a => 1, b => 2}, 'right structure'; + is "$url", 'http://example.com:8080?a=1&b=2#23', 'right format'; + $url->query(Mojo::Parameters->new('%E5=%E4')->charset(undef)); + is_deeply $url->query->to_hash, {"\xe5" => "\xe4"}, 'right structure'; + is "$url", 'http://example.com:8080?%E5=%E4#23', 'right format'; +}; + +subtest 'Query string' => sub { + my $url = Mojo::URL->new('wss://sri:foo:bar@example.com:8080?_monkeybiz%3B&_monkey;23#23'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'wss', 'right scheme'; + is $url->userinfo, 'sri:foo:bar', 'right userinfo'; + is $url->username, 'sri', 'right username'; + is $url->password, 'foo:bar', 'right password'; + is $url->host, 'example.com', 'right host'; + is $url->port, '8080', 'right port'; + is $url->path, '', 'no path'; + is $url->query, '_monkeybiz%3B&_monkey;23', 'right query'; + is_deeply $url->query->pairs, ['_monkeybiz;', '', '_monkey;23', ''], 'right structure'; + is $url->query, '_monkeybiz%3B=&_monkey%3B23=', 'right query'; + is $url->fragment, '23', 'right fragment'; + is "$url", 'wss://example.com:8080?_monkeybiz%3B=&_monkey%3B23=#23', 'right format'; + $url = Mojo::URL->new('https://example.com/0?0#0'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'https', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->username, undef, 'no username'; + is $url->password, undef, 'no password'; + is $url->host, 'example.com', 'right host'; + is $url->port, undef, 'no port'; + is $url->host_port, 'example.com', 'right host and port'; + is $url->path, '/0', 'no path'; + is $url->query, '0', 'right query'; + is $url->fragment, '0', 'right fragment'; + is "$url", 'https://example.com/0?0#0', 'right format'; +}; + +subtest 'No authority' => sub { + my $url = Mojo::URL->new('DATA:image/png;base64,helloworld123'); + is $url->scheme, 'DATA', 'right scheme'; + is $url->protocol, 'data', 'right protocol'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, undef, 'no host'; + is $url->port, undef, 'no port'; + is $url->path, 'image/png;base64,helloworld123', 'right path'; + is $url->query, '', 'no query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'data:image/png;base64,helloworld123', 'right format'; + $url = $url->clone; + is $url->scheme, 'DATA', 'right scheme'; + is $url->protocol, 'data', 'right protocol'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, undef, 'no host'; + is $url->port, undef, 'no port'; + is $url->path, 'image/png;base64,helloworld123', 'right path'; + is $url->query, '', 'no query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'data:image/png;base64,helloworld123', 'right format'; + $url = Mojo::URL->new->parse('mailto:sri@example.com'); + is $url->scheme, 'mailto', 'right scheme'; + is $url->protocol, 'mailto', 'right protocol'; + is $url->path, 'sri@example.com', 'right path'; + is "$url", 'mailto:sri@example.com', 'right format'; + $url = Mojo::URL->new->parse('foo:/test/123?foo=bar#baz'); + is $url->scheme, 'foo', 'right scheme'; + is $url->protocol, 'foo', 'right protocol'; + is $url->path, '/test/123', 'right path'; + is $url->query, 'foo=bar', 'right query'; + is $url->fragment, 'baz', 'right fragment'; + is "$url", 'foo:/test/123?foo=bar#baz', 'right format'; + is $url->scheme('Bar')->to_string, 'bar:/test/123?foo=bar#baz', 'right format'; + is $url->scheme, 'Bar', 'right scheme'; + is $url->protocol, 'bar', 'right protocol'; + is $url->host, undef, 'no host'; + is $url->path, '/test/123', 'right path'; + is $url->query, 'foo=bar', 'right query'; + is $url->fragment, 'baz', 'right fragment'; + is "$url", 'bar:/test/123?foo=bar#baz', 'right format'; + $url = Mojo::URL->new->parse('file:///foo/bar'); + is $url->scheme, 'file', 'right scheme'; + is $url->protocol, 'file', 'right protocol'; + is $url->path, '/foo/bar', 'right path'; + is "$url", 'file:///foo/bar', 'right format'; + $url = $url->clone; + is $url->scheme, 'file', 'right scheme'; + is $url->protocol, 'file', 'right protocol'; + is $url->path, '/foo/bar', 'right path'; + is "$url", 'file:///foo/bar', 'right format'; + $url = Mojo::URL->new->parse('foo:0'); + is $url->scheme, 'foo', 'right scheme'; + is $url->protocol, 'foo', 'right protocol'; + is $url->path, '0', 'right path'; + is "$url", 'foo:0', 'right format'; +}; + +subtest 'Relative' => sub { + is(Mojo::URL->new->to_abs, '', 'no result'); + my $url = Mojo::URL->new('foo?foo=bar#23'); + is $url->path_query, 'foo?foo=bar', 'right path and query'; + ok !$url->is_abs, 'is not absolute'; + is "$url", 'foo?foo=bar#23', 'right relative version'; + $url = Mojo::URL->new('/foo?foo=bar#23'); + is $url->path_query, '/foo?foo=bar', 'right path and query'; + ok !$url->is_abs, 'is not absolute'; + is "$url", '/foo?foo=bar#23', 'right relative version'; +}; + +subtest 'Relative without scheme' => sub { + my $url = Mojo::URL->new('//localhost/23/'); + ok !$url->is_abs, 'is not absolute'; + is $url->scheme, undef, 'no scheme'; + is $url->protocol, '', 'no protocol'; + is $url->host, 'localhost', 'right host'; + is $url->path, '/23/', 'right path'; + is "$url", '//localhost/23/', 'right relative version'; + is $url->to_abs(Mojo::URL->new('http://')), 'http://localhost/23/', 'right absolute version'; + is $url->to_abs(Mojo::URL->new('https://')), 'https://localhost/23/', 'right absolute version'; + is $url->to_abs(Mojo::URL->new('http://mojolicious.org')), 'http://localhost/23/', 'right absolute version'; + is $url->to_abs(Mojo::URL->new('http://mojolicious.org:8080')), 'http://localhost/23/', 'right absolute version'; + $url = Mojo::URL->new('///bar/23/'); + ok !$url->is_abs, 'is not absolute'; + is $url->host, '', 'no host'; + is $url->path, '/bar/23/', 'right path'; + is "$url", '///bar/23/', 'right relative version'; + $url = Mojo::URL->new('////bar//23/'); + ok !$url->is_abs, 'is not absolute'; + is $url->host, '', 'no host'; + is $url->path, '//bar//23/', 'right path'; + is "$url", '////bar//23/', 'right relative version'; +}; + +subtest 'Relative path' => sub { + my $url = Mojo::URL->new('http://example.com/foo/?foo=bar#23'); + $url->path('bar'); + is "$url", 'http://example.com/foo/bar?foo=bar#23', 'right path'; + $url = Mojo::URL->new('http://example.com?foo=bar#23'); + $url->path('bar'); + is "$url", 'http://example.com/bar?foo=bar#23', 'right path'; + $url = Mojo::URL->new('http://example.com/foo?foo=bar#23'); + $url->path('bar'); + is "$url", 'http://example.com/bar?foo=bar#23', 'right path'; + $url = Mojo::URL->new('http://example.com/foo/bar?foo=bar#23'); + $url->path('yada/baz'); + is "$url", 'http://example.com/foo/yada/baz?foo=bar#23', 'right path'; + $url = Mojo::URL->new('http://example.com/foo/bar?foo=bar#23'); + $url->path('../baz'); + is "$url", 'http://example.com/foo/../baz?foo=bar#23', 'right path'; + $url->path->canonicalize; + is "$url", 'http://example.com/baz?foo=bar#23', 'right absolute path'; +}; + +subtest 'Absolute (base without trailing slash)' => sub { + my $url = Mojo::URL->new('/foo?foo=bar#23'); + $url->base->parse('http://example.com/bar'); + ok !$url->is_abs, 'not absolute'; + is $url->to_abs, 'http://example.com/foo?foo=bar#23', 'right absolute version'; + $url = Mojo::URL->new('../cages/birds.gif'); + $url->base->parse('http://www.aviary.com/products/intro.html'); + ok !$url->is_abs, 'not absolute'; + is $url->to_abs, 'http://www.aviary.com/cages/birds.gif', 'right absolute version'; + $url = Mojo::URL->new('.././cages/./birds.gif'); + $url->base->parse('http://www.aviary.com/./products/./intro.html'); + ok !$url->is_abs, 'not absolute'; + is $url->to_abs, 'http://www.aviary.com/cages/birds.gif', 'right absolute version'; +}; + +subtest 'Absolute with path' => sub { + my $url = Mojo::URL->new('../foo?foo=bar#23'); + $url->base->parse('http://example.com/bar/baz/'); + ok !$url->is_abs, 'not absolute'; + is $url->to_abs, 'http://example.com/bar/foo?foo=bar#23', 'right absolute version'; + is $url->to_abs->base, 'http://example.com/bar/baz/', 'right base'; +}; + +subtest 'Absolute with query' => sub { + my $url = Mojo::URL->new('?foo=bar#23'); + $url->base->parse('http://example.com/bar/baz/'); + is $url->to_abs, 'http://example.com/bar/baz/?foo=bar#23', 'right absolute version'; +}; + +subtest 'Clone (advanced)' => sub { + my $url = Mojo::URL->new('ws://sri:foobar@example.com:8080/test/index.html?monkey=biz&foo=1#23'); + my $clone = $url->clone; + ok $clone->is_abs, 'is absolute'; + is $clone->scheme, 'ws', 'right scheme'; + is $clone->userinfo, 'sri:foobar', 'right userinfo'; + is $clone->host, 'example.com', 'right host'; + is $clone->port, '8080', 'right port'; + is $clone->path, '/test/index.html', 'right path'; + is $clone->query, 'monkey=biz&foo=1', 'right query'; + is $clone->fragment, '23', 'right fragment'; + is "$clone", 'ws://example.com:8080/test/index.html?monkey=biz&foo=1#23', 'right format'; + $clone->path('/index.xml'); + is "$clone", 'ws://example.com:8080/index.xml?monkey=biz&foo=1#23', 'right format'; +}; + +subtest 'Clone (with base)' => sub { + my $url = Mojo::URL->new('/test/index.html'); + $url->base->parse('http://127.0.0.1'); + is "$url", '/test/index.html', 'right format'; + my $clone = $url->clone; + is "$url", '/test/index.html', 'right format'; + ok !$clone->is_abs, 'not absolute'; + is $clone->scheme, undef, 'no scheme'; + is $clone->host, undef, 'no host'; + is $clone->base->scheme, 'http', 'right base scheme'; + is $clone->base->host, '127.0.0.1', 'right base host'; + is $clone->path, '/test/index.html', 'right path'; + is $clone->to_abs->to_string, 'http://127.0.0.1/test/index.html', 'right absolute version'; +}; + +subtest 'Clone (with base path)' => sub { + my $url = Mojo::URL->new('test/index.html'); + $url->base->parse('http://127.0.0.1/foo/'); + is "$url", 'test/index.html', 'right format'; + my $clone = $url->clone; + is "$url", 'test/index.html', 'right format'; + ok !$clone->is_abs, 'not absolute'; + is $clone->scheme, undef, 'no scheme'; + is $clone->host, undef, 'no host'; + is $clone->base->scheme, 'http', 'right base scheme'; + is $clone->base->host, '127.0.0.1', 'right base host'; + is $clone->path, 'test/index.html', 'right path'; + is $clone->to_abs->to_string, 'http://127.0.0.1/foo/test/index.html', 'right absolute version'; +}; + +subtest 'IPv6' => sub { + my $url = Mojo::URL->new('wss://[::1]:3000/'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'wss', 'right scheme'; + is $url->host, '[::1]', 'right host'; + is $url->port, 3000, 'right port'; + is $url->path, '/', 'right path'; + is "$url", 'wss://[::1]:3000/', 'right format'; +}; + +subtest 'Escaped host' => sub { + my $url = Mojo::URL->new('http+unix://%2FUsers%2Fsri%2Ftest.sock/index.html'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'http+unix', 'right scheme'; + is $url->host, '/Users/sri/test.sock', 'right host'; + is $url->port, undef, 'no port'; + is $url->host_port, '/Users/sri/test.sock', 'right host and port'; + is $url->path, '/index.html', 'right path'; + is "$url", 'http+unix://%2FUsers%2Fsri%2Ftest.sock/index.html', 'right format'; +}; + +subtest 'IDNA' => sub { + my $url = Mojo::URL->new('http://bücher.ch:3000/foo'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'http', 'right scheme'; + is $url->host, 'bücher.ch', 'right host'; + is $url->ihost, 'xn--bcher-kva.ch', 'right internationalized host'; + is $url->port, 3000, 'right port'; + is $url->host_port, 'xn--bcher-kva.ch:3000', 'right host and port'; + is $url->path, '/foo', 'right path'; + is $url->path_query, '/foo', 'right path and query'; + is "$url", 'http://xn--bcher-kva.ch:3000/foo', 'right format'; + $url = Mojo::URL->new('http://bücher.bücher.ch:3000/foo'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'http', 'right scheme'; + is $url->host, 'bücher.bücher.ch', 'right host'; + is $url->ihost, 'xn--bcher-kva.xn--bcher-kva.ch', 'right internationalized host'; + is $url->port, 3000, 'right port'; + is $url->path, '/foo', 'right path'; + is "$url", 'http://xn--bcher-kva.xn--bcher-kva.ch:3000/foo', 'right format'; + $url = Mojo::URL->new('http://bücher.bücher.bücher.ch:3000/foo'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'http', 'right scheme'; + is $url->host, 'bücher.bücher.bücher.ch', 'right host'; + is $url->ihost, 'xn--bcher-kva.xn--bcher-kva.xn--bcher-kva.ch', 'right internationalized host'; + is $url->port, 3000, 'right port'; + is $url->path, '/foo', 'right path'; + is "$url", 'http://xn--bcher-kva.xn--bcher-kva.xn--bcher-kva.ch:3000/foo', 'right format'; + $url = Mojo::URL->new->scheme('http')->ihost('xn--n3h.xn--n3h.net'); + is $url->scheme, 'http', 'right scheme'; + is $url->host, '☃.☃.net', 'right host'; + is $url->ihost, 'xn--n3h.xn--n3h.net', 'right internationalized host'; + is "$url", 'http://xn--n3h.xn--n3h.net', 'right format'; +}; + +subtest 'IDNA (escaped userinfo and host)' => sub { + my $url = Mojo::URL->new('https://%E2%99%A5:%E2%99%A5@kr%E4ih.com:3000'); + is $url->userinfo, '♥:♥', 'right userinfo'; + is $url->username, '♥', 'right username'; + is $url->password, '♥', 'right password'; + is $url->host, "kr\xe4ih.com", 'right host'; + is $url->ihost, 'xn--krih-moa.com', 'right internationalized host'; + is $url->port, 3000, 'right port'; + is "$url", 'https://xn--krih-moa.com:3000', 'right format'; +}; + +subtest 'IDNA (snowman)' => sub { + my $url = Mojo::URL->new('http://☃:☃@☃.☃.de/☃?☃#☃'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, '☃:☃', 'right userinfo'; + is $url->host, '☃.☃.de', 'right host'; + is $url->ihost, 'xn--n3h.xn--n3h.de', 'right internationalized host'; + is $url->path, '/%E2%98%83', 'right path'; + is $url->query, '%E2%98%83', 'right query'; + is $url->fragment, '☃', 'right fragment'; + is "$url", 'http://xn--n3h.xn--n3h.de/%E2%98%83?%E2%98%83#%E2%98%83', 'right format'; + is $url->to_unsafe_string, 'http://%E2%98%83:%E2%98%83@xn--n3h.xn--n3h.de/%E2%98%83?%E2%98%83#%E2%98%83', + 'right format'; + }; + +subtest 'IRI/IDNA' => sub { + my $url = Mojo::URL->new('http://☃.net/♥/?q=♥☃'); + is $url->path->parts->[0], '♥', 'right path part'; + is $url->path, '/%E2%99%A5/', 'right path'; + is $url->query, 'q=%E2%99%A5%E2%98%83', 'right query'; + is $url->query->param('q'), '♥☃', 'right query value'; + $url = Mojo::URL->new('http://☃.Net/♥/♥/?♥=☃'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'http', 'right scheme'; + is $url->host, '☃.Net', 'right host'; + is $url->ihost, 'xn--n3h.Net', 'right internationalized host'; + is $url->path, '/%E2%99%A5/%E2%99%A5/', 'right path'; + is_deeply $url->path->parts, ['♥', '♥'], 'right structure'; + is $url->query->param('♥'), '☃', 'right query value'; + is "$url", 'http://xn--n3h.Net/%E2%99%A5/%E2%99%A5/?%E2%99%A5=%E2%98%83', 'right format'; + $url = Mojo::URL->new('http://xn--n3h.net/%E2%99%A5/%E2%99%A5/?%E2%99%A5=%E2%98%83'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'http', 'right scheme'; + is $url->host, 'xn--n3h.net', 'right host'; + is $url->ihost, 'xn--n3h.net', 'right internationalized host'; + is $url->path, '/%E2%99%A5/%E2%99%A5/', 'right path'; + is_deeply $url->path->parts, ['♥', '♥'], 'right structure'; + is $url->query->param('♥'), '☃', 'right query value'; + is "$url", 'http://xn--n3h.net/%E2%99%A5/%E2%99%A5/?%E2%99%A5=%E2%98%83', 'right format'; +}; + +subtest 'Already absolute' => sub { + my $url = Mojo::URL->new('http://foo.com/'); + is $url->to_abs, 'http://foo.com/', 'right absolute version'; +}; + +subtest '"0"' => sub { + my $url = Mojo::URL->new('http://0@foo.com#0'); + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, '0', 'right userinfo'; + is $url->username, '0', 'right username'; + is $url->password, undef, 'no password'; + is $url->host, 'foo.com', 'right host'; + is $url->fragment, '0', 'right fragment'; + is "$url", 'http://foo.com#0', 'right format'; + is $url->to_unsafe_string, 'http://0@foo.com#0', 'right format'; +}; + +subtest 'Empty path elements' => sub { + my $url = Mojo::URL->new('http://example.com/foo//bar/23/'); + ok $url->is_abs, 'is absolute'; + is $url->path, '/foo//bar/23/', 'right path'; + is "$url", 'http://example.com/foo//bar/23/', 'right format'; + $url = Mojo::URL->new('http://example.com//foo//bar/23/'); + ok $url->is_abs, 'is absolute'; + is $url->path, '//foo//bar/23/', 'right path'; + is "$url", 'http://example.com//foo//bar/23/', 'right format'; + $url = Mojo::URL->new('http://example.com/foo///bar/23/'); + ok $url->is_abs, 'is absolute'; + is $url->path, '/foo///bar/23/', 'right path'; + is "$url", 'http://example.com/foo///bar/23/', 'right format'; +}; + +subtest 'Merge relative path' => sub { + my $url = Mojo::URL->new('http://foo.bar/baz?yada'); + is $url->base, '', 'no base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz', 'right path'; + is $url->query, 'yada', 'right query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/baz?yada', 'right absolute URL'; + $url = Mojo::URL->new('zzz?Zzz')->base($url)->to_abs; + is $url->base, 'http://foo.bar/baz?yada', 'right base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/zzz', 'right path'; + is $url->query, 'Zzz', 'right query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/zzz?Zzz', 'right absolute URL'; +}; + +subtest 'Merge relative path with directory' => sub { + my $url = Mojo::URL->new('http://foo.bar/baz/index.html?yada'); + is $url->base, '', 'no base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/index.html', 'right path'; + is $url->query, 'yada', 'right query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/baz/index.html?yada', 'right absolute URL'; + $url = Mojo::URL->new('zzz?Zzz')->base($url)->to_abs; + is $url->base, 'http://foo.bar/baz/index.html?yada', 'right base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/zzz', 'right path'; + is $url->query, 'Zzz', 'right query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/baz/zzz?Zzz', 'right absolute URL'; +}; + +subtest 'Merge absolute path' => sub { + my $url = Mojo::URL->new('http://foo.bar/baz/index.html?yada'); + is $url->base, '', 'no base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/index.html', 'right path'; + is $url->query, 'yada', 'right query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/baz/index.html?yada', 'right absolute URL'; + $url = Mojo::URL->new('/zzz?Zzz')->base($url)->to_abs; + is $url->base, 'http://foo.bar/baz/index.html?yada', 'right base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/zzz', 'right path'; + is $url->query, 'Zzz', 'right query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/zzz?Zzz', 'right absolute URL'; +}; + +subtest 'Merge absolute path without query' => sub { + my $url = Mojo::URL->new('http://foo.bar/baz/index.html?yada'); + is $url->base, '', 'no base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/index.html', 'right path'; + is $url->query, 'yada', 'right query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/baz/index.html?yada', 'right absolute URL'; + $url = Mojo::URL->new('/zzz')->base($url)->to_abs; + is $url->base, 'http://foo.bar/baz/index.html?yada', 'right base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/zzz', 'right path'; + is $url->query, '', 'no query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/zzz', 'right absolute URL'; +}; + +subtest 'Merge absolute path with fragment' => sub { + my $url = Mojo::URL->new('http://foo.bar/baz/index.html?yada#test1'); + is $url->base, '', 'no base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/index.html', 'right path'; + is $url->query, 'yada', 'right query'; + is $url->fragment, 'test1', 'right fragment'; + is "$url", 'http://foo.bar/baz/index.html?yada#test1', 'right absolute URL'; + $url = Mojo::URL->new('/zzz#test2')->base($url)->to_abs; + is $url->base, 'http://foo.bar/baz/index.html?yada#test1', 'right base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/zzz', 'right path'; + is $url->query, '', 'no query'; + is $url->fragment, 'test2', 'right fragment'; + is "$url", 'http://foo.bar/zzz#test2', 'right absolute URL'; +}; + +subtest 'Merge relative path with fragment' => sub { + my $url = Mojo::URL->new('http://foo.bar/baz/index.html?yada#test1'); + is $url->base, '', 'no base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/index.html', 'right path'; + is $url->query, 'yada', 'right query'; + is $url->fragment, 'test1', 'right fragment'; + is "$url", 'http://foo.bar/baz/index.html?yada#test1', 'right absolute URL'; + $url = Mojo::URL->new('zzz#test2')->base($url)->to_abs; + is $url->base, 'http://foo.bar/baz/index.html?yada#test1', 'right base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/zzz', 'right path'; + is $url->query, '', 'no query'; + is $url->fragment, 'test2', 'right fragment'; + is "$url", 'http://foo.bar/baz/zzz#test2', 'right absolute URL'; +}; + +subtest 'Merge absolute path without fragment' => sub { + my $url = Mojo::URL->new('http://foo.bar/baz/index.html?yada#test1'); + is $url->base, '', 'no base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/index.html', 'right path'; + is $url->query, 'yada', 'right query'; + is $url->fragment, 'test1', 'right fragment'; + is "$url", 'http://foo.bar/baz/index.html?yada#test1', 'right absolute URL'; + $url = Mojo::URL->new('/zzz')->base($url)->to_abs; + is $url->base, 'http://foo.bar/baz/index.html?yada#test1', 'right base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/zzz', 'right path'; + is $url->query, '', 'no query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/zzz', 'right absolute URL'; +}; + +subtest 'Merge relative path without fragment' => sub { + my $url = Mojo::URL->new('http://foo.bar/baz/index.html?yada#test1'); + is $url->base, '', 'no base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/index.html', 'right path'; + is $url->query, 'yada', 'right query'; + is $url->fragment, 'test1', 'right fragment'; + is "$url", 'http://foo.bar/baz/index.html?yada#test1', 'right absolute URL'; + $url = Mojo::URL->new('zzz')->base($url)->to_abs; + is $url->base, 'http://foo.bar/baz/index.html?yada#test1', 'right base'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'foo.bar', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/baz/zzz', 'right path'; + is $url->query, '', 'no query'; + is $url->fragment, undef, 'no fragment'; + is "$url", 'http://foo.bar/baz/zzz', 'right absolute URL'; +}; + +subtest 'Hosts' => sub { + my $url = Mojo::URL->new('http://mojolicious.org'); + is $url->host, 'mojolicious.org', 'right host'; + $url = Mojo::URL->new('http://[::1]'); + is $url->host, '[::1]', 'right host'; + $url = Mojo::URL->new('http://127.0.0.1'); + is $url->host, '127.0.0.1', 'right host'; + $url = Mojo::URL->new('http://0::127.0.0.1'); + is $url->host, '0::127.0.0.1', 'right host'; + $url = Mojo::URL->new('http://[0::127.0.0.1]'); + is $url->host, '[0::127.0.0.1]', 'right host'; + $url = Mojo::URL->new('http://mojolicious.org:3000'); + is $url->host, 'mojolicious.org', 'right host'; + $url = Mojo::URL->new('http://[::1]:3000'); + is $url->host, '[::1]', 'right host'; + $url = Mojo::URL->new('http://127.0.0.1:3000'); + is $url->host, '127.0.0.1', 'right host'; + $url = Mojo::URL->new('http://0::127.0.0.1:3000'); + is $url->host, '0::127.0.0.1', 'right host'; + $url = Mojo::URL->new('http://[0::127.0.0.1]:3000'); + is $url->host, '[0::127.0.0.1]', 'right host'; + $url = Mojo::URL->new('http://foo.1.1.1.1.de/'); + is $url->host, 'foo.1.1.1.1.de', 'right host'; + $url = Mojo::URL->new('http://1.1.1.1.1.1/'); + is $url->host, '1.1.1.1.1.1', 'right host'; +}; + +subtest 'Heavily escaped path and empty fragment' => sub { + my $url = Mojo::URL->new('http://example.com/mojo%2Fg%2B%2B-4%2E2_4%2E2%2E3-2ubuntu7_i386%2Edeb#'); + ok $url->is_abs, 'is absolute'; + is $url->scheme, 'http', 'right scheme'; + is $url->userinfo, undef, 'no userinfo'; + is $url->host, 'example.com', 'right host'; + is $url->port, undef, 'no port'; + is $url->path, '/mojo%2Fg%2B%2B-4%2E2_4%2E2%2E3-2ubuntu7_i386%2Edeb', 'right path'; + is $url->query, '', 'no query'; + is $url->fragment, '', 'right fragment'; + is "$url", 'http://example.com/mojo%2Fg%2B%2B-4%2E2_4%2E2%2E3-2ubuntu7_i386%2Edeb#', 'right format'; + $url->path->canonicalize; + is "$url", 'http://example.com/mojo/g++-4.2_4.2.3-2ubuntu7_i386.deb#', 'right format'; +}; + +subtest '"%" in path' => sub { + my $url = Mojo::URL->new('http://mojolicious.org/100%_fun'); + is $url->path->parts->[0], '100%_fun', 'right part'; + is $url->path, '/100%25_fun', 'right path'; + is "$url", 'http://mojolicious.org/100%25_fun', 'right format'; + $url = Mojo::URL->new('http://mojolicious.org/100%fun'); + is $url->path->parts->[0], '100%fun', 'right part'; + is $url->path, '/100%25fun', 'right path'; + is "$url", 'http://mojolicious.org/100%25fun', 'right format'; + $url = Mojo::URL->new('http://mojolicious.org/100%25_fun'); + is $url->path->parts->[0], '100%_fun', 'right part'; + is $url->path, '/100%25_fun', 'right path'; + is "$url", 'http://mojolicious.org/100%25_fun', 'right format'; +}; + +subtest 'Trailing dot' => sub { + my $url = Mojo::URL->new('http://☃.net./♥'); + is $url->ihost, 'xn--n3h.net.', 'right internationalized host'; + is $url->host, '☃.net.', 'right host'; + is "$url", 'http://xn--n3h.net./%E2%99%A5', 'right format'; +}; + +subtest 'No charset' => sub { + my $url = Mojo::URL->new; + $url->path->charset(undef); + $url->query->charset(undef); + $url->parse('HTTP://FOO.BAR/%E4/?%E5=%E4'); + is $url->scheme, 'HTTP', 'right scheme'; + is $url->protocol, 'http', 'right protocol'; + is $url->host, 'FOO.BAR', 'right host'; + is $url->ihost, 'FOO.BAR', 'right internationalized host'; + is $url->path, '/%E4/', 'right path'; + is_deeply $url->path->parts, ["\xe4"], 'right structure'; + ok $url->path->leading_slash, 'has leading slash'; + ok $url->path->trailing_slash, 'has trailing slash'; + is $url->query, '%E5=%E4', 'right query'; + is $url->query->param("\xe5"), "\xe4", 'right value'; + is "$url", 'http://FOO.BAR/%E4/?%E5=%E4', 'right format'; +}; + +subtest 'Resolve RFC 1808 examples' => sub { + my $base = Mojo::URL->new('http://a/b/c/d?q#f'); + my $url = Mojo::URL->new('g'); + is $url->to_abs($base), 'http://a/b/c/g', 'right absolute version'; + $url = Mojo::URL->new('./g'); + is $url->to_abs($base), 'http://a/b/c/g', 'right absolute version'; + $url = Mojo::URL->new('g/'); + is $url->to_abs($base), 'http://a/b/c/g/', 'right absolute version'; + $url = Mojo::URL->new('//g'); + is $url->to_abs($base), 'http://g', 'right absolute version'; + $url = Mojo::URL->new('?y'); + is $url->to_abs($base), 'http://a/b/c/d?y', 'right absolute version'; + $url = Mojo::URL->new('g?y'); + is $url->to_abs($base), 'http://a/b/c/g?y', 'right absolute version'; + $url = Mojo::URL->new('g?y/./x'); + is $url->to_abs($base), 'http://a/b/c/g?y/./x', 'right absolute version'; + $url = Mojo::URL->new('#s'); + is $url->to_abs($base), 'http://a/b/c/d?q#s', 'right absolute version'; + $url = Mojo::URL->new('g#s'); + is $url->to_abs($base), 'http://a/b/c/g#s', 'right absolute version'; + $url = Mojo::URL->new('g#s/./x'); + is $url->to_abs($base), 'http://a/b/c/g#s/./x', 'right absolute version'; + $url = Mojo::URL->new('g?y#s'); + is $url->to_abs($base), 'http://a/b/c/g?y#s', 'right absolute version'; + $url = Mojo::URL->new('.'); + is $url->to_abs($base), 'http://a/b/c', 'right absolute version'; + $url = Mojo::URL->new('./'); + is $url->to_abs($base), 'http://a/b/c/', 'right absolute version'; + $url = Mojo::URL->new('..'); + is $url->to_abs($base), 'http://a/b', 'right absolute version'; + $url = Mojo::URL->new('../'); + is $url->to_abs($base), 'http://a/b/', 'right absolute version'; + $url = Mojo::URL->new('../g'); + is $url->to_abs($base), 'http://a/b/g', 'right absolute version'; + $url = Mojo::URL->new('../..'); + is $url->to_abs($base), 'http://a/', 'right absolute version'; + $url = Mojo::URL->new('../../'); + is $url->to_abs($base), 'http://a/', 'right absolute version'; + $url = Mojo::URL->new('../../g'); + is $url->to_abs($base), 'http://a/g', 'right absolute version'; +}; done_testing(); diff --git a/t/mojo/util.t b/t/mojo/util.t index 856af49683..fdeeb1a620 100644 --- a/t/mojo/util.t +++ b/t/mojo/util.t @@ -16,82 +16,90 @@ use Mojo::Util qw(b64_decode b64_encode camelize class_to_file class_to_path dec qw(split_header steady_time tablify term_escape trim unindent unquote), qw(url_escape url_unescape xml_escape xor_encode); -# camelize -is camelize('foo_bar_baz'), 'FooBarBaz', 'right camelized result'; -is camelize('FooBarBaz'), 'FooBarBaz', 'right camelized result'; -is camelize('foo_b_b'), 'FooBB', 'right camelized result'; -is camelize('foo-b_b'), 'Foo::BB', 'right camelized result'; -is camelize('FooBar'), 'FooBar', 'already camelized'; -is camelize('Foo::Bar'), 'Foo::Bar', 'already camelized'; - -# decamelize -is decamelize('FooBarBaz'), 'foo_bar_baz', 'right decamelized result'; -is decamelize('foo_bar_baz'), 'foo_bar_baz', 'right decamelized result'; -is decamelize('FooBB'), 'foo_b_b', 'right decamelized result'; -is decamelize('Foo::BB'), 'foo-b_b', 'right decamelized result'; - -# class_to_file -is class_to_file('Foo::Bar'), 'foo_bar', 'right file'; -is class_to_file('FooBar'), 'foo_bar', 'right file'; -is class_to_file('FOOBar'), 'foobar', 'right file'; -is class_to_file('FOOBAR'), 'foobar', 'right file'; -is class_to_file('FOO::Bar'), 'foobar', 'right file'; -is class_to_file('FooBAR'), 'foo_bar', 'right file'; -is class_to_file('Foo::BAR'), 'foo_bar', 'right file'; -is class_to_file("Foo'BAR"), 'foo_bar', 'right file'; -is class_to_file("Foo'Bar::Baz"), 'foo_bar_baz', 'right file'; - -# class_to_path -is class_to_path('Foo::Bar'), 'Foo/Bar.pm', 'right path'; -is class_to_path("Foo'Bar"), 'Foo/Bar.pm', 'right path'; -is class_to_path("Foo'Bar::Baz"), 'Foo/Bar/Baz.pm', 'right path'; -is class_to_path("Foo::Bar'Baz"), 'Foo/Bar/Baz.pm', 'right path'; -is class_to_path("Foo::Bar::Baz"), 'Foo/Bar/Baz.pm', 'right path'; -is class_to_path("Foo'Bar'Baz"), 'Foo/Bar/Baz.pm', 'right path'; - -# split_header -is_deeply split_header(''), [], 'right result'; -is_deeply split_header('foo=b=a=r'), [['foo', 'b=a=r']], 'right result'; -is_deeply split_header('a=b ,, , c=d ;; ; e=f g h=i'), [['a', 'b'], ['c', 'd', 'e', 'f', 'g', undef, 'h', 'i']], - 'right result'; -is_deeply split_header(',,foo,, ,bar'), [['foo', undef], ['bar', undef]], 'right result'; -is_deeply split_header(';;foo; ; ;bar'), [['foo', undef, 'bar', undef]], 'right result'; -is_deeply split_header('foo=;bar=""'), [['foo', '', 'bar', '']], 'right result'; -is_deeply split_header('foo=bar baz=yada'), [['foo', 'bar', 'baz', 'yada']], 'right result'; -is_deeply split_header('foo,bar,baz'), [['foo', undef], ['bar', undef], ['baz', undef]], 'right result'; -is_deeply split_header('f "o" o , ba r'), [['f', undef, '"o"', undef, 'o', undef], ['ba', undef, 'r', undef]], - 'right result'; -is_deeply split_header('foo="b,; a\" r\"\\\\"'), [['foo', 'b,; a" r"\\']], 'right result'; -is_deeply split_header('foo = "b a\" r\"\\\\"; bar="ba z"'), [['foo', 'b a" r"\\', 'bar', 'ba z']], 'right result'; -my $header = q{; rel="x"; t*=UTF-8'de'a%20b}; -my $tree = [['', undef, 'rel', 'x', 't*', 'UTF-8\'de\'a%20b']]; -is_deeply split_header($header), $tree, 'right result'; -$header = 'a=b c; A=b.c; D=/E; a-b=3; expires=Thu, 07 Aug 2008 07:07:59 GMT; Ab;'; -$tree = [ - ['a', 'b', 'c', undef, 'A', 'b.c', 'D', '/E', 'a-b', '3', 'expires', 'Thu'], - ['07', undef, 'Aug', undef, '2008', undef, '07:07:59', undef, 'GMT', undef, 'Ab', undef] -]; -is_deeply split_header($header), $tree, 'right result'; - -# split_cookie_header -is_deeply split_cookie_header(''), [], 'right result'; -is_deeply split_cookie_header('a=b; expires=Thu, 07 Aug 2008 07:07:59 GMT,c=d'), - [['a', 'b', 'expires', 'Thu, 07 Aug 2008 07:07:59 GMT'], ['c', 'd']], 'right result'; -is_deeply split_cookie_header('a=b; expires=Tuesday, 09-Nov-1999 23:12:40 GMT, c=d'), - [['a', 'b', 'expires', 'Tuesday, 09-Nov-1999 23:12:40 GMT'], ['c', 'd']], 'right result'; -is_deeply split_cookie_header('a=b; expires=Tuesday, 09-Nov-1999 23:12:40 GMT;, c=d;'), - [['a', 'b', 'expires', 'Tuesday, 09-Nov-1999 23:12:40 GMT'], ['c', 'd']], 'right result'; -is_deeply split_cookie_header('a=b; expires=Sun,06 Nov 1994 08:49:37 UTC; path=/'), - [['a', 'b', 'expires', 'Sun,06 Nov 1994 08:49:37 UTC', 'path', '/']], 'right result'; -is_deeply split_cookie_header('a=b ; expires = Sunday 06 Nov 94 08:49:37UTC ; path=/'), - [['a', 'b', 'expires', 'Sunday 06 Nov 94 08:49:37UTC', 'path', '/']], 'right result'; -$header = 'expires=Thu, 07 Aug 2008 07:07:59 GMT, a=b'; -$tree = [['expires', 'Thu'], ['07', undef, 'Aug', undef, '2008', undef, '07:07:59', undef, 'GMT', undef], ['a', 'b']]; -is_deeply split_cookie_header($header), $tree, 'right result'; - -# extract_usage -is extract_usage, "extract_usage test!\n", 'right result'; -is extract_usage(curfile->sibling('lib', 'myapp.pl')), "USAGE: myapp.pl daemon\n\n test\n123\n", 'right result'; +subtest 'camelize' => sub { + is camelize('foo_bar_baz'), 'FooBarBaz', 'right camelized result'; + is camelize('FooBarBaz'), 'FooBarBaz', 'right camelized result'; + is camelize('foo_b_b'), 'FooBB', 'right camelized result'; + is camelize('foo-b_b'), 'Foo::BB', 'right camelized result'; + is camelize('FooBar'), 'FooBar', 'already camelized'; + is camelize('Foo::Bar'), 'Foo::Bar', 'already camelized'; +}; + +subtest 'decamelize' => sub { + is decamelize('FooBarBaz'), 'foo_bar_baz', 'right decamelized result'; + is decamelize('foo_bar_baz'), 'foo_bar_baz', 'right decamelized result'; + is decamelize('FooBB'), 'foo_b_b', 'right decamelized result'; + is decamelize('Foo::BB'), 'foo-b_b', 'right decamelized result'; +}; + +subtest 'class_to_file' => sub { + is class_to_file('Foo::Bar'), 'foo_bar', 'right file'; + is class_to_file('FooBar'), 'foo_bar', 'right file'; + is class_to_file('FOOBar'), 'foobar', 'right file'; + is class_to_file('FOOBAR'), 'foobar', 'right file'; + is class_to_file('FOO::Bar'), 'foobar', 'right file'; + is class_to_file('FooBAR'), 'foo_bar', 'right file'; + is class_to_file('Foo::BAR'), 'foo_bar', 'right file'; + is class_to_file("Foo'BAR"), 'foo_bar', 'right file'; + is class_to_file("Foo'Bar::Baz"), 'foo_bar_baz', 'right file'; +}; + +subtest 'class_to_path' => sub { + is class_to_path('Foo::Bar'), 'Foo/Bar.pm', 'right path'; + is class_to_path("Foo'Bar"), 'Foo/Bar.pm', 'right path'; + is class_to_path("Foo'Bar::Baz"), 'Foo/Bar/Baz.pm', 'right path'; + is class_to_path("Foo::Bar'Baz"), 'Foo/Bar/Baz.pm', 'right path'; + is class_to_path("Foo::Bar::Baz"), 'Foo/Bar/Baz.pm', 'right path'; + is class_to_path("Foo'Bar'Baz"), 'Foo/Bar/Baz.pm', 'right path'; +}; + +subtest 'split_header' => sub { + is_deeply split_header(''), [], 'right result'; + is_deeply split_header('foo=b=a=r'), [['foo', 'b=a=r']], 'right result'; + is_deeply split_header('a=b ,, , c=d ;; ; e=f g h=i'), [['a', 'b'], ['c', 'd', 'e', 'f', 'g', undef, 'h', 'i']], + 'right result'; + is_deeply split_header(',,foo,, ,bar'), [['foo', undef], ['bar', undef]], 'right result'; + is_deeply split_header(';;foo; ; ;bar'), [['foo', undef, 'bar', undef]], 'right result'; + is_deeply split_header('foo=;bar=""'), [['foo', '', 'bar', '']], 'right result'; + is_deeply split_header('foo=bar baz=yada'), [['foo', 'bar', 'baz', 'yada']], 'right result'; + is_deeply split_header('foo,bar,baz'), [['foo', undef], ['bar', undef], ['baz', undef]], 'right result'; + is_deeply split_header('f "o" o , ba r'), [['f', undef, '"o"', undef, 'o', undef], ['ba', undef, 'r', undef]], + 'right result'; + is_deeply split_header('foo="b,; a\" r\"\\\\"'), [['foo', 'b,; a" r"\\']], 'right result'; + is_deeply split_header('foo = "b a\" r\"\\\\"; bar="ba z"'), [['foo', 'b a" r"\\', 'bar', 'ba z']], 'right result'; + my $header = q{; rel="x"; t*=UTF-8'de'a%20b}; + my $tree = [['', undef, 'rel', 'x', 't*', 'UTF-8\'de\'a%20b']]; + is_deeply split_header($header), $tree, 'right result'; + $header = 'a=b c; A=b.c; D=/E; a-b=3; expires=Thu, 07 Aug 2008 07:07:59 GMT; Ab;'; + $tree = [ + ['a', 'b', 'c', undef, 'A', 'b.c', 'D', '/E', 'a-b', '3', 'expires', 'Thu'], + ['07', undef, 'Aug', undef, '2008', undef, '07:07:59', undef, 'GMT', undef, 'Ab', undef] + ]; + is_deeply split_header($header), $tree, 'right result'; +}; + +subtest 'split_cookie_header' => sub { + is_deeply split_cookie_header(''), [], 'right result'; + is_deeply split_cookie_header('a=b; expires=Thu, 07 Aug 2008 07:07:59 GMT,c=d'), + [['a', 'b', 'expires', 'Thu, 07 Aug 2008 07:07:59 GMT'], ['c', 'd']], 'right result'; + is_deeply split_cookie_header('a=b; expires=Tuesday, 09-Nov-1999 23:12:40 GMT, c=d'), + [['a', 'b', 'expires', 'Tuesday, 09-Nov-1999 23:12:40 GMT'], ['c', 'd']], 'right result'; + is_deeply split_cookie_header('a=b; expires=Tuesday, 09-Nov-1999 23:12:40 GMT;, c=d;'), + [['a', 'b', 'expires', 'Tuesday, 09-Nov-1999 23:12:40 GMT'], ['c', 'd']], 'right result'; + is_deeply split_cookie_header('a=b; expires=Sun,06 Nov 1994 08:49:37 UTC; path=/'), + [['a', 'b', 'expires', 'Sun,06 Nov 1994 08:49:37 UTC', 'path', '/']], 'right result'; + is_deeply split_cookie_header('a=b ; expires = Sunday 06 Nov 94 08:49:37UTC ; path=/'), + [['a', 'b', 'expires', 'Sunday 06 Nov 94 08:49:37UTC', 'path', '/']], 'right result'; + my $header = 'expires=Thu, 07 Aug 2008 07:07:59 GMT, a=b'; + my $tree + = [['expires', 'Thu'], ['07', undef, 'Aug', undef, '2008', undef, '07:07:59', undef, 'GMT', undef], ['a', 'b']]; + is_deeply split_cookie_header($header), $tree, 'right result'; +}; + +subtest 'extract_usage' => sub { + is extract_usage, "extract_usage test!\n", 'right result'; + is extract_usage(curfile->sibling('lib', 'myapp.pl')), "USAGE: myapp.pl daemon\n\n test\n123\n", 'right result'; +}; =head1 SYNOPSIS @@ -99,26 +107,26 @@ is extract_usage(curfile->sibling('lib', 'myapp.pl')), "USAGE: myapp.pl daemon\n =cut -# getopt -getopt ['--charset', 'UTF-8'], 'c|charset=s' => \my $charset; -is $charset, 'UTF-8', 'right string'; -my $array = ['-t', 'test', '-h', '--whatever', 'Whatever!', 'stuff']; -getopt $array, ['pass_through'], 't|test=s' => \my $test; -is $test, 'test', 'right string'; -is_deeply $array, ['-h', '--whatever', 'Whatever!', 'stuff'], 'right structure'; -getopt $array, 'h' => \my $flag, 'w|whatever=s' => \my $whatever; -ok $flag, 'flag has been set'; -is $whatever, 'Whatever!', 'right string'; -is_deeply $array, ['stuff'], 'right structure'; -{ - local @ARGV = ('--charset', 'UTF-16', 'test'); - getopt 'c|charset=s' => \my @charset; - is_deeply \@charset, ['UTF-16'], 'right structure'; - is_deeply \@ARGV, ['test'], 'right structure'; -} - -# getopt (return value) -{ +subtest 'getopt' => sub { + getopt ['--charset', 'UTF-8'], 'c|charset=s' => \my $charset; + is $charset, 'UTF-8', 'right string'; + my $array = ['-t', 'test', '-h', '--whatever', 'Whatever!', 'stuff']; + getopt $array, ['pass_through'], 't|test=s' => \my $test; + is $test, 'test', 'right string'; + is_deeply $array, ['-h', '--whatever', 'Whatever!', 'stuff'], 'right structure'; + getopt $array, 'h' => \my $flag, 'w|whatever=s' => \my $whatever; + ok $flag, 'flag has been set'; + is $whatever, 'Whatever!', 'right string'; + is_deeply $array, ['stuff'], 'right structure'; + { + local @ARGV = ('--charset', 'UTF-16', 'test'); + getopt 'c|charset=s' => \my @charset; + is_deeply \@charset, ['UTF-16'], 'right structure'; + is_deeply \@ARGV, ['test'], 'right structure'; + } +}; + +subtest 'getopt (return value)' => sub { local $SIG{__WARN__} = sub { }; my $return = getopt ['--lang', 'de'], 'l|lang=s' => \my $lang; @@ -134,317 +142,358 @@ is_deeply $array, ['stuff'], 'right structure'; $return = getopt ['--lnag', 'de', '--lang', 'de'], 'l|lang=s' => \$lang; is $lang, 'de', 'right result'; ok !$return, 'right return value'; -} - -# unindent -is unindent(" test\n 123\n 456\n"), "test\n 123\n456\n", 'right unindented result'; -is unindent("\ttest\n\t\t123\n\t456\n"), "test\n\t123\n456\n", 'right unindented result'; -is unindent("\t \ttest\n\t \t\t123\n\t \t456\n"), "test\n\t123\n456\n", 'right unindented result'; -is unindent("\n\n\n test\n 123\n 456\n"), "\n\n\ntest\n 123\n456\n", 'right unindented result'; -is unindent(" test\n 123\n 456\n"), "test\n 123\n456\n", 'right unindented result'; -is unindent(" test\n 123\n 456\n"), " test\n123\n 456\n", 'right unindented result'; -is unindent("test\n123\n"), "test\n123\n", 'right unindented result'; -is unindent(" test\n\n 123\n"), "test\n\n123\n", 'right unindented result'; -is unindent(' test'), 'test', 'right unindented result'; -is unindent(" te st\r\n\r\n 1 2 3\r\n 456\r\n"), "te st\r\n\r\n 1 2 3\r\n456\r\n", 'right unindented result'; - -# b64_encode -is b64_encode('foobar$%^&3217'), "Zm9vYmFyJCVeJjMyMTc=\n", 'right Base64 encoded result'; - -# b64_decode -is b64_decode("Zm9vYmFyJCVeJjMyMTc=\n"), 'foobar$%^&3217', 'right Base64 decoded result'; - -# b64_encode (UTF-8) -is b64_encode(encode 'UTF-8', "foo\x{df}\x{0100}bar%23\x{263a}"), "Zm9vw5/EgGJhciUyM+KYug==\n", - 'right Base64 encoded result'; - -# b64_decode (UTF-8) -is decode('UTF-8', b64_decode "Zm9vw5/EgGJhciUyM+KYug==\n"), "foo\x{df}\x{0100}bar%23\x{263a}", - 'right Base64 decoded result'; - -# b64_encode (custom line ending) -is b64_encode('foobar$%^&3217', ''), 'Zm9vYmFyJCVeJjMyMTc=', 'right Base64 encoded result'; - -# decode (invalid UTF-8) -is decode('UTF-8', "\x{1000}"), undef, 'decoding invalid UTF-8 worked'; - -# decode (invalid encoding) -is decode('does_not_exist', ''), undef, 'decoding with invalid encoding worked'; - -# encode (invalid encoding) -eval { encode('does_not_exist', '') }; -like $@, qr/Unknown encoding 'does_not_exist'/, 'right error'; - -# url_escape -is url_escape('business;23'), 'business%3B23', 'right URL escaped result'; - -# url_escape (custom pattern) -is url_escape('&business;23', 's&'), '%26bu%73ine%73%73;23', 'right URL escaped result'; - -# url_escape (nothing to escape) -is url_escape('foobar123-._~'), 'foobar123-._~', 'no changes'; - -# url_unescape -is url_unescape('business%3B23'), 'business;23', 'right URL unescaped result'; - -# UTF-8 url_escape -is url_escape(encode 'UTF-8', "foo\x{df}\x{0100}bar\x{263a}"), 'foo%C3%9F%C4%80bar%E2%98%BA', - 'right URL escaped result'; - -# UTF-8 url_unescape -is decode('UTF-8', url_unescape 'foo%C3%9F%C4%80bar%E2%98%BA'), "foo\x{df}\x{0100}bar\x{263a}", - 'right URL unescaped result'; - -# html_unescape -is html_unescape('<foo>bar<baz>&"'), "bar&\"", 'right HTML unescaped result'; -is html_unescape('foo<baz>&"Œ&Foo;'), "foo&\"\x{152}&Foo;", 'right HTML unescaped result'; - -# html_unescape (special entities) -is html_unescape('foo ☃ ∳ bar ¹baz'), "foo ☃ \x{2233} bar ¹baz", - 'right HTML unescaped result'; - -# html_unescape (multi-character entity) -is html_unescape('∾̳'), "\x{223e}\x{0333}", 'right HTML unescaped result'; - -# html_unescape (apos) -is html_unescape('foobar'<baz>&"'), "foobar'&\"", 'right HTML unescaped result'; - -# html_unescape (nothing to unescape) -is html_unescape('foobar'), 'foobar', 'no changes'; - -# html_unescape (relaxed) -is html_unescape('&0&Ltf&&0oo ba;<r'), "&0&Ltf&&0oo\x{00a0}ba;\nbar"baz"'yada\n'<la}), "la<f>\nbar"baz"'yada\n'&lt;la", - 'right XML escaped result'; -is xml_escape('привет'), 'привет<foo>', 'right XML escaped result'; - -# xml_escape (nothing to escape) -is xml_escape('привет'), 'привет', 'no changes'; - -# xml_escape (XSS) -is xml_escape('

'), '<p>', 'right XSS escaped result'; -is xml_escape(b('

')), '

', 'right XSS escaped result'; - -# punycode_encode -is punycode_encode('bücher'), 'bcher-kva', 'right punycode encoded result'; - -# punycode_decode -is punycode_decode('bcher-kva'), 'bücher', 'right punycode decoded result'; - -# RFC 3492 -my @tests = ( - '(A) Arabic (Egyptian):', - "\x{0644}\x{064a}\x{0647}\x{0645}\x{0627}\x{0628}\x{062a}\x{0643}" - . "\x{0644}\x{0645}\x{0648}\x{0634}\x{0639}\x{0631}\x{0628}\x{064a}" - . "\x{061f}", - 'egbpdaj6bu4bxfgehfvwxn', - '(B) Chinese (simplified):', - "\x{4ed6}\x{4eec}\x{4e3a}\x{4ec0}\x{4e48}\x{4e0d}\x{8bf4}\x{4e2d}" . "\x{6587}", - 'ihqwcrb4cv8a8dqg056pqjye', - '(C) Chinese (traditional):', - "\x{4ed6}\x{5011}\x{7232}\x{4ec0}\x{9ebd}\x{4e0d}\x{8aaa}\x{4e2d}" . "\x{6587}", - 'ihqwctvzc91f659drss3x8bo0yb', - '(D) Czech: Proprostnemluvesky', - "\x{0050}\x{0072}\x{006f}\x{010d}\x{0070}\x{0072}\x{006f}\x{0073}" - . "\x{0074}\x{011b}\x{006e}\x{0065}\x{006d}\x{006c}\x{0075}\x{0076}" - . "\x{00ed}\x{010d}\x{0065}\x{0073}\x{006b}\x{0079}", - 'Proprostnemluvesky-uyb24dma41a', - '(E) Hebrew:', - "\x{05dc}\x{05de}\x{05d4}\x{05d4}\x{05dd}\x{05e4}\x{05e9}\x{05d5}" - . "\x{05d8}\x{05dc}\x{05d0}\x{05de}\x{05d3}\x{05d1}\x{05e8}\x{05d9}" - . "\x{05dd}\x{05e2}\x{05d1}\x{05e8}\x{05d9}\x{05ea}", - '4dbcagdahymbxekheh6e0a7fei0b', - '(F) Hindi (Devanagari):', - "\x{092f}\x{0939}\x{0932}\x{094b}\x{0917}\x{0939}\x{093f}\x{0928}" - . "\x{094d}\x{0926}\x{0940}\x{0915}\x{094d}\x{092f}\x{094b}\x{0902}" - . "\x{0928}\x{0939}\x{0940}\x{0902}\x{092c}\x{094b}\x{0932}\x{0938}" - . "\x{0915}\x{0924}\x{0947}\x{0939}\x{0948}\x{0902}", - 'i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd', - '(G) Japanese (kanji and hiragana):', - "\x{306a}\x{305c}\x{307f}\x{3093}\x{306a}\x{65e5}\x{672c}\x{8a9e}" - . "\x{3092}\x{8a71}\x{3057}\x{3066}\x{304f}\x{308c}\x{306a}\x{3044}" - . "\x{306e}\x{304b}", - 'n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa', - '(H) Korean (Hangul syllables):', - "\x{c138}\x{acc4}\x{c758}\x{baa8}\x{b4e0}\x{c0ac}\x{b78c}\x{b4e4}" - . "\x{c774}\x{d55c}\x{ad6d}\x{c5b4}\x{b97c}\x{c774}\x{d574}\x{d55c}" - . "\x{b2e4}\x{ba74}\x{c5bc}\x{b9c8}\x{b098}\x{c88b}\x{c744}\x{ae4c}", - '989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c', - '(I) Russian (Cyrillic):', - "\x{043f}\x{043e}\x{0447}\x{0435}\x{043c}\x{0443}\x{0436}\x{0435}" - . "\x{043e}\x{043d}\x{0438}\x{043d}\x{0435}\x{0433}\x{043e}\x{0432}" - . "\x{043e}\x{0440}\x{044f}\x{0442}\x{043f}\x{043e}\x{0440}\x{0443}" - . "\x{0441}\x{0441}\x{043a}\x{0438}", - 'b1abfaaepdrnnbgefbadotcwatmq2g4l', - '(J) Spanish: PorqunopuedensimplementehablarenEspaol', - "\x{0050}\x{006f}\x{0072}\x{0071}\x{0075}\x{00e9}\x{006e}\x{006f}" - . "\x{0070}\x{0075}\x{0065}\x{0064}\x{0065}\x{006e}\x{0073}\x{0069}" - . "\x{006d}\x{0070}\x{006c}\x{0065}\x{006d}\x{0065}\x{006e}\x{0074}" - . "\x{0065}\x{0068}\x{0061}\x{0062}\x{006c}\x{0061}\x{0072}\x{0065}" - . "\x{006e}\x{0045}\x{0073}\x{0070}\x{0061}\x{00f1}\x{006f}\x{006c}", - 'PorqunopuedensimplementehablarenEspaol-fmd56a', - '(K) Vietnamese: Tisaohkhngth' - . 'chnitingVi' - . 't', - "\x{0054}\x{1ea1}\x{0069}\x{0073}\x{0061}\x{006f}\x{0068}\x{1ecd}" - . "\x{006b}\x{0068}\x{00f4}\x{006e}\x{0067}\x{0074}\x{0068}\x{1ec3}" - . "\x{0063}\x{0068}\x{1ec9}\x{006e}\x{00f3}\x{0069}\x{0074}\x{0069}" - . "\x{1ebf}\x{006e}\x{0067}\x{0056}\x{0069}\x{1ec7}\x{0074}", - 'TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g', - '(L) 3B', - "\x{0033}\x{5e74}\x{0042}\x{7d44}\x{91d1}\x{516b}\x{5148}\x{751f}", - '3B-ww4c5e180e575a65lsy2b', - '(M) -with-SUPER-MONKEYS', - "\x{5b89}\x{5ba4}\x{5948}\x{7f8e}\x{6075}\x{002d}\x{0077}\x{0069}" - . "\x{0074}\x{0068}\x{002d}\x{0053}\x{0055}\x{0050}\x{0045}\x{0052}" - . "\x{002d}\x{004d}\x{004f}\x{004e}\x{004b}\x{0045}\x{0059}\x{0053}", - '-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n', - '(N) Hello-Another-Way-', - "\x{0048}\x{0065}\x{006c}\x{006c}\x{006f}\x{002d}\x{0041}\x{006e}" - . "\x{006f}\x{0074}\x{0068}\x{0065}\x{0072}\x{002d}\x{0057}\x{0061}" - . "\x{0079}\x{002d}\x{305d}\x{308c}\x{305e}\x{308c}\x{306e}\x{5834}" - . "\x{6240}", - 'Hello-Another-Way--fc4qua05auwb3674vfr0b', - '(O) 2', - "\x{3072}\x{3068}\x{3064}\x{5c4b}\x{6839}\x{306e}\x{4e0b}\x{0032}", - '2-u9tlzr9756bt3uc0v', - '(P) MajiKoi5', - "\x{004d}\x{0061}\x{006a}\x{0069}\x{3067}\x{004b}\x{006f}\x{0069}" . "\x{3059}\x{308b}\x{0035}\x{79d2}\x{524d}", - 'MajiKoi5-783gue6qz075azm5e', - '(Q) de', - "\x{30d1}\x{30d5}\x{30a3}\x{30fc}\x{0064}\x{0065}\x{30eb}\x{30f3}" . "\x{30d0}", - 'de-jg4avhby1noc0d', - '(R) ', - "\x{305d}\x{306e}\x{30b9}\x{30d4}\x{30fc}\x{30c9}\x{3067}", - 'd9juau41awczczp', - '(S) -> $1.00 <-', - "\x{002d}\x{003e}\x{0020}\x{0024}\x{0031}\x{002e}\x{0030}\x{0030}" . "\x{0020}\x{003c}\x{002d}", - '-> $1.00 <--' -); - -for (my $i = 0; $i < @tests; $i += 3) { - my ($d, $o, $p) = @tests[$i, $i + 1, $i + 2]; - is punycode_encode($o), $p, "punycode_encode $d"; - is punycode_decode($p), $o, "punycode_decode $d"; -} - -# quote -is quote('foo; 23 "bar'), '"foo; 23 \"bar"', 'right quoted result'; -is quote('"foo; 23 "bar"'), '"\"foo; 23 \"bar\""', 'right quoted result'; - -# unquote -is unquote('"foo 23 \"bar"'), 'foo 23 "bar', 'right unquoted result'; -is unquote('"\"foo 23 \"bar\""'), '"foo 23 "bar"', 'right unquoted result'; - -# trim -is trim(' la la la '), 'la la la', 'right trimmed result'; -is trim(" \n la la la \n "), 'la la la', 'right trimmed result'; -is trim("\n la\nla la \n"), "la\nla la", 'right trimmed result'; -is trim(" \nla \n \t\nla\nla\n "), "la \n \t\nla\nla", 'right trimmed result'; - -# md5_bytes -is unpack('H*', md5_bytes(encode 'UTF-8', 'foo bar baz ♥')), 'a740aeb6e066f158cbf19fd92e890d2d', - 'right binary md5 checksum'; - -# md5_sum -is md5_sum('foo bar baz'), 'ab07acbb1e496801937adfa772424bf7', 'right md5 checksum'; - -# sha1_bytes -is unpack('H*', sha1_bytes 'foo bar baz'), 'c7567e8b39e2428e38bf9c9226ac68de4c67dc39', 'right binary sha1 checksum'; - -# sha1_sum -is sha1_sum('foo bar baz'), 'c7567e8b39e2428e38bf9c9226ac68de4c67dc39', 'right sha1 checksum'; - -# hmac_sha1_sum -is hmac_sha1_sum('Hi there', 'abc1234567890'), '5344f37e1948dd3ffb07243a4d9201a227abd6e1', 'right hmac sha1 checksum'; - -# secure_compare -ok secure_compare('hello', 'hello'), 'values are equal'; -ok !secure_compare('hell', 'hello'), 'values are not equal'; -ok !secure_compare('hallo', 'hello'), 'values are not equal'; -ok secure_compare('0', '0'), 'values are equal'; -ok secure_compare('1', '1'), 'values are equal'; -ok !secure_compare('1', '0'), 'values are not equal'; -ok !secure_compare('0', '1'), 'values are not equal'; -ok secure_compare('00', '00'), 'values are equal'; -ok secure_compare('11', '11'), 'values are equal'; -ok !secure_compare('11', '00'), 'values are not equal'; -ok !secure_compare('00', '11'), 'values are not equal'; -ok secure_compare('♥', '♥'), 'values are equal'; -ok secure_compare('0♥', '0♥'), 'values are equal'; -ok secure_compare('♥1', '♥1'), 'values are equal'; -ok !secure_compare('♥', '♥0'), 'values are not equal'; -ok !secure_compare('0♥', '♥'), 'values are not equal'; -ok !secure_compare('0♥1', '1♥0'), 'values are not equal'; -ok !secure_compare('', '♥'), 'values are not equal'; -ok !secure_compare('♥', ''), 'values are not equal'; - -# xor_encode -is xor_encode('hello', 'foo'), "\x0e\x0a\x03\x0a\x00", 'right result'; -is xor_encode("\x0e\x0a\x03\x0a\x00", 'foo'), 'hello', 'right result'; -is xor_encode('hello world', 'x'), "\x10\x1d\x14\x14\x17\x58\x0f\x17\x0a\x14\x1c", 'right result'; -is xor_encode("\x10\x1d\x14\x14\x17\x58\x0f\x17\x0a\x14\x1c", 'x'), 'hello world', 'right result'; -is xor_encode('hello', '123456789'), "\x59\x57\x5f\x58\x5a", 'right result'; -is xor_encode("\x59\x57\x5f\x58\x5a", '123456789'), 'hello', 'right result'; - -# steady_time -like steady_time, qr/^[\d.]+$/, 'high resolution time'; - -# monkey_patch -{ - - package MojoMonkeyTest; - sub foo {'foo'} -} -ok !!MojoMonkeyTest->can('foo'), 'function "foo" exists'; -is MojoMonkeyTest::foo(), 'foo', 'right result'; -ok !MojoMonkeyTest->can('bar'), 'function "bar" does not exist'; -monkey_patch 'MojoMonkeyTest', bar => sub {'bar'}; -ok !!MojoMonkeyTest->can('bar'), 'function "bar" exists'; -is MojoMonkeyTest::bar(), 'bar', 'right result'; -monkey_patch 'MojoMonkeyTest', foo => sub {'baz'}; -ok !!MojoMonkeyTest->can('foo'), 'function "foo" exists'; -is MojoMonkeyTest::foo(), 'baz', 'right result'; -ok !MojoMonkeyTest->can('yin'), 'function "yin" does not exist'; -ok !MojoMonkeyTest->can('yang'), 'function "yang" does not exist'; -monkey_patch 'MojoMonkeyTest', - yin => sub {'yin'}, - yang => sub {'yang'}; -ok !!MojoMonkeyTest->can('yin'), 'function "yin" exists'; -is MojoMonkeyTest::yin(), 'yin', 'right result'; -ok !!MojoMonkeyTest->can('yang'), 'function "yang" exists'; -is MojoMonkeyTest::yang(), 'yang', 'right result'; - -# monkey_patch (with name) -is subname(MojoMonkeyTest->can('foo')), 'MojoMonkeyTest::foo', 'right name'; -is subname(MojoMonkeyTest->can('bar')), 'MojoMonkeyTest::bar', 'right name'; - -# tablify -is tablify([["f\r\no o\r\n", 'bar']]), "fo o bar\n", 'right result'; -is tablify([[" foo", ' b a r']]), " foo b a r\n", 'right result'; -is tablify([['foo']]), "foo\n", 'right result'; -is tablify([['foo', 'yada'], ['yada', 'yada']]), "foo yada\nyada yada\n", 'right result'; -is tablify([[undef, 'yada'], ['yada', undef]]), " yada\nyada \n", 'right result'; -is tablify([['foo', 'bar', 'baz'], ['yada', 'yada', 'yada']]), "foo bar baz\nyada yada yada\n", 'right result'; -is tablify([['a', '', 0], [0, '', 'b']]), "a 0\n0 b\n", 'right result'; -is tablify([[1, 2], [3]]), "1 2\n3\n", 'right result'; -is tablify([[1], [2, 3]]), "1\n2 3\n", 'right result'; -is tablify([[1], [], [2, 3]]), "1\n\n2 3\n", 'right result'; - -# deprecated -{ +}; + +subtest 'unindent' => sub { + is unindent(" test\n 123\n 456\n"), "test\n 123\n456\n", 'right unindented result'; + is unindent("\ttest\n\t\t123\n\t456\n"), "test\n\t123\n456\n", 'right unindented result'; + is unindent("\t \ttest\n\t \t\t123\n\t \t456\n"), "test\n\t123\n456\n", 'right unindented result'; + is unindent("\n\n\n test\n 123\n 456\n"), "\n\n\ntest\n 123\n456\n", 'right unindented result'; + is unindent(" test\n 123\n 456\n"), "test\n 123\n456\n", 'right unindented result'; + is unindent(" test\n 123\n 456\n"), " test\n123\n 456\n", 'right unindented result'; + is unindent("test\n123\n"), "test\n123\n", 'right unindented result'; + is unindent(" test\n\n 123\n"), "test\n\n123\n", 'right unindented result'; + is unindent(' test'), 'test', 'right unindented result'; + is unindent(" te st\r\n\r\n 1 2 3\r\n 456\r\n"), "te st\r\n\r\n 1 2 3\r\n456\r\n", 'right unindented result'; +}; + +subtest 'b64_encode' => sub { + is b64_encode('foobar$%^&3217'), "Zm9vYmFyJCVeJjMyMTc=\n", 'right Base64 encoded result'; +}; + +subtest 'b64_decode' => sub { + is b64_decode("Zm9vYmFyJCVeJjMyMTc=\n"), 'foobar$%^&3217', 'right Base64 decoded result'; +}; + +subtest 'b64_encode (UTF-8)' => sub { + is b64_encode(encode 'UTF-8', "foo\x{df}\x{0100}bar%23\x{263a}"), "Zm9vw5/EgGJhciUyM+KYug==\n", + 'right Base64 encoded result'; +}; + +subtest 'b64_decode (UTF-8)' => sub { + is decode('UTF-8', b64_decode "Zm9vw5/EgGJhciUyM+KYug==\n"), "foo\x{df}\x{0100}bar%23\x{263a}", + 'right Base64 decoded result'; +}; + +subtest 'b64_encode (custom line ending)' => sub { + is b64_encode('foobar$%^&3217', ''), 'Zm9vYmFyJCVeJjMyMTc=', 'right Base64 encoded result'; +}; + +subtest 'decode (invalid UTF-8)' => sub { + is decode('UTF-8', "\x{1000}"), undef, 'decoding invalid UTF-8 worked'; +}; + +subtest 'decode (invalid encoding)' => sub { + is decode('does_not_exist', ''), undef, 'decoding with invalid encoding worked'; +}; + +subtest 'encode (invalid encoding)' => sub { + eval { encode('does_not_exist', '') }; + like $@, qr/Unknown encoding 'does_not_exist'/, 'right error'; +}; + +subtest 'url_escape' => sub { + is url_escape('business;23'), 'business%3B23', 'right URL escaped result'; +}; + +subtest 'url_escape (custom pattern)' => sub { + is url_escape('&business;23', 's&'), '%26bu%73ine%73%73;23', 'right URL escaped result'; +}; + +subtest 'url_escape (nothing to escape)' => sub { + is url_escape('foobar123-._~'), 'foobar123-._~', 'no changes'; +}; + +subtest 'url_unescape' => sub { + is url_unescape('business%3B23'), 'business;23', 'right URL unescaped result'; +}; + +subtest 'UTF-8 url_escape' => sub { + is url_escape(encode 'UTF-8', "foo\x{df}\x{0100}bar\x{263a}"), 'foo%C3%9F%C4%80bar%E2%98%BA', + 'right URL escaped result'; +}; + +subtest 'UTF-8 url_unescape' => sub { + is decode('UTF-8', url_unescape 'foo%C3%9F%C4%80bar%E2%98%BA'), "foo\x{df}\x{0100}bar\x{263a}", + 'right URL unescaped result'; +}; + +subtest 'html_unescape' => sub { + is html_unescape('<foo>bar<baz>&"'), "bar&\"", 'right HTML unescaped result'; + is html_unescape('foo<baz>&"Œ&Foo;'), "foo&\"\x{152}&Foo;", 'right HTML unescaped result'; +}; + +subtest 'html_unescape (special entities)' => sub { + is html_unescape('foo ☃ ∳ bar ¹baz'), "foo ☃ \x{2233} bar ¹baz", + 'right HTML unescaped result'; +}; + +subtest 'html_unescape (multi-character entity)' => sub { + is html_unescape('∾̳'), "\x{223e}\x{0333}", 'right HTML unescaped result'; +}; + +subtest 'html_unescape (apos)' => sub { + is html_unescape('foobar'<baz>&"'), "foobar'&\"", 'right HTML unescaped result'; +}; + +subtest 'html_unescape (nothing to unescape)' => sub { + is html_unescape('foobar'), 'foobar', 'no changes'; +}; + +subtest 'html_unescape (relaxed)' => sub { + is html_unescape('&0&Ltf&&0oo ba;<r'), "&0&Ltf&&0oo\x{00a0}ba; sub { + is html_attr_unescape('/?foo<=bar'), '/?foo<=bar', 'right HTML unescaped result'; + is html_attr_unescape('/?f<oo=bar'), '/?f<oo=bar', 'right HTML unescaped result'; + is html_attr_unescape('/?f<-oo=bar'), '/?f<-oo=bar', 'right HTML unescaped result'; + is html_attr_unescape('/?foo=<'), '/?foo=<', 'right HTML unescaped result'; + is html_attr_unescape('/?f<oo=bar'), '/?f sub { + is html_unescape('&#০৩৯;&#x০৩৯;'), '&#০৩৯;&#x০৩৯;', 'no changes'; +}; + +subtest 'xml_escape' => sub { + is xml_escape(qq{la\nbar"baz"'yada\n'<la}), "la<f>\nbar"baz"'yada\n'&lt;la", + 'right XML escaped result'; + is xml_escape('привет'), 'привет<foo>', 'right XML escaped result'; +}; + +subtest 'xml_escape (nothing to escape)' => sub { + is xml_escape('привет'), 'привет', 'no changes'; +}; + +subtest 'xml_escape (XSS)' => sub { + is xml_escape('

'), '<p>', 'right XSS escaped result'; + is xml_escape(b('

')), '

', 'right XSS escaped result'; +}; + +subtest 'punycode_encode' => sub { + is punycode_encode('bücher'), 'bcher-kva', 'right punycode encoded result'; +}; + +subtest 'punycode_decode' => sub { + is punycode_decode('bcher-kva'), 'bücher', 'right punycode decoded result'; +}; + +subtest 'RFC 3492' => sub { + my @tests = ( + '(A) Arabic (Egyptian):', + "\x{0644}\x{064a}\x{0647}\x{0645}\x{0627}\x{0628}\x{062a}\x{0643}" + . "\x{0644}\x{0645}\x{0648}\x{0634}\x{0639}\x{0631}\x{0628}\x{064a}" + . "\x{061f}", + 'egbpdaj6bu4bxfgehfvwxn', + '(B) Chinese (simplified):', + "\x{4ed6}\x{4eec}\x{4e3a}\x{4ec0}\x{4e48}\x{4e0d}\x{8bf4}\x{4e2d}" . "\x{6587}", + 'ihqwcrb4cv8a8dqg056pqjye', + '(C) Chinese (traditional):', + "\x{4ed6}\x{5011}\x{7232}\x{4ec0}\x{9ebd}\x{4e0d}\x{8aaa}\x{4e2d}" . "\x{6587}", + 'ihqwctvzc91f659drss3x8bo0yb', + '(D) Czech: Proprostnemluvesky', + "\x{0050}\x{0072}\x{006f}\x{010d}\x{0070}\x{0072}\x{006f}\x{0073}" + . "\x{0074}\x{011b}\x{006e}\x{0065}\x{006d}\x{006c}\x{0075}\x{0076}" + . "\x{00ed}\x{010d}\x{0065}\x{0073}\x{006b}\x{0079}", + 'Proprostnemluvesky-uyb24dma41a', + '(E) Hebrew:', + "\x{05dc}\x{05de}\x{05d4}\x{05d4}\x{05dd}\x{05e4}\x{05e9}\x{05d5}" + . "\x{05d8}\x{05dc}\x{05d0}\x{05de}\x{05d3}\x{05d1}\x{05e8}\x{05d9}" + . "\x{05dd}\x{05e2}\x{05d1}\x{05e8}\x{05d9}\x{05ea}", + '4dbcagdahymbxekheh6e0a7fei0b', + '(F) Hindi (Devanagari):', + "\x{092f}\x{0939}\x{0932}\x{094b}\x{0917}\x{0939}\x{093f}\x{0928}" + . "\x{094d}\x{0926}\x{0940}\x{0915}\x{094d}\x{092f}\x{094b}\x{0902}" + . "\x{0928}\x{0939}\x{0940}\x{0902}\x{092c}\x{094b}\x{0932}\x{0938}" + . "\x{0915}\x{0924}\x{0947}\x{0939}\x{0948}\x{0902}", + 'i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd', + '(G) Japanese (kanji and hiragana):', + "\x{306a}\x{305c}\x{307f}\x{3093}\x{306a}\x{65e5}\x{672c}\x{8a9e}" + . "\x{3092}\x{8a71}\x{3057}\x{3066}\x{304f}\x{308c}\x{306a}\x{3044}" + . "\x{306e}\x{304b}", + 'n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa', + '(H) Korean (Hangul syllables):', + "\x{c138}\x{acc4}\x{c758}\x{baa8}\x{b4e0}\x{c0ac}\x{b78c}\x{b4e4}" + . "\x{c774}\x{d55c}\x{ad6d}\x{c5b4}\x{b97c}\x{c774}\x{d574}\x{d55c}" + . "\x{b2e4}\x{ba74}\x{c5bc}\x{b9c8}\x{b098}\x{c88b}\x{c744}\x{ae4c}", + '989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c', + '(I) Russian (Cyrillic):', + "\x{043f}\x{043e}\x{0447}\x{0435}\x{043c}\x{0443}\x{0436}\x{0435}" + . "\x{043e}\x{043d}\x{0438}\x{043d}\x{0435}\x{0433}\x{043e}\x{0432}" + . "\x{043e}\x{0440}\x{044f}\x{0442}\x{043f}\x{043e}\x{0440}\x{0443}" + . "\x{0441}\x{0441}\x{043a}\x{0438}", + 'b1abfaaepdrnnbgefbadotcwatmq2g4l', + '(J) Spanish: PorqunopuedensimplementehablarenEspaol', + "\x{0050}\x{006f}\x{0072}\x{0071}\x{0075}\x{00e9}\x{006e}\x{006f}" + . "\x{0070}\x{0075}\x{0065}\x{0064}\x{0065}\x{006e}\x{0073}\x{0069}" + . "\x{006d}\x{0070}\x{006c}\x{0065}\x{006d}\x{0065}\x{006e}\x{0074}" + . "\x{0065}\x{0068}\x{0061}\x{0062}\x{006c}\x{0061}\x{0072}\x{0065}" + . "\x{006e}\x{0045}\x{0073}\x{0070}\x{0061}\x{00f1}\x{006f}\x{006c}", + 'PorqunopuedensimplementehablarenEspaol-fmd56a', + '(K) Vietnamese: Tisaohkhngth' + . 'chnitingVi' + . 't', + "\x{0054}\x{1ea1}\x{0069}\x{0073}\x{0061}\x{006f}\x{0068}\x{1ecd}" + . "\x{006b}\x{0068}\x{00f4}\x{006e}\x{0067}\x{0074}\x{0068}\x{1ec3}" + . "\x{0063}\x{0068}\x{1ec9}\x{006e}\x{00f3}\x{0069}\x{0074}\x{0069}" + . "\x{1ebf}\x{006e}\x{0067}\x{0056}\x{0069}\x{1ec7}\x{0074}", + 'TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g', + '(L) 3B', + "\x{0033}\x{5e74}\x{0042}\x{7d44}\x{91d1}\x{516b}\x{5148}\x{751f}", + '3B-ww4c5e180e575a65lsy2b', + '(M) -with-SUPER-MONKEYS', + "\x{5b89}\x{5ba4}\x{5948}\x{7f8e}\x{6075}\x{002d}\x{0077}\x{0069}" + . "\x{0074}\x{0068}\x{002d}\x{0053}\x{0055}\x{0050}\x{0045}\x{0052}" + . "\x{002d}\x{004d}\x{004f}\x{004e}\x{004b}\x{0045}\x{0059}\x{0053}", + '-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n', + '(N) Hello-Another-Way-', + "\x{0048}\x{0065}\x{006c}\x{006c}\x{006f}\x{002d}\x{0041}\x{006e}" + . "\x{006f}\x{0074}\x{0068}\x{0065}\x{0072}\x{002d}\x{0057}\x{0061}" + . "\x{0079}\x{002d}\x{305d}\x{308c}\x{305e}\x{308c}\x{306e}\x{5834}" + . "\x{6240}", + 'Hello-Another-Way--fc4qua05auwb3674vfr0b', + '(O) 2', + "\x{3072}\x{3068}\x{3064}\x{5c4b}\x{6839}\x{306e}\x{4e0b}\x{0032}", + '2-u9tlzr9756bt3uc0v', + '(P) MajiKoi5', + "\x{004d}\x{0061}\x{006a}\x{0069}\x{3067}\x{004b}\x{006f}\x{0069}" . "\x{3059}\x{308b}\x{0035}\x{79d2}\x{524d}", + 'MajiKoi5-783gue6qz075azm5e', + '(Q) de', + "\x{30d1}\x{30d5}\x{30a3}\x{30fc}\x{0064}\x{0065}\x{30eb}\x{30f3}" . "\x{30d0}", + 'de-jg4avhby1noc0d', + '(R) ', + "\x{305d}\x{306e}\x{30b9}\x{30d4}\x{30fc}\x{30c9}\x{3067}", + 'd9juau41awczczp', + '(S) -> $1.00 <-', + "\x{002d}\x{003e}\x{0020}\x{0024}\x{0031}\x{002e}\x{0030}\x{0030}" . "\x{0020}\x{003c}\x{002d}", + '-> $1.00 <--' + ); + + for (my $i = 0; $i < @tests; $i += 3) { + my ($d, $o, $p) = @tests[$i, $i + 1, $i + 2]; + is punycode_encode($o), $p, "punycode_encode $d"; + is punycode_decode($p), $o, "punycode_decode $d"; + } +}; + +subtest 'quote' => sub { + is quote('foo; 23 "bar'), '"foo; 23 \"bar"', 'right quoted result'; + is quote('"foo; 23 "bar"'), '"\"foo; 23 \"bar\""', 'right quoted result'; +}; + +subtest 'unquote' => sub { + is unquote('"foo 23 \"bar"'), 'foo 23 "bar', 'right unquoted result'; + is unquote('"\"foo 23 \"bar\""'), '"foo 23 "bar"', 'right unquoted result'; +}; + +subtest 'trim' => sub { + is trim(' la la la '), 'la la la', 'right trimmed result'; + is trim(" \n la la la \n "), 'la la la', 'right trimmed result'; + is trim("\n la\nla la \n"), "la\nla la", 'right trimmed result'; + is trim(" \nla \n \t\nla\nla\n "), "la \n \t\nla\nla", 'right trimmed result'; +}; + +subtest 'md5_bytes' => sub { + is unpack('H*', md5_bytes(encode 'UTF-8', 'foo bar baz ♥')), 'a740aeb6e066f158cbf19fd92e890d2d', + 'right binary md5 checksum'; +}; + +subtest 'md5_sum' => sub { + is md5_sum('foo bar baz'), 'ab07acbb1e496801937adfa772424bf7', 'right md5 checksum'; +}; + +subtest 'sha1_bytes' => sub { + is unpack('H*', sha1_bytes 'foo bar baz'), 'c7567e8b39e2428e38bf9c9226ac68de4c67dc39', 'right binary sha1 checksum'; +}; + +subtest 'sha1_sum' => sub { + is sha1_sum('foo bar baz'), 'c7567e8b39e2428e38bf9c9226ac68de4c67dc39', 'right sha1 checksum'; +}; + +subtest 'hmac_sha1_sum' => sub { + is hmac_sha1_sum('Hi there', 'abc1234567890'), '5344f37e1948dd3ffb07243a4d9201a227abd6e1', 'right hmac sha1 checksum'; +}; + +subtest 'secure_compare' => sub { + ok secure_compare('hello', 'hello'), 'values are equal'; + ok !secure_compare('hell', 'hello'), 'values are not equal'; + ok !secure_compare('hallo', 'hello'), 'values are not equal'; + ok secure_compare('0', '0'), 'values are equal'; + ok secure_compare('1', '1'), 'values are equal'; + ok !secure_compare('1', '0'), 'values are not equal'; + ok !secure_compare('0', '1'), 'values are not equal'; + ok secure_compare('00', '00'), 'values are equal'; + ok secure_compare('11', '11'), 'values are equal'; + ok !secure_compare('11', '00'), 'values are not equal'; + ok !secure_compare('00', '11'), 'values are not equal'; + ok secure_compare('♥', '♥'), 'values are equal'; + ok secure_compare('0♥', '0♥'), 'values are equal'; + ok secure_compare('♥1', '♥1'), 'values are equal'; + ok !secure_compare('♥', '♥0'), 'values are not equal'; + ok !secure_compare('0♥', '♥'), 'values are not equal'; + ok !secure_compare('0♥1', '1♥0'), 'values are not equal'; + ok !secure_compare('', '♥'), 'values are not equal'; + ok !secure_compare('♥', ''), 'values are not equal'; +}; + +subtest 'xor_encode' => sub { + is xor_encode('hello', 'foo'), "\x0e\x0a\x03\x0a\x00", 'right result'; + is xor_encode("\x0e\x0a\x03\x0a\x00", 'foo'), 'hello', 'right result'; + is xor_encode('hello world', 'x'), "\x10\x1d\x14\x14\x17\x58\x0f\x17\x0a\x14\x1c", 'right result'; + is xor_encode("\x10\x1d\x14\x14\x17\x58\x0f\x17\x0a\x14\x1c", 'x'), 'hello world', 'right result'; + is xor_encode('hello', '123456789'), "\x59\x57\x5f\x58\x5a", 'right result'; + is xor_encode("\x59\x57\x5f\x58\x5a", '123456789'), 'hello', 'right result'; +}; + +subtest 'steady_time' => sub { + like steady_time, qr/^[\d.]+$/, 'high resolution time'; +}; + +subtest 'monkey_patch' => sub { + { + package MojoMonkeyTest; + sub foo {'foo'} + } + ok !!MojoMonkeyTest->can('foo'), 'function "foo" exists'; + is MojoMonkeyTest::foo(), 'foo', 'right result'; + ok !MojoMonkeyTest->can('bar'), 'function "bar" does not exist'; + monkey_patch 'MojoMonkeyTest', bar => sub {'bar'}; + ok !!MojoMonkeyTest->can('bar'), 'function "bar" exists'; + is MojoMonkeyTest::bar(), 'bar', 'right result'; + monkey_patch 'MojoMonkeyTest', foo => sub {'baz'}; + ok !!MojoMonkeyTest->can('foo'), 'function "foo" exists'; + is MojoMonkeyTest::foo(), 'baz', 'right result'; + ok !MojoMonkeyTest->can('yin'), 'function "yin" does not exist'; + ok !MojoMonkeyTest->can('yang'), 'function "yang" does not exist'; + monkey_patch 'MojoMonkeyTest', + yin => sub {'yin'}, + yang => sub {'yang'}; + ok !!MojoMonkeyTest->can('yin'), 'function "yin" exists'; + is MojoMonkeyTest::yin(), 'yin', 'right result'; + ok !!MojoMonkeyTest->can('yang'), 'function "yang" exists'; + is MojoMonkeyTest::yang(), 'yang', 'right result'; +}; + +subtest 'monkey_patch (with name)' => sub { + is subname(MojoMonkeyTest->can('foo')), 'MojoMonkeyTest::foo', 'right name'; + is subname(MojoMonkeyTest->can('bar')), 'MojoMonkeyTest::bar', 'right name'; +}; + +subtest 'tablify' => sub { + is tablify([["f\r\no o\r\n", 'bar']]), "fo o bar\n", 'right result'; + is tablify([[" foo", ' b a r']]), " foo b a r\n", 'right result'; + is tablify([['foo']]), "foo\n", 'right result'; + is tablify([['foo', 'yada'], ['yada', 'yada']]), "foo yada\nyada yada\n", 'right result'; + is tablify([[undef, 'yada'], ['yada', undef]]), " yada\nyada \n", 'right result'; + is tablify([['foo', 'bar', 'baz'], ['yada', 'yada', 'yada']]), "foo bar baz\nyada yada yada\n", 'right result'; + is tablify([['a', '', 0], [0, '', 'b']]), "a 0\n0 b\n", 'right result'; + is tablify([[1, 2], [3]]), "1 2\n3\n", 'right result'; + is tablify([[1], [2, 3]]), "1\n2 3\n", 'right result'; + is tablify([[1], [], [2, 3]]), "1\n\n2 3\n", 'right result'; +}; + +subtest 'deprecated' => sub { my ($warn, $die) = @_; local $SIG{__WARN__} = sub { $warn = shift }; local $SIG{__DIE__} = sub { $die = shift }; @@ -456,70 +505,77 @@ is tablify([[1], [], [2, 3]]), "1\n\n2 3\n", 'right result'; ok !eval { Mojo::DeprecationTest::foo() }, 'no result'; ok !$warn, 'no warning'; like $die, qr/foo is DEPRECATED at .*util\.t line \d+/, 'right exception'; -} - -# dumper -is dumper([1, 2]), "[\n 1,\n 2\n]\n", 'right result'; - -# term_escape -is term_escape("Accept: */*\x0d\x0a"), "Accept: */*\\x0d\x0a", 'right result'; -is term_escape("\t\b\r\n\f"), "\\x09\\x08\\x0d\n\\x0c", 'right result'; -is term_escape("\x00\x09\x0b\x1f\x7f\x80\x9f"), '\x00\x09\x0b\x1f\x7f\x80\x9f', 'right result'; - -# slugify -is slugify('a & b'), 'a-b', 'right result'; -is slugify('a & b'), 'a-amp-b', 'right result'; -is slugify(123), '123', 'right result'; -is slugify(' Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/'), - 'jack-jill-like-numbers-123-and-4-and-silly-characters', 'right result'; -is slugify("Un \x{e9}l\x{e9}phant \x{e0} l'or\x{e9}e du bois"), 'un-elephant-a-loree-du-bois', 'right result'; -is slugify("Un \x{e9}l\x{e9}phant \x{e0} l'or\x{e9}e du bois", 1), "un-\x{e9}l\x{e9}phant-\x{e0}-lor\x{e9}e-du-bois", - 'right result'; -is slugify('Hello, World!'), 'hello-world', 'right result'; -is slugify('spam & eggs'), 'spam-eggs', 'right result'; -is slugify('spam & ıçüş', 1), 'spam-ıçüş', 'right result'; -is slugify('foo ıç bar', 1), 'foo-ıç-bar', 'right result'; -is slugify(' foo ıç bar', 1), 'foo-ıç-bar', 'right result'; -is slugify('你好', 1), '你好', 'right result'; - -# gzip/gunzip -my $uncompressed = 'a' x 1000; -my $compressed = gzip $uncompressed; -isnt $compressed, $uncompressed, 'string changed'; -ok length $compressed < length $uncompressed, 'string is shorter'; -my $result = gunzip $compressed; -is $result, $uncompressed, 'same string'; - -# scope_guard -$test = 'a'; -{ - my $guard = scope_guard sub { $test .= 'c' }; - $test .= 'b'; -} -$test .= 'd'; -is $test, 'abcd', 'right order'; - -# humanize_bytes -is humanize_bytes(0), '0B', 'zero Bytes'; -is humanize_bytes(1), '1B', 'one Byte'; -is humanize_bytes(-1023), '-1023B', 'negative Bytes'; -is humanize_bytes(1024), '1KiB', 'one KiB'; -is humanize_bytes(1025), '1KiB', 'one KiB'; -is humanize_bytes(1024 * 1024), '1MiB', 'one MiB'; -is humanize_bytes(1024 * 1024 * 1024), '1GiB', 'one GiB'; -is humanize_bytes(1024 * 1024 * 1024 * 1024), '1TiB', 'one TiB'; -is humanize_bytes(3000), '2.9KiB', 'almost 3KiB'; -is humanize_bytes(-3000), '-2.9KiB', 'almost -3KiB'; -is humanize_bytes(13443399680), '13GiB', 'two digits GiB'; -is humanize_bytes(8007188480), '7.5GiB', 'smaller GiB'; -is humanize_bytes(-8007188480), '-7.5GiB', 'negative smaller GiB'; -is humanize_bytes(-1099511627776), '-1TiB', 'negative smaller TiB'; -is humanize_bytes(717946880), '685MiB', 'large MiB'; -is humanize_bytes(-717946880), '-685MiB', 'large negative MiB'; -is humanize_bytes(245760), '240KiB', 'less than a MiB'; - -# Hide DATA usage from error messages -eval { die 'whatever' }; -unlike $@, qr/DATA/, 'DATA has been hidden'; +}; + +subtest 'dumper' => sub { + is dumper([1, 2]), "[\n 1,\n 2\n]\n", 'right result'; +}; + +subtest 'term_escape' => sub { + is term_escape("Accept: */*\x0d\x0a"), "Accept: */*\\x0d\x0a", 'right result'; + is term_escape("\t\b\r\n\f"), "\\x09\\x08\\x0d\n\\x0c", 'right result'; + is term_escape("\x00\x09\x0b\x1f\x7f\x80\x9f"), '\x00\x09\x0b\x1f\x7f\x80\x9f', 'right result'; +}; + +subtest 'slugify' => sub { + is slugify('a & b'), 'a-b', 'right result'; + is slugify('a & b'), 'a-amp-b', 'right result'; + is slugify(123), '123', 'right result'; + is slugify(' Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/'), + 'jack-jill-like-numbers-123-and-4-and-silly-characters', 'right result'; + is slugify("Un \x{e9}l\x{e9}phant \x{e0} l'or\x{e9}e du bois"), 'un-elephant-a-loree-du-bois', 'right result'; + is slugify("Un \x{e9}l\x{e9}phant \x{e0} l'or\x{e9}e du bois", 1), "un-\x{e9}l\x{e9}phant-\x{e0}-lor\x{e9}e-du-bois", + 'right result'; + is slugify('Hello, World!'), 'hello-world', 'right result'; + is slugify('spam & eggs'), 'spam-eggs', 'right result'; + is slugify('spam & ıçüş', 1), 'spam-ıçüş', 'right result'; + is slugify('foo ıç bar', 1), 'foo-ıç-bar', 'right result'; + is slugify(' foo ıç bar', 1), 'foo-ıç-bar', 'right result'; + is slugify('你好', 1), '你好', 'right result'; +}; + +subtest 'gzip/gunzip' => sub { + my $uncompressed = 'a' x 1000; + my $compressed = gzip $uncompressed; + isnt $compressed, $uncompressed, 'string changed'; + ok length $compressed < length $uncompressed, 'string is shorter'; + my $result = gunzip $compressed; + is $result, $uncompressed, 'same string'; +}; + +subtest 'scope_guard' => sub { + my $test = 'a'; + { + my $guard = scope_guard sub { $test .= 'c' }; + $test .= 'b'; + } + $test .= 'd'; + is $test, 'abcd', 'right order'; +}; + +subtest 'humanize_bytes' => sub { + is humanize_bytes(0), '0B', 'zero Bytes'; + is humanize_bytes(1), '1B', 'one Byte'; + is humanize_bytes(-1023), '-1023B', 'negative Bytes'; + is humanize_bytes(1024), '1KiB', 'one KiB'; + is humanize_bytes(1025), '1KiB', 'one KiB'; + is humanize_bytes(1024 * 1024), '1MiB', 'one MiB'; + is humanize_bytes(1024 * 1024 * 1024), '1GiB', 'one GiB'; + is humanize_bytes(1024 * 1024 * 1024 * 1024), '1TiB', 'one TiB'; + is humanize_bytes(3000), '2.9KiB', 'almost 3KiB'; + is humanize_bytes(-3000), '-2.9KiB', 'almost -3KiB'; + is humanize_bytes(13443399680), '13GiB', 'two digits GiB'; + is humanize_bytes(8007188480), '7.5GiB', 'smaller GiB'; + is humanize_bytes(-8007188480), '-7.5GiB', 'negative smaller GiB'; + is humanize_bytes(-1099511627776), '-1TiB', 'negative smaller TiB'; + is humanize_bytes(717946880), '685MiB', 'large MiB'; + is humanize_bytes(-717946880), '-685MiB', 'large negative MiB'; + is humanize_bytes(245760), '240KiB', 'less than a MiB'; +}; + +subtest 'Hide DATA usage from error messages' => sub { + eval { die 'whatever' }; + unlike $@, qr/DATA/, 'DATA has been hidden'; +}; done_testing();