1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2009-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%%-------------------------------------------------------------------
22%%% File: ct_log_SUITE
23%%%
24%%% Description: Test that ct:log, ct:pal and io:format print to
25%%% the test case log file as expected, with or without special HTML
26%%% characters being escaped.
27%%%
28%%%-------------------------------------------------------------------
29-module(ct_log_SUITE).
30
31-compile(export_all).
32
33-include_lib("common_test/include/ct.hrl").
34-include_lib("common_test/include/ct_event.hrl").
35
36-define(eh, ct_test_support_eh).
37
38%%--------------------------------------------------------------------
39%% TEST SERVER CALLBACK FUNCTIONS
40%%--------------------------------------------------------------------
41init_per_suite(Config) ->
42    Config.
43
44end_per_suite(_Config) ->
45    ok.
46
47init_per_testcase(_TestCase, Config) ->
48    Config.
49
50end_per_testcase(_TestCase, _Config) ->
51    ok.
52
53suite() -> [{ct_hooks,[ts_install_cth]}].
54
55all() ->
56    [{group,print_and_verify}].
57
58groups() ->
59    [{print_and_verify,[sequence],[print,verify]}].
60
61init_per_group(_GroupName, Config) ->
62    Config.
63
64end_per_group(_GroupName, Config) ->
65    Config.
66
67%%--------------------------------------------------------------------
68%% TEST CASES
69%%--------------------------------------------------------------------
70
71%%%-----------------------------------------------------------------
72%%%
73print(Config) ->
74    TcLogFile = proplists:get_value(tc_logfile, Config),
75    Pid = self(),
76    String = atom_to_list(?MODULE),
77
78    %% START mark
79    io:format("LOGGING START~n"),
80
81    %% io:format
82    io:format("1. Printing nothing~n", []),
83    io:format("2. Printing a string: ~s~n", [String]),
84    io:format("3. Printing a string: ~p~n", [String]),
85    io:format("4. Printing a tuple: ~w~n", [{module,?MODULE}]),
86    io:format("5. Printing a pid: ~w~n", [Pid]),
87    io:format("6. Printing HTML: <pre>~s</pre>~n", [String]),
88
89    %% ct:pal
90    ct:pal("1. Printing nothing"),
91    ct:pal("2. Printing nothing", []),
92    ct:pal("3. Printing a string: ~s", [String]),
93    ct:pal("4. Printing a string: ~p", [String]),
94    ct:pal("5. Printing a tuple: ~w", [{module,?MODULE}]),
95    ct:pal("6. Printing a pid: ~w", [Pid]),
96    ct:pal("7. Printing HTML: <pre>~s</pre>", [String]),
97    ct:pal(ct_internal, "8. Printing with category"),
98    ct:pal(ct_internal, "9. Printing with ~s", ["category"]),
99    ct:pal(50, "10. Printing with importance"),
100    ct:pal(50, "11. Printing with ~s", ["importance"]),
101    ct:pal(ct_internal, 50, "12. Printing with ~s", ["category and importance"]),
102
103    ct:pal("13. Printing with heading", [],
104           [{heading,"This is a heading"}]),
105    ct:pal(ct_internal, "14. Printing with category and heading", [],
106           [{heading,"This is a heading"}]),
107    ct:pal(50, "15. Printing with importance and heading", [],
108           [{heading,"This is a heading"}]),
109    ct:pal(ct_internal, 50, "16. Printing with category, importance and heading", [],
110           [{heading,"This is a heading"}]),
111
112    %% ct:log
113    ct:log("1. Printing nothing"),
114    ct:log("2. Printing nothing", []),
115    ct:log("3. Printing a string: ~s", [String]),
116    ct:log("4. Printing a string: ~p", [String]),
117    ct:log("5. Printing a tuple: ~w", [{module,?MODULE}]),
118    ct:log("6. Printing a pid: ~w", [Pid]),
119    ct:log("7. Printing HTML: <pre>~s</pre>", [String]),
120    ct:log("8. Printing a pid escaped: ~w", [Pid], [esc_chars]),
121    ct:log("9. Printing a string escaped: ~p", [String], [esc_chars]),
122    ct:log("10. Printing HTML escaped: <pre>~s</pre>", [String], [esc_chars]),
123    ct:log("11. Printing a string, no css: ~s", [String], [no_css]),
124    ct:log("12. Printing a pid escaped, no css: ~w", [Pid],
125	   [esc_chars, no_css]),
126    ct:log(ct_internal, "13. Printing with category"),
127    ct:log(ct_internal, "14. Printing with ~s", ["category"]),
128    ct:log(ct_internal, "15. Printing with ~s, no_css", ["category"],
129	   [no_css]),
130    ct:log(50, "16. Printing with importance"),
131    ct:log(50, "17. Printing with ~s", ["importance"]),
132    ct:log(50, "18. Printing with ~s, no_css", ["importance"], [no_css]),
133    ct:log(ct_internal, 50, "19. Printing with category and importance"),
134    ct:log(ct_internal, 50, "20. Printing with ~s", ["category and importance"]),
135    ct:log(ct_internal, 50, "21. Printing a pid escaped with ~s, no_css: ~w",
136	   ["category and importance",Pid], [esc_chars,no_css]),
137
138    ct:log("22. Printing with heading", [],
139           [{heading,"This is a heading"}]),
140    ct:log(ct_internal, "23. Printing with category and heading", [],
141           [{heading,"This is a heading"}]),
142    ct:log(50, "24. Printing with importance and heading", [],
143           [{heading,"This is a heading"}]),
144    ct:log(ct_internal, 50, "25. Printing with category, importance and heading", [],
145           [{heading,"This is a heading"}]),
146
147    %% END mark
148    ct:log("LOGGING END", [], [no_css]),
149
150
151    %% ct:print
152    ct:print("1. Does this show??"),
153    ct:print("2. Does this ~s", ["show??"]),
154    ct:print("3. Is this a non-html pid?? ~w", [self()]),
155    ct:print(ct_internal, "4. Printing with category"),
156    ct:print(ct_internal, "5. Printing with ~s", ["category"]),
157    ct:print(50, "6. Printing with importance"),
158    ct:print(50, "7. Printing with ~s", ["importance"]),
159    ct:print(ct_internal, 50, "8. Printing with ~s", ["category and importance"]),
160    ct:print("9. Printing with heading", [],
161           [{heading,"This is a heading"}]),
162    ct:print(ct_internal, "10. Printing with category and heading", [],
163           [{heading,"This is a heading"}]),
164    ct:print(50, "11. Printing with importance and heading", [],
165           [{heading,"This is a heading"}]),
166    ct:print(ct_internal, 50, "12. Printing with category, importance and heading", [],
167           [{heading,"This is a heading"}]),
168
169    {save_config,[{the_logfile,TcLogFile},{the_pid,Pid},{the_string,String}]}.
170
171
172verify(Config) ->
173    {print,SavedCfg} = proplists:get_value(saved_config, Config),
174    TcLogFile = proplists:get_value(the_logfile, SavedCfg),
175    Pid = proplists:get_value(the_pid, SavedCfg),
176    StrPid = lists:flatten(io_lib:format("~p",[Pid])),
177    EscPid = "&lt;" ++ string:slice(StrPid, 1, length(StrPid)-2) ++ "&gt;",
178    String = proplists:get_value(the_string, SavedCfg),
179    ct:log("Read from prev testcase: ~p & ~p", [TcLogFile,Pid]),
180    {ok,Dev} = file:open(TcLogFile, [read]),
181    ok = read_until(Dev, "LOGGING START\n"),
182
183    ct:pal("VERIFYING LOG ENTRIES...", []),
184
185    %% io:format
186    match_line(Dev, "1. Printing nothing", []),
187    read_nl(Dev),
188    match_line(Dev, "2. Printing a string: ~s", [String]),
189    read_nl(Dev),
190    match_line(Dev, "3. Printing a string: ~p", [String]),
191    read_nl(Dev),
192    match_line(Dev, "4. Printing a tuple: ~w", [{module,?MODULE}]),
193    read_nl(Dev),
194    match_line(Dev, "5. Printing a pid: ~s", [EscPid]),
195    read_nl(Dev),
196    match_line(Dev, "6. Printing HTML: &lt;pre&gt;~s&lt;/pre&gt;", [String]),
197    read_nl(Dev),
198
199    %% ct:pal
200    read_header(Dev),
201    match_line(Dev, "1. Printing nothing", []),
202    read_footer(Dev),
203    read_header(Dev),
204    match_line(Dev, "2. Printing nothing", []),
205    read_footer(Dev),
206    read_header(Dev),
207    match_line(Dev, "3. Printing a string: ~s", [String]),
208    read_footer(Dev),
209    read_header(Dev),
210    match_line(Dev, "4. Printing a string: ~p", [String]),
211    read_footer(Dev),
212    read_header(Dev),
213    match_line(Dev, "5. Printing a tuple: ~w", [{module,?MODULE}]),
214    read_footer(Dev),
215    read_header(Dev),
216    match_line(Dev, "6. Printing a pid: ~s", [EscPid]),
217    read_footer(Dev),
218    read_header(Dev),
219    match_line(Dev, "7. Printing HTML: &lt;pre&gt;~s&lt;/pre&gt;", [String]),
220    read_footer(Dev),
221    read_header(Dev, "\"ct_internal\""),
222    match_line(Dev, "8. Printing with category", []),
223    read_footer(Dev),
224    read_header(Dev, "\"ct_internal\""),
225    match_line(Dev, "9. Printing with ~s", ["category"]),
226    read_footer(Dev),
227    read_header(Dev),
228    match_line(Dev, "10. Printing with importance", []),
229    read_footer(Dev),
230    read_header(Dev),
231    match_line(Dev, "11. Printing with ~s", ["importance"]),
232    read_footer(Dev),
233    read_header(Dev, "\"ct_internal\""),
234    match_line(Dev, "12. Printing with ~s", ["category and importance"]),
235    read_footer(Dev),
236    read_header(Dev, "\"default\"", "This is a heading"),
237    match_line(Dev, "13. Printing with heading", []),
238    read_footer(Dev),
239    read_header(Dev, "\"ct_internal\"", "This is a heading"),
240    match_line(Dev, "14. Printing with category and heading", []),
241    read_footer(Dev),
242    read_header(Dev, "\"default\"", "This is a heading"),
243    match_line(Dev, "15. Printing with importance and heading", []),
244    read_footer(Dev),
245    read_header(Dev, "\"ct_internal\"", "This is a heading"),
246    match_line(Dev, "16. Printing with category, importance and heading", []),
247    read_footer(Dev),
248
249    %% ct:log
250    read_header(Dev),
251    match_line(Dev, "1. Printing nothing", []),
252    read_footer(Dev),
253    read_header(Dev),
254    match_line(Dev, "2. Printing nothing", []),
255    read_footer(Dev),
256    read_header(Dev),
257    match_line(Dev, "3. Printing a string: ~s", [String]),
258    read_footer(Dev),
259    read_header(Dev),
260    match_line(Dev, "4. Printing a string: ~p", [String]),
261    read_footer(Dev),
262    read_header(Dev),
263    match_line(Dev, "5. Printing a tuple: ~w", [{module,?MODULE}]),
264    read_footer(Dev),
265    read_header(Dev),
266    match_line(Dev, "6. Printing a pid: ~w", [Pid]),
267    read_footer(Dev),
268    read_header(Dev),
269    match_line(Dev, "7. Printing HTML: <pre>~s</pre>", [String]),
270    read_footer(Dev),
271    read_header(Dev),
272    match_line(Dev, "8. Printing a pid escaped: ~s", [EscPid]),
273    read_footer(Dev),
274    read_header(Dev),
275    match_line(Dev, "9. Printing a string escaped: ~p", [String]),
276    read_footer(Dev),
277    read_header(Dev),
278    match_line(Dev, "10. Printing HTML escaped: &lt;pre&gt;~s&lt;/pre&gt;",
279	       [String]),
280    read_footer(Dev),
281    match_line(Dev, "11. Printing a string, no css: ~s", [String]),
282    match_line(Dev, "12. Printing a pid escaped, no css: ~s", [EscPid]),
283    read_header(Dev, "\"ct_internal\""),
284    match_line(Dev, "13. Printing with category", []),
285    read_footer(Dev),
286    read_header(Dev, "\"ct_internal\""),
287    match_line(Dev, "14. Printing with ~s", ["category"]),
288    read_footer(Dev),
289    match_line(Dev, "15. Printing with ~s, no_css", ["category"]),
290    read_header(Dev),
291    match_line(Dev, "16. Printing with importance", []),
292    read_footer(Dev),
293    read_header(Dev),
294    match_line(Dev, "17. Printing with ~s", ["importance"]),
295    read_footer(Dev),
296    match_line(Dev, "18. Printing with ~s, no_css", ["importance"]),
297    read_header(Dev, "\"ct_internal\""),
298    match_line(Dev, "19. Printing with category and importance", []),
299    read_footer(Dev),
300    read_header(Dev, "\"ct_internal\""),
301    match_line(Dev, "20. Printing with ~s", ["category and importance"]),
302    read_footer(Dev),
303    match_line(Dev, "21. Printing a pid escaped with ~s, no_css: ~s",
304	       ["category and importance",EscPid]),
305    read_header(Dev, "\"default\"", "This is a heading"),
306    match_line(Dev, "22. Printing with heading", []),
307    read_footer(Dev),
308    read_header(Dev, "\"ct_internal\"", "This is a heading"),
309    match_line(Dev, "23. Printing with category and heading", []),
310    read_footer(Dev),
311    read_header(Dev, "\"default\"", "This is a heading"),
312    match_line(Dev, "24. Printing with importance and heading", []),
313    read_footer(Dev),
314    read_header(Dev, "\"ct_internal\"", "This is a heading"),
315    match_line(Dev, "25. Printing with category, importance and heading", []),
316    read_footer(Dev),
317    file:close(Dev),
318    ok.
319
320%%%-----------------------------------------------------------------
321%%% HELP FUNCTIONS
322%%%-----------------------------------------------------------------
323
324read_until(Dev, Pat) ->
325    case file:read_line(Dev) of
326	{ok,Pat} ->
327	    file:read_line(Dev),		% \n
328	    ok;
329	eof ->
330	    file:close(Dev),
331	    {error,{not_found,Pat}};
332	_ ->
333	    read_until(Dev, Pat)
334    end.
335
336match_line(Dev, Format, Args) ->
337    Pat = lists:flatten(io_lib:format(Format, Args)),
338    Line = element(2, file:read_line(Dev)),
339
340    %% for debugging purposes:
341    ct:pal("L: ~tp", [Line], [no_css]),
342
343    case re:run(Line, Pat) of
344	{match,_} ->
345	    ok;
346	nomatch ->
347	    ct:pal("ERROR! No match for ~p", [Pat]),
348	    file:close(Dev),
349	    ct:fail({mismatch,Pat,Line})
350    end.
351
352read_header(Dev) ->
353    read_header(Dev, "\"default\"", "User").
354
355read_header(Dev, Cat) ->
356    read_header(Dev, Cat, "User").
357
358read_header(Dev, Cat, Heading) ->
359    file:read_line(Dev),			% \n
360    "</pre>\n" = element(2, file:read_line(Dev)),
361    {ok,Hd} = file:read_line(Dev),
362
363    %% for debugging purposes:
364    ct:pal("H: ~tp", [Hd], [no_css]),
365
366    Pat = "<div class="++Cat++"><pre><b>"++
367          "\\*\\*\\* "++Heading++" \\d{4}-\\d{2}-\\d{2} "++
368          "\\d{2}:\\d{2}:\\d{2}.\\d{1,} \\*\\*\\*</b>",
369
370    case re:run(Hd, Pat) of
371        {match,_} ->
372            ok;
373        _ ->
374            ct:pal("ERROR! No match for ~p", [Pat]),
375	    file:close(Dev),
376	    ct:fail({mismatch,Pat,Hd})
377    end.
378
379read_footer(Dev) ->
380    "</pre></div>\n" = element(2, file:read_line(Dev)),
381    "<pre>\n" = element(2, file:read_line(Dev)),
382    %% for debugging purposes:
383    ct:pal("F: </pre></div><pre>", [], [no_css]).
384
385read_nl(Dev) ->
386    file:read_line(Dev).
387
388
389