Browse Source

Add tests for jsone_inet module

Takeru Ohta 3 years ago
parent
commit
984eccb5bb
2 changed files with 75 additions and 15 deletions
  1. 34 15
      src/jsone_inet.erl
  2. 41 0
      test/jsone_inet_tests.erl

+ 34 - 15
src/jsone_inet.erl

@@ -51,23 +51,31 @@
 %% @doc Convert an IP address into a text representation.
 %%
 %% Please refer to the doc of `jsone:ip_address_to_json_string/1' for the detail.
--spec ip_address_to_json_string(inet:ip_address()|any()) -> {ok, jsone:jsone_string()} | error.
+-spec ip_address_to_json_string(inet:ip_address()|any()) -> {ok, jsone:json_string()} | error.
 ip_address_to_json_string({A, B, C, D}) when ?IS_IPV4(A, B, C, D) ->
     {ok, iolist_to_binary(io_lib:format("~p.~p.~p.~p", [A, B, C, D]))};
 ip_address_to_json_string({A, B, C, D, E, F, G, H}) when ?IS_IPV6(A, B, C, D, E, F, G, H) ->
     Text =
         case {A, B, C, D, E, F} of
             {0, 0, 0, 0, 0, 0} ->
-                %% IPv4-mapped address
-                io_lib:format("::~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, G band 16#ff]);
-            {0, 0, 0, 0, 0, 16#ffff} ->
                 %% IPv4-compatible address
-                io_lib:format("::ffff:~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, G band 16#ff]);
+                io_lib:format("::~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, H band 16#ff]);
+            {0, 0, 0, 0, 0, 16#ffff} ->
+                %% IPv4-mapped address
+                io_lib:format("::ffff:~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, H band 16#ff]);
             {0, 0, 0, 0, 16#ffff, 0} ->
                 %% IPv4-translated address
-                io_lib:format("::ffff:0:~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, G band 16#ff]);
+                io_lib:format("::ffff:0:~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, H band 16#ff]);
+            {16#64, 16#ff9b, 0, 0, 0, 0} ->
+                %% IPv4/IPv6 translation
+                io_lib:format("64:ff9b::~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, H band 16#ff]);
+            {16#64, 16#ff9b, 1, _, _, _} ->
+                %% IPv4/IPv6 translation
+                {Prefix, _} = format_ipv6([A, B, C, D, E, F], 0, 0),
+                Last = lists:flatten(io_lib:format("~p.~p.~p.~p", [G bsr 8, G band 16#ff, H bsr 8, H band 16#ff])),
+                string:join(Prefix ++ [Last], ":");
             _ ->
-                format_ipv6([A, B, C, D, E, F, G, H]);
+                format_ipv6([A, B, C, D, E, F, G, H])
         end,
     {ok, iolist_to_binary(Text)};
 ip_address_to_json_string(_) ->
@@ -78,11 +86,18 @@ ip_address_to_json_string(_) ->
 %%--------------------------------------------------------------------------------
 -spec format_ipv6([0..65535]) -> string().
 format_ipv6(Xs) ->
-    {Ys, _} = format_ipv6(Xs, 0, 0),
-    string:join(Ys, ":").
+    case format_ipv6(Xs, 0, 0) of
+        {Ys, shortening} -> [$: | string:join(Ys, ":")];
+        {Ys, _} ->
+            Text = string:join(Ys, ":"),
+            case lists:last(Text) of
+                $: -> [Text | ":"];
+                _  -> Text
+            end
+    end.
 
 -spec format_ipv6([0..65535], non_neg_integer(), non_neg_integer()) -> {[string()], not_shortened | shortening | shortened}.
-format_ipv6([], Zeros, MaxZeros) ->
+format_ipv6([], _Zeros, _MaxZeros) ->
     {[], not_shortened};
 format_ipv6([X | Xs], Zeros0, MaxZeros) ->
     Zeros1 =
@@ -90,15 +105,19 @@ format_ipv6([X | Xs], Zeros0, MaxZeros) ->
             0 -> Zeros0 + 1;
             _ -> 0
         end,
-    Longest = Zeros1 > MaxZeros,
+    Shorten = Zeros1 > MaxZeros andalso Zeros1 > 1,
     case format_ipv6(Xs, Zeros1, max(Zeros1, MaxZeros)) of
         {Ys, not_shortened} ->
-            case Longest of
-                true  -> {Ys, shortening};
-                false -> {[integer_to_list(X, 16) | Ys], not_shortened}
+            case Shorten of
+                true  -> {["" | Ys], shortening};
+                false -> {[to_hex(X) | Ys], not_shortened}
             end;
         {Ys, shortening} when X =:= 0 ->
             {Ys, shortening};
         {Ys, _} ->
-            {[integer_to_list(X, 16) | Ys], shortened}
+            {[to_hex(X) | Ys], shortened}
     end.
+
+-spec to_hex(0..65535) -> string().
+to_hex(N) ->
+    string:lowercase(integer_to_list(N, 16)).

+ 41 - 0
test/jsone_inet_tests.erl

@@ -0,0 +1,41 @@
+%% Copyright (c) 2013-2021, Takeru Ohta <phjgt308@gmail.com>
+-module(jsone_inet_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+
+format_ipv4_test() ->
+    Expected = <<"127.0.0.1">>,
+    {ok, Addr} = inet:parse_ipv4_address(binary_to_list(Expected)),
+    {ok, Actual} = jsone_inet:ip_address_to_json_string(Addr),
+    ?assertEqual(Actual, Expected).
+
+format_ipv6_test() ->
+    Addresses =
+        [
+         <<"::127.0.0.1">>,
+         <<"::ffff:192.0.2.1">>,
+         <<"::ffff:0:255.255.255.255">>,
+         <<"64:ff9b::0.0.0.0">>,
+         <<"64:ff9b:1::192.168.1.1">>,
+         <<"64:ff9b:1::1:192.168.1.1">>,
+         <<"::1:2:3:2001:db8">>,
+         <<"2001:db8::">>,
+         <<"2001:db8::1">>,
+         <<"2001:db8::1:0:0:1">>,
+         <<"2001:db8:0:1:1:1:1:1">>,
+         <<"2001:0:0:1::1">>,
+         <<"2001:db8:85a3::8a2e:370:7334">>
+        ],
+    lists:foreach(
+      fun (Expected) ->
+              {ok, Addr} = inet:parse_ipv6_address(binary_to_list(Expected)),
+              {ok, Bin} = jsone_inet:ip_address_to_json_string(Addr),
+              ?assertEqual(Expected, Bin)
+      end,
+      Addresses).
+
+invalid_ip_addr_test() ->
+    ?assertEqual(jsone_inet:ip_address_to_json_string(foo), error),
+    ?assertEqual(jsone_inet:ip_address_to_json_string({1, 2, 3}), error),
+    ?assertEqual(jsone_inet:ip_address_to_json_string({0, 10000, 0, 0}), error),
+    ?assertEqual(jsone_inet:ip_address_to_json_string({-1, 0, 0, 0, 0, 0, 0, 0}), error).