%%
%% MessagePack for Erlang
%%
%% Copyright (C) 2009-2013 UENISHI Kota
%%
%%    Licensed under the Apache License, Version 2.0 (the "License");
%%    you may not use this file except in compliance with the License.
%%    You may obtain a copy of the License at
%%
%%        http://www.apache.org/licenses/LICENSE-2.0
%%
%%    Unless required by applicable law or agreed to in writing, software
%%    distributed under the License is distributed on an "AS IS" BASIS,
%%    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%    See the License for the specific language governing permissions and
%%    limitations under the License.
%%

-module(msgpack_ext_tests).

-compile(export_all).

-include_lib("eunit/include/eunit.hrl").

-behaviour(msgpack_ext).

ext_test() ->
    Packer = fun({foobar, Me}, _) ->
                     {ok, {12, term_to_binary(Me)}}
             end,
    Unpacker = fun(12, Bin) ->
                       {ok, {foobar, binary_to_term(Bin)}}
               end,
    Ref = make_ref(),
    Opt = [{ext,{Packer,Unpacker}}],
    Bin = msgpack:pack({foobar, Ref}, Opt),
    {ok, {foobar, Ref}} = msgpack:unpack(Bin, Opt).

uuid_example_test() ->
    Packer =   fun({uuid, UUID}, _) when is_binary(UUID) ->
                       {ok, {42, UUID}}
               end,
    Unpacker = fun(42, Bin0) ->
                       {ok, {uuid, Bin0}}
               end,
    UUID0 = {uuid, <<221,85,73,226,102,90,82,118,40,26,166,74,52,42,61,207>>},
    Opt = [{ext,{Packer,Unpacker}}],
    Bin = msgpack:pack(UUID0, Opt),
    {ok, UUID0} = msgpack:unpack(Bin, Opt).

pack_native({native, Term}, _) when is_pid(Term) orelse
                                 is_reference(Term) orelse
                                 is_port(Term) orelse
                                 is_tuple(Term) orelse
                                 is_function(Term) ->
    {ok, {42, term_to_binary(Term)}}.

unpack_native(42, Bin) ->
    {ok, {native, binary_to_term(Bin)}}.

native_test() ->
    Opt = [{ext, {fun pack_native/2, fun unpack_native/2}}],
    Term = {native, {self(), make_ref(), foobar, fun() -> ok end}},
    {ok, Term} = msgpack:unpack(msgpack:pack(Term, Opt), Opt).

pack_ext(T, O) -> pack_native(T, O).
unpack_ext(I, B, _) -> unpack_native(I, B).

behaviour_test() ->
    Opt = [{ext, ?MODULE}],
    Term = {native, {self(), make_ref(), foobar, fun() -> ok end}},
    {ok, Term} = msgpack:unpack(msgpack:pack(Term, Opt), Opt).


ext_typecode_range_test() ->
    %% typecode range from msgpack spec. [-128,-1] is the "reserved"
    %% range, [0,127] is the "user-defined" range.
    TypecodeMin = -128,
    TypecodeMax = 127,
    Packer = fun ({thing, N}, _) ->
                     {ok, {N, msgpack:pack(N)}}
             end,
    Unpacker = fun(N, Bin, _) ->
                       Result = msgpack:unpack(Bin),
                       ?assertEqual({ok, N}, Result),
                       Result
               end,
    Opt = [{ext,{Packer,Unpacker}}],
    %% it should be possible to use an uncontroversial ext type code:
    Enc = msgpack:pack({thing,1}, Opt),
    ?assertMatch({ok, 1}, msgpack:unpack(Enc, Opt)),
    %% it should be possible to use ext typecodes covering the entire
    %% range specified in the msgpack specification:
    [begin
         Encoded = msgpack:pack({thing, N}, Opt),
         Result = msgpack:unpack(Encoded, Opt),
         ?assertMatch({ok, N}, Result)
     end || N <- lists:seq(TypecodeMin,TypecodeMax)],
    %% using codes outside the allowed range should fail:
    [?assertError({case_clause, _}, msgpack:pack({thing, N}, Opt))
     || N <- [-129, 128]],
    ok.
