-module(eini_tests).

-author('shino@accense.com').

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

-import(eini, [parse/1]).

setup() ->
  ok.

teardown(_) ->
  ok.

empty_test_() ->
  {setup,
   fun setup/0,
   fun teardown/1,
   [
    ?_assertEqual({ok, []}, parse("")),
    ?_assertEqual({ok, []}, parse("\n"))
   ]}.

one_section_title_only_test_() ->
  {setup,
   fun setup/0,
   fun teardown/1,
   [
    %% Title only
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "[title]\n"
                   )),
    %% Title only, but trailing spaces
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "[title]  \n"
                   )),
    %% Title only, but comment lines
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "; comment line\n"
                    "  \n"
                    "[title]\n"
                   )),
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "; comment line\n"
                    "; comment line 2\n"
                    "  \n"
                    "[title]\n"
                   )),
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "; comment line\n"
                    "; comment line 2\n"
                    "  \n"
                    "[title]\n"
                    "; comment after section title"
                   )),
    %% Title only, but preceding blank lines
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "\n"
                    "  \n"
                    "[title]\n"
                   )),
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "  \n"
                    "\n"
                    "[title]\n"
                   )),
    %% Title only, but preceding blank lines and trailing spaces
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "\n"
                    "  \n"
                    "[title]\t\s\n"
                   )),
    %% Title only, but trailing blank lines
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "[title]\n"
                    "\n"
                    "  \n"
                    "\n"
                   )),
    %% Title only, but trailing spaces and trailing blank lines
    ?_assertEqual({ok, [
                        {title, []}
                       ]},
                  parse(
                    "[title]  \n"
                    "\n"
                    "\n"
                   ))
   ]}.

one_section_title_only_syntax_error_test_() ->
  {setup,
   fun setup/0,
   fun teardown/1,
   [
    %% No ] char
    ?_assertMatch({error, {syntax_error, 1, _Reason}},
                  parse(
                    "[title\n"
                   ))
   ]}.

one_section_title_and_one_prop_test_() ->
  {setup,
   fun setup/0,
   fun teardown/1,
   [
    %% Simple case
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]\n"
                    "key1=value1\n"
                   )),
    %% title has trailing spaces
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "key1=value1\n"
                   )),
    %% Single blank line between title and a prop
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "\n"
                    "key1=value1\n"
                   )),
    %% Single comment line between title and a prop
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "; comment\n"
                    "key1=value1\n"
                   )),
    %% Single comment line after a prop
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "key1=value1\n"
                    "; comment\n"
                   )),
    %% Multi blank lines between title and a prop
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "\n"
                    "    \n"
                    "\n"
                    "key1=value1\n"
                   )),
    %% Multi blank lines after a prop
    ?_assertEqual({ok, [
                        {title, 
                         [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "key1=value1\n"
                    "\n"
                    "    \n"
                    "\n"
                   )),
    %% Multi blank lines between title and a prop and
    %% multi blank lines after a prop
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "\n"
                    "    \n"
                    "\n"
                    "key1=value1\n"
                    "\n"
                    "    \n"
                    "\n"
                   )),

    %% value has [ char
    ?_assertEqual({ok, [
                        {title, [{key1, <<"va[lue1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "key1=va[lue1\n"
                   )),
    %% value has ] char
    ?_assertEqual({ok, [
                        {title, [{key1, <<"valu]e1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "key1=valu]e1\n"
                   )),
    %% value has [ and ] chars
    ?_assertEqual({ok, [
                        {title, [{key1, <<"va[lu]e1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "key1=va[lu]e1\n"
                   )),
    %% value has ; char
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1;continue">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "key1=value1;continue\n"
                   )),
    %% key has trailing spaces
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "key1   =value1\n"
                   )),
    %% value has preceding and trailing spaces
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1">>}]}
                       ]},
                  parse(
                    "[title]  \n"
                    "key1=  value1  \n"
                   )),
    %% value has characters which can not used in titles or keys
    ?_assertEqual({ok, [
                        {title, [{key1, <<"value1$% '""#!+*=@/:+">>}]}
                       ]},
                  parse(
                    "[title]\n"
                    "key1=value1$% '""#!+*=@/:+\n"
                   ))
   ]}.

one_section_title_and_two_props_test_() ->
  {setup,
   fun setup/0,
   fun teardown/1,
   [
    %% Simple case
    ?_assertEqual({ok, [
                        {title,
                         [{key1, <<"value1">>},
                          {key2, <<"value2">>}]}
                       ]},
                  parse(
                    "[title]\n"
                    "key1=value1\n"
                    "key2=value2\n"
                   )),
    %% Blank lines
    ?_assertEqual({ok, [
                        {title,
                         [{key1, <<"value1">>},
                          {key2, <<"value2">>}]}
                       ]},
                  parse(
                    "[title]\n"
                    "\n"
                    "key1=value1\n"
                    "  \n"
                    "\n"
                    "key2=value2\n"
                    "\n"
                    "\n"
                   ))
   ]}.

two_section_test_() ->
  {setup,
   fun setup/0,
   fun teardown/1,
   [
    ?_assertEqual({ok, [
                        {titleA, []},
                        {titleB, []}
                       ]},
                  parse(
                    "[titleA]\n"
                    "[titleB]\n"
                   )),
    ?_assertEqual({ok, [
                        {titleA,
                         [{keyA1, <<"valueA1">>}]},
                        {titleB,
                         [{keyB1, <<"valueB1">>}]}
                       ]},
                  parse(
                    "[titleA]\n"
                    "keyA1=valueA1\n"
                    "[titleB]  \n"
                    "keyB1=valueB1\n"
                   ))
   ]}.

binary_two_section_test_() ->
  {setup,
   fun setup/0,
   fun teardown/1,
   [
    ?_assertEqual({ok, [
                        {titleA, []},
                        {titleB, []}
                       ]},
                  parse(
                    "[titleA]\n"
                    "[titleB]\n"
                   )),
    ?_assertEqual({ok, [
                        {titleA,
                         [{keyA1, <<"valueA1">>}]},
                        {titleB,
                         [{keyB1, <<"valueB1">>}]}
                       ]},
                  parse(
                    <<"[titleA]\n"
                      "keyA1=valueA1\n"
                      "[titleB]  \n"
                      "keyB1=valueB1\n">>
                   ))
   ]}.

lex_error_title_test_() ->
  {setup,
   fun setup/0,
   fun teardown/ 1,
   [
    %% vertical tab in section title
    ?_assertMatch({error, {illegal_character, 1, _Reason}},
                  parse("[ti\vtle]")),
    ?_assertMatch({error, {illegal_character, 3, _Reason}},
                  parse(
                    "[titleA]\n"
                    "keyA1=valueA1\n"
                    "[tit\vleB]  \n"
                    "keyB1=valueB1\n"
                   ))
   ]}.
  
syntax_error_title_test_() ->
  {setup,
   fun setup/0,
   fun teardown/1,
   [
    %% blank char before section title
    ?_assertMatch({error, {syntax_error, 1, ["syntax error before: ", _]}},
                  parse("  [title]")),
    %% blank char in section title
    ?_assertMatch({error, {syntax_error, 3, _Reason}},
                  parse(
                    "[titleA]\n"
                    "keyA1=valueA1\n"
                    "[tit  leB]\n"
                    "keyB1=valueB1\n"
                   ))
   ]}.
  
dup_title_test_() ->
  {setup,
   fun setup/0,
   fun teardown/ 1,
   [
    ?_assertEqual({error, {duplicate_title, titleA}},
                  parse(
                    "[titleA]\n"
                    "keyA1=valueA1\n"
                    "[titleA]  \n"
                    "keyB1=valueB1\n"
                   ))
   ]}.
  
dup_key_test_() ->
  {setup,
   fun setup/0,
   fun teardown/ 1,
   [
    ?_assertEqual({error, {duplicate_key, titleB, key2}},
                  parse(
                    "[titleA]\n"
                    "key1=value1\n"
                    "[titleB]  \n"
                    "key1=value1\n"
                    "key2=value2\n"
                    "key3=value3\n"
                    "key2=value4\n"
                   )),
    ?_assertEqual({error, {duplicate_key, titleB, key2}},
                  parse(
                    "[titleA]\n"
                    "key1=value1\n"
                    "[titleB]  \n"
                    "key1=value1\n"
                    "key2=value2\n"
                    "key3=value3\n"
                    "key2  =  value4\n"
                   ))
   ]}.