123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- %%%-------------------------------------------------------------------
- %%% File: erlydtl_tests.erl
- %%% @author Roberto Saccon <rsaccon@gmail.com> [http://rsaccon.com]
- %%% @author Evan Miller <emmiller@gmail.com>
- %%% @copyright 2008 Roberto Saccon, Evan Miller
- %%% @doc ErlyDTL test suite
- %%% @end
- %%%
- %%% The MIT License
- %%%
- %%% Copyright (c) 2007 Roberto Saccon
- %%%
- %%% Permission is hereby granted, free of charge, to any person obtaining a copy
- %%% of this software and associated documentation files (the "Software"), to deal
- %%% in the Software without restriction, including without limitation the rights
- %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- %%% copies of the Software, and to permit persons to whom the Software is
- %%% furnished to do so, subject to the following conditions:
- %%%
- %%% The above copyright notice and this permission notice shall be included in
- %%% all copies or substantial portions of the Software.
- %%%
- %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- %%% THE SOFTWARE.
- %%%
- %%% @since 2008-02-11 by Roberto Saccon
- %%%-------------------------------------------------------------------
- -module(erlydtl_functional_tests).
- -author('rsaccon@gmail.com').
- -author('emmiller@gmail.com').
- %% API
- -export([run_tests/0, run_test/1]).
- test_list() ->
- %% order is important.
- ["autoescape", "comment", "extends", "filters", "for", "for_list",
- "for_tuple", "for_list_preset", "for_preset", "for_records",
- "for_records_preset", "include", "if", "if_preset", "ifequal",
- "ifequal_preset", "ifnotequal", "ifnotequal_preset", "now",
- "var", "var_preset", "cycle", "custom_tag", "custom_tag1",
- "custom_tag2", "custom_tag3", "custom_tag4", "custom_call",
- "include_template", "include_path", "ssi", "extends_path",
- "extends_path2", "trans", "extends2", "extends3",
- "recursive_block", "extend_recursive_block"
- ].
- setup_compile("for_list_preset") ->
- CompileVars = [{fruit_list, [["apple", "apples"], ["banana", "bananas"], ["coconut", "coconuts"]]}],
- {ok, CompileVars};
- setup_compile("for_preset") ->
- CompileVars = [{fruit_list, ["preset-apple", "preset-banana", "preset-coconut"]}],
- {ok, CompileVars};
- setup_compile("for_records_preset") ->
- Link1a = [{name, "Amazon (preset)"}, {url, "http://amazon.com"}],
- Link2a = [{name, "Google (preset)"}, {url, "http://google.com"}],
- Link3a = [{name, "Microsoft (preset)"}, {url, "http://microsoft.com"}],
- CompileVars = [{software_links, [Link1a, Link2a, Link3a]}],
- {ok, CompileVars};
- setup_compile("if_preset") ->
- CompileVars = [{var1, "something"}],
- {ok, CompileVars};
- setup_compile("ifequal_preset") ->
- CompileVars = [{var1, "foo"}, {var2, "foo"}],
- {ok, CompileVars};
- setup_compile("ifnotequal_preset") ->
- CompileVars = [{var1, "foo"}, {var2, "foo"}],
- {ok, CompileVars};
- setup_compile("var_preset") ->
- CompileVars = [{preset_var1, "preset-var1"}, {preset_var2, "preset-var2"}],
- {ok, CompileVars};
- setup_compile("extends2") ->
- File = templates_dir("input/extends2"),
- Error = {none, erlydtl_compiler, unexpected_extends_tag},
- {{error, [{File, [Error]}], []}, []};
- setup_compile("extends3") ->
- File = templates_dir("input/extends3"),
- Include = templates_dir("input/imaginary"),
- Error = {none, erlydtl_compiler, {read_file, Include, enoent}},
- {{error, [{File, [Error]}], []}, []};
- setup_compile(_) ->
- {ok, []}.
- %% @spec (Name::string()) -> {CompileStatus::atom(), PresetVars::list(),
- %% RenderStatus::atom(), RenderVars::list()} | skip
- %% @doc
- %% @end
- %%--------------------------------------------------------------------
- setup("autoescape") ->
- RenderVars = [{var1, "<b>bold</b>"}],
- {ok, RenderVars};
- setup("extends") ->
- RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
- {ok, RenderVars};
- setup("filters") ->
- RenderVars = [
- {date_var1, {1975,7,24}},
- {datetime_var1, {{1975,7,24}, {7,13,1}}},
- {'list', ["eins", "zwei", "drei"]}
- ],
- {ok, RenderVars};
- setup("for") ->
- RenderVars = [{fruit_list, ["apple", "banana", "coconut"]}],
- {ok, RenderVars};
- setup("for_list") ->
- RenderVars = [{fruit_list, [["apple", "apples", "$1"], ["banana", "bananas", "$2"], ["coconut", "coconuts", "$500"]]}],
- {ok, RenderVars};
- setup("for_tuple") ->
- RenderVars = [{fruit_list, [{"apple", "apples"}, {"banana", "bananas"}, {"coconut", "coconuts"}]}],
- {ok, RenderVars};
- setup("for_records") ->
- Link1 = [{name, "Amazon"}, {url, "http://amazon.com"}],
- Link2 = [{name, "Google"}, {url, "http://google.com"}],
- Link3 = [{name, "Microsoft"}, {url, "http://microsoft.com"}],
- RenderVars = [{link_list, [Link1, Link2, Link3]}],
- {ok, RenderVars};
- setup("for_records_preset") ->
- Link1b = [{name, "Canon"}, {url, "http://canon.com"}],
- Link2b = [{name, "Leica"}, {url, "http://leica.com"}],
- Link3b = [{name, "Nikon"}, {url, "http://nikon.com"}],
- RenderVars = [{photo_links, [Link1b, Link2b, Link3b]}],
- {ok, RenderVars};
- setup("include") ->
- RenderVars = [{var1, "foostring1"}, {var2, "foostring2"}],
- {ok, RenderVars};
- setup("if") ->
- RenderVars = [{var1, "something"}],
- {ok, RenderVars};
- setup("ifequal") ->
- RenderVars = [{var1, "foo"}, {var2, "foo"}, {var3, "bar"}],
- {ok, RenderVars};
- setup("ifequal_preset") ->
- RenderVars = [{var3, "bar"}],
- {ok, RenderVars};
- setup("ifnotequal") ->
- RenderVars = [{var1, "foo"}, {var2, "foo"}, {var3, "bar"}],
- {ok, RenderVars};
- setup("now") ->
- {ok, [], [], skip_check};
- setup("var") ->
- RenderVars = [{var1, "foostring1"}, {var2, "foostring2"}, {var_not_used, "foostring3"}],
- {ok, RenderVars};
- setup("var_preset") ->
- RenderVars = [{var1, "foostring1"}, {var2, "foostring2"}],
- {ok, RenderVars};
- setup("cycle") ->
- RenderVars = [{test, [integer_to_list(X) || X <- lists:seq(1, 20)]},
- {a, "Apple"}, {b, "Banana"}, {c, "Cherry"}],
- {ok, RenderVars};
- setup("include_template") ->
- RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
- {ok, RenderVars};
- setup("include_path") ->
- RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
- {ok, RenderVars};
- setup("extends_path") ->
- RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
- {ok, RenderVars};
- setup("extends_path2") ->
- RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
- {ok, RenderVars};
- setup("trans") ->
- RenderVars = [{locale, "reverse"}],
- {ok, RenderVars};
- setup("locale") ->
- {ok, _RenderVars = [{locale, "ru"}]};
- setup("custom_tag1") ->
- {ok, [{a, <<"a1">>}], [{locale, ru}], <<"b1\n">>};
- setup("custom_tag2") ->
- {ok, [{a, <<"a1">>}], [{locale, ru}, {foo, bar}], <<"b2\n">>};
- setup("custom_tag3") ->
- {ok, [{a, <<"a1">>}], [{locale, ru}], <<"b3\n">>};
- setup("custom_tag4") ->
- {ok, [], [], <<"a\n">>};
- setup("ssi") ->
- RenderVars = [{path, filename:absname(filename:join(["tests", "input", "ssi_include.html"]))}],
- {ok, RenderVars};
- %%--------------------------------------------------------------------
- %% Custom tags
- %%--------------------------------------------------------------------
- setup("custom_call") ->
- RenderVars = [{var1, "something"}],
- {ok, RenderVars};
- setup(_) ->
- {ok, []}.
- run_tests() ->
- io:format("Running functional tests...~n"),
- file:set_cwd(erlydtl_deps:get_base_dir()),
- case [filelib:ensure_dir(
- filename:join([templates_dir(Dir), "foo"]))
- || Dir <- ["output", "expect"]] -- [ok,ok]
- of
- [] ->
- case fold_tests() of
- {N, []}->
- Msg = lists:concat(["All ", N, " functional tests passed~n~n"]),
- io:format(Msg),
- {ok, Msg};
- {N, Errs} ->
- io:format(
- "~b / ~b functional tests failed.~nErrors: ~n",
- [length(Errs), N]),
- [io:format(" ~s [~s] ~s~n", [Name, Error, Reason])
- || {Name, Error, Reason} <- Errs],
- throw(failed)
- end;
- Err ->
- [io:format("Ensure dir failed: ~p~n~n", [Reason]) || {error, Reason} <- Err],
- throw(failed)
- end.
- run_test(Name) ->
- test_compile_render(Name).
- %%====================================================================
- %% Internal functions
- %%====================================================================
- fold_tests() ->
- lists:foldl(fun(Name, {AccCount, AccErrs}) ->
- Res = case catch test_compile_render(Name) of
- ok -> {AccCount + 1, AccErrs};
- {'EXIT', Reason} ->
- {AccCount + 1, [{Name, crash,
- io_lib:format("~p", [Reason])}
- | AccErrs]};
- {Error, Reason} ->
- {AccCount + 1, [{Name, Error, Reason}
- | AccErrs]}
- end,
- io:format("~n"), Res
- end, {0, []}, test_list()).
- test_compile_render(Name) ->
- File = filename:join([templates_docroot(), Name]),
- Module = "functional_test_" ++ Name,
- io:format(" Template: ~p, ... ", [Name]),
- case setup_compile(Name) of
- {CompileStatus, CompileVars} ->
- Options = [
- {vars, CompileVars},
- force_recompile,
- return_errors,
- return_warnings,
- %% debug_compiler,
- {custom_tags_modules, [erlydtl_custom_tags]}],
- io:format("compiling ... "),
- case erlydtl:compile(File, Module, Options) of
- {ok, Mod, [{File, [{none,erlydtl_compiler,no_out_dir}]}]} ->
- if CompileStatus =:= ok -> test_render(Name, Mod);
- true ->
- io:format("missing error"),
- {error, "compiling should have failed :" ++ File}
- end;
- {error, _, _}=CompileStatus ->
- io:format("ok");
- Err ->
- io:format("failed"),
- {compile_error, io_lib:format("Actual: ~p, Expected: ~p", [Err, CompileStatus])}
- end;
- skip -> io:format("skipped")
- end.
- test_render(Name, Module) ->
- File = filename:join([templates_docroot(), Name]),
- {RenderStatus, Vars, Opts, RenderResult} =
- case setup(Name) of
- {RS, V} -> {RS, V, [], get_expected_result(Name)};
- {RS, V, O} -> {RS, V, O, get_expected_result(Name)};
- {RS, V, O, R} -> {RS, V, O, R}
- end,
- io:format("rendering ... "),
- case catch Module:render(Vars, Opts) of
- {ok, Output} ->
- Data = iolist_to_binary(Output),
- if RenderStatus =:= ok ->
- if RenderResult =:= undefined ->
- [with_template_filename(
- Dir, Name,
- fun(F) -> file:write_file(F, Data) end)
- || Dir <- ["output", "expect"]],
- io:format("~n #### NOTE: created new expected output file: \"tests/expect/~s\"."
- "~n Please verify contents.", [Name]);
- RenderResult =:= Data ->
- io:format("ok");
- RenderResult =:= skip_check ->
- io:format("ok (not checked for regression)");
- true ->
- io:format("failed"),
- with_template_filename(
- "output", Name,
- fun(F) -> file:write_file(F, Data) end),
- {error, io_lib:format(
- "Expected output does not match rendered output~n"
- "==Expected==~n~p~n--Actual--~n~p~n==End==~n",
- [RenderResult, Data])}
- end;
- true ->
- io:format("missing error"),
- {missing_error, "rendering should have failed :" ++ File}
- end;
- {'EXIT', Reason} ->
- io:format("failed"),
- {render_error, io_lib:format("failed invoking render method of ~p ~p", [Module, Reason])};
- Err ->
- if RenderStatus =:= error -> io:format("ok");
- true -> io:format("failed"),
- {render_error, io_lib:format("~p", [Err])}
- end
- end.
- get_expected_result(Name) ->
- with_template_filename(
- "expect", Name,
- fun(F) ->
- case filelib:is_regular(F) of
- true -> {ok, Data} = file:read_file(F), Data;
- false -> undefined
- end
- end).
- with_template_filename(Dir, Name, Fun) ->
- FileName = filename:join([templates_dir(Dir), Name]),
- Fun(FileName).
- templates_docroot() -> templates_dir("input").
- templates_dir(Name) -> filename:join(["tests", Name]).
|