erlydtl_functional_tests.erl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. %%%-------------------------------------------------------------------
  2. %%% File: erlydtl_tests.erl
  3. %%% @author Roberto Saccon <rsaccon@gmail.com> [http://rsaccon.com]
  4. %%% @author Evan Miller <emmiller@gmail.com>
  5. %%% @copyright 2008 Roberto Saccon, Evan Miller
  6. %%% @doc ErlyDTL test suite
  7. %%% @end
  8. %%%
  9. %%% The MIT License
  10. %%%
  11. %%% Copyright (c) 2007 Roberto Saccon
  12. %%%
  13. %%% Permission is hereby granted, free of charge, to any person obtaining a copy
  14. %%% of this software and associated documentation files (the "Software"), to deal
  15. %%% in the Software without restriction, including without limitation the rights
  16. %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  17. %%% copies of the Software, and to permit persons to whom the Software is
  18. %%% furnished to do so, subject to the following conditions:
  19. %%%
  20. %%% The above copyright notice and this permission notice shall be included in
  21. %%% all copies or substantial portions of the Software.
  22. %%%
  23. %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24. %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25. %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  26. %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27. %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  28. %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  29. %%% THE SOFTWARE.
  30. %%%
  31. %%% @since 2008-02-11 by Roberto Saccon
  32. %%%-------------------------------------------------------------------
  33. -module(erlydtl_functional_tests).
  34. -author('rsaccon@gmail.com').
  35. -author('emmiller@gmail.com').
  36. %% API
  37. -export([run_tests/0, run_test/1]).
  38. test_list() ->
  39. % order is important.
  40. ["autoescape", "comment", "extends", "filters", "for", "for_list",
  41. "for_tuple", "for_list_preset", "for_preset", "for_records",
  42. "for_records_preset", "include", "if", "if_preset", "ifequal",
  43. "ifequal_preset", "ifnotequal", "ifnotequal_preset", "now",
  44. "var", "var_preset", "cycle",
  45. "custom_tag", "custom_tag1", "custom_tag2", "custom_tag3",
  46. "custom_call",
  47. "include_template", "include_path", "ssi",
  48. "extends_path", "extends_path2", "trans" ].
  49. setup_compile("for_list_preset") ->
  50. CompileVars = [{fruit_list, [["apple", "apples"], ["banana", "bananas"], ["coconut", "coconuts"]]}],
  51. {ok, CompileVars};
  52. setup_compile("for_preset") ->
  53. CompileVars = [{fruit_list, ["preset-apple", "preset-banana", "preset-coconut"]}],
  54. {ok, CompileVars};
  55. setup_compile("for_records_preset") ->
  56. Link1a = [{name, "Amazon (preset)"}, {url, "http://amazon.com"}],
  57. Link2a = [{name, "Google (preset)"}, {url, "http://google.com"}],
  58. Link3a = [{name, "Microsoft (preset)"}, {url, "http://microsoft.com"}],
  59. CompileVars = [{software_links, [Link1a, Link2a, Link3a]}],
  60. {ok, CompileVars};
  61. setup_compile("if_preset") ->
  62. CompileVars = [{var1, "something"}],
  63. {ok, CompileVars};
  64. setup_compile("ifequal_preset") ->
  65. CompileVars = [{var1, "foo"}, {var2, "foo"}],
  66. {ok, CompileVars};
  67. setup_compile("ifnotequal_preset") ->
  68. CompileVars = [{var1, "foo"}, {var2, "foo"}],
  69. {ok, CompileVars};
  70. setup_compile("var_preset") ->
  71. CompileVars = [{preset_var1, "preset-var1"}, {preset_var2, "preset-var2"}],
  72. {ok, CompileVars};
  73. setup_compile(_) ->
  74. {ok, []}.
  75. %% @spec (Name::string()) -> {CompileStatus::atom(), PresetVars::list(),
  76. %% RenderStatus::atom(), RenderVars::list()} | skip
  77. %% @doc
  78. %% @end
  79. %%--------------------------------------------------------------------
  80. setup("autoescape") ->
  81. RenderVars = [{var1, "<b>bold</b>"}],
  82. {ok, RenderVars};
  83. setup("extends") ->
  84. RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
  85. {ok, RenderVars};
  86. setup("filters") ->
  87. RenderVars = [
  88. {date_var1, {1975,7,24}},
  89. {datetime_var1, {{1975,7,24}, {7,13,1}}},
  90. {'list', ["eins", "zwei", "drei"]}
  91. ],
  92. {ok, RenderVars};
  93. setup("for") ->
  94. RenderVars = [{fruit_list, ["apple", "banana", "coconut"]}],
  95. {ok, RenderVars};
  96. setup("for_list") ->
  97. RenderVars = [{fruit_list, [["apple", "apples", "$1"], ["banana", "bananas", "$2"], ["coconut", "coconuts", "$500"]]}],
  98. {ok, RenderVars};
  99. setup("for_tuple") ->
  100. RenderVars = [{fruit_list, [{"apple", "apples"}, {"banana", "bananas"}, {"coconut", "coconuts"}]}],
  101. {ok, RenderVars};
  102. setup("for_records") ->
  103. Link1 = [{name, "Amazon"}, {url, "http://amazon.com"}],
  104. Link2 = [{name, "Google"}, {url, "http://google.com"}],
  105. Link3 = [{name, "Microsoft"}, {url, "http://microsoft.com"}],
  106. RenderVars = [{link_list, [Link1, Link2, Link3]}],
  107. {ok, RenderVars};
  108. setup("for_records_preset") ->
  109. Link1b = [{name, "Canon"}, {url, "http://canon.com"}],
  110. Link2b = [{name, "Leica"}, {url, "http://leica.com"}],
  111. Link3b = [{name, "Nikon"}, {url, "http://nikon.com"}],
  112. RenderVars = [{photo_links, [Link1b, Link2b, Link3b]}],
  113. {ok, RenderVars};
  114. setup("include") ->
  115. RenderVars = [{var1, "foostring1"}, {var2, "foostring2"}],
  116. {ok, RenderVars};
  117. setup("if") ->
  118. RenderVars = [{var1, "something"}],
  119. {ok, RenderVars};
  120. setup("ifequal") ->
  121. RenderVars = [{var1, "foo"}, {var2, "foo"}, {var3, "bar"}],
  122. {ok, RenderVars};
  123. setup("ifequal_preset") ->
  124. RenderVars = [{var3, "bar"}],
  125. {ok, RenderVars};
  126. setup("ifnotequal") ->
  127. RenderVars = [{var1, "foo"}, {var2, "foo"}, {var3, "bar"}],
  128. {ok, RenderVars};
  129. setup("var") ->
  130. RenderVars = [{var1, "foostring1"}, {var2, "foostring2"}, {var_not_used, "foostring3"}],
  131. {ok, RenderVars};
  132. setup("var_preset") ->
  133. RenderVars = [{var1, "foostring1"}, {var2, "foostring2"}],
  134. {ok, RenderVars};
  135. setup("cycle") ->
  136. RenderVars = [{test, [integer_to_list(X) || X <- lists:seq(1, 20)]},
  137. {a, "Apple"}, {b, "Banana"}, {c, "Cherry"}],
  138. {ok, RenderVars};
  139. setup("include_template") ->
  140. RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
  141. {ok, RenderVars};
  142. setup("include_path") ->
  143. RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
  144. {ok, RenderVars};
  145. setup("extends_path") ->
  146. RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
  147. {ok, RenderVars};
  148. setup("extends_path2") ->
  149. RenderVars = [{base_var, "base-barstring"}, {test_var, "test-barstring"}],
  150. {ok, RenderVars};
  151. setup("trans") ->
  152. RenderVars = [{locale, "reverse"}],
  153. {ok, RenderVars};
  154. setup("locale") ->
  155. {ok, _RenderVars = [{locale, "ru"}]};
  156. setup("custom_tag1") ->
  157. {ok, [{a, <<"a1">>}], [{locale, ru}, {custom_tags_context, ctx}], [<<"b1">>, <<"\n">>]};
  158. setup("custom_tag2") ->
  159. {ok, [{a, <<"a1">>}], [{locale, ru}, {custom_tags_context, ctx}], [<<"b2">>, <<"\n">>]};
  160. setup("custom_tag3") ->
  161. {ok, [{a, <<"a1">>}], [{locale, ru}], [<<"b3">>, <<"\n">>]};
  162. setup("ssi") ->
  163. RenderVars = [{path, filename:absname(filename:join(["tests", "input", "ssi_include.html"]))}],
  164. {ok, RenderVars};
  165. %%--------------------------------------------------------------------
  166. %% Custom tags
  167. %%--------------------------------------------------------------------
  168. setup("custom_call") ->
  169. RenderVars = [{var1, "something"}],
  170. {ok, RenderVars};
  171. setup(_) ->
  172. {ok, []}.
  173. run_tests() ->
  174. io:format("Running functional tests...~n"),
  175. case filelib:ensure_dir(filename:join([templates_outdir(), "foo"])) of
  176. ok ->
  177. case fold_tests() of
  178. {N, []}->
  179. Msg = lists:concat(["All ", N, " functional tests passed~n~n"]),
  180. io:format(Msg),
  181. {ok, Msg};
  182. {_, Errs} ->
  183. io:format("Errors: ~p~n~n",[Errs]),
  184. failed
  185. end;
  186. {error, Reason} ->
  187. io:format("Error: ~p~n~n", [Reason]),
  188. failed
  189. end.
  190. run_test(Name) ->
  191. test_compile_render(filename:join([templates_docroot(), Name])).
  192. %%====================================================================
  193. %% Internal functions
  194. %%====================================================================
  195. fold_tests() ->
  196. lists:foldl(fun(Name, {AccCount, AccErrs}) ->
  197. case test_compile_render(Name) of
  198. ok ->
  199. {AccCount + 1, AccErrs};
  200. {error, Reason} ->
  201. {AccCount + 1, [{Name, Reason} | AccErrs]}
  202. end
  203. end, {0, []}, test_list()
  204. ).
  205. test_compile_render(Name) ->
  206. File = filename:join([templates_docroot(), Name]),
  207. Module = "example_" ++ Name,
  208. case setup_compile(Name) of
  209. {CompileStatus, CompileVars} ->
  210. Options = [
  211. {vars, CompileVars},
  212. {force_recompile, true},
  213. {custom_tags_modules, [erlydtl_custom_tags]}],
  214. io:format(" Template: ~p, ... compiling ... ", [Name]),
  215. case erlydtl:compile(File, Module, Options) of
  216. ok ->
  217. case CompileStatus of
  218. ok -> test_render(Name, list_to_atom(Module));
  219. _ -> {error, "compiling should have failed :" ++ File}
  220. end;
  221. {error, Err} ->
  222. case CompileStatus of
  223. error ->
  224. io:format("~n"),
  225. ok;
  226. _ ->
  227. io:format("~nCompile errror: ~p~n",[Err]),
  228. Err
  229. end
  230. end;
  231. skip ->
  232. ok;
  233. _ ->
  234. {error, "no 'setup' clause defined for this test"}
  235. end.
  236. test_render(Name, Module) ->
  237. File = filename:join([templates_docroot(), Name]),
  238. {RenderStatus, Vars, Opts, RenderResult} =
  239. case setup(Name) of
  240. {RS, V} -> {RS, V, [], undefined};
  241. {RS, V, O} -> {RS, V, O, undefined};
  242. {RS, V, O, R} -> {RS, V, O, R}
  243. end,
  244. case catch Module:render(Vars, Opts) of
  245. {ok, Data} ->
  246. io:format("rendering~n"),
  247. case RenderStatus of
  248. ok ->
  249. case RenderResult of
  250. undefined ->
  251. {File, _} = Module:source(),
  252. OutFile = filename:join([templates_outdir(), filename:basename(File)]),
  253. case file:open(OutFile, [write]) of
  254. {ok, IoDev} ->
  255. file:write(IoDev, Data),
  256. file:close(IoDev),
  257. ok;
  258. Err ->
  259. Err
  260. end;
  261. _ when Data =:= RenderResult ->
  262. ok;
  263. _ ->
  264. {error, lists:flatten(io_lib:format("Test ~s failed\n"
  265. "Expected: ~p\n"
  266. "Value: ~p\n", [Name, RenderResult, Data]))}
  267. end;
  268. _ ->
  269. {error, "rendering should have failed :" ++ File}
  270. end;
  271. {'EXIT', Reason} ->
  272. io:format("~n"),
  273. {error, lists:flatten(io_lib:format("failed invoking render method of ~p ~p", [Module, Reason]))};
  274. Err ->
  275. io:format("~n"),
  276. case RenderStatus of
  277. error -> ok;
  278. _ -> Err
  279. end
  280. end.
  281. templates_docroot() ->
  282. filename:join([erlydtl_deps:get_base_dir(), "tests", "input"]).
  283. templates_outdir() ->
  284. filename:join([erlydtl_deps:get_base_dir(), "tests", "output"]).