1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2006-2019. 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%%% Purpose : Sub routines for test suite for the xmerl application,
22%%% xmerl_xsd module.
23%%%-------------------------------------------------------------------
24%%% @private
25%%% File    : xmerl_xsd_lib.erl
26%%% Author  : Bertil Karlsson <bertil@finrod>
27%%% Description :
28%%%
29%%% Created : 28 Apr 2006 by Bertil Karlsson <bertil@finrod>
30%%%-------------------------------------------------------------------
31-module(xmerl_xsd_lib).
32
33-compile(export_all).
34
35-include_lib("common_test/include/ct.hrl").
36-include("xmerl.hrl").
37-include("xmerl_xsd.hrl").
38-include_lib("kernel/include/file.hrl").
39
40
41compare_test_results(Config, ST, IT) ->
42    ResST=compare_schema_test_results(ST),
43    ResIT=compare_instance_test_results(IT),
44    io:format("compare_test_results:~n  ST = ~p~n  IT = ~p~n  ResST = ~p~n  ResIT = ~p~n",[ST, IT, ResST, ResIT]),
45    case process_reference_results(Config, ResST, ResIT) of
46	error -> error;
47	Diff -> return_results(Diff, ResST, ResIT, length(ST)+length(IT))
48    end.
49
50compare_schema_test_results(ST) ->
51    {[N||{N, false}<-ST], [N||{N, enoent}<-ST]}.
52compare_instance_test_results(IT) ->
53    {[N||{N, false}<-IT], [N||{N, enoent}<-IT]}.
54
55return_results({SkippedN, Diff},{STErrs, _},{ITErrs, _}, TotN) ->
56    NumErrs = length(STErrs ++ ITErrs),
57    case NumErrs == TotN of
58	true when TotN > 0 ->
59	    exit(all_tests_cases_failed);
60	_ ->
61	    return_results2(Diff, TotN - NumErrs, SkippedN, TotN)
62    end.
63
64%% return_results2(Diff,{[],[]},{[],[]},TotN) ->
65%%     {comment,io_lib:format("~p successful test cases.~n"++Diff,[TotN])};
66%% return_results2(Diff,{STErrs,[]},{ITErrs,[]},TotN) ->
67%%     {comment,io_lib:format("Total number of test cases: ~p~nThe following ~p test cases failed: ~p~n"++Diff,[TotN,length(STErrs++ITErrs),STErrs++ITErrs])};
68%% return_results2(Diff,{STErrs,STOther},{ITErrs,ITOther},TotN) ->
69%%     {comment,io_lib:format("Total number of test cases: ~p~nThe following ~p test cases failed: ~p~nThe following ~p test cases was malicious ~p~~n"++Diff,[TotN,length(STErrs++ITErrs),STErrs++ITErrs,length(STOther++ITOther),STOther++ITOther])}.
70
71return_results2(_, 0, 0, 0) ->
72    {comment,io_lib:format("This test case was empty.~n", [])};
73return_results2({[], [], [], []}, NumSucc, SkippedN, TotN) ->
74    {comment,io_lib:format("~p successful tests, ~p skipped tests of totally ~p test cases.~n",
75			   [NumSucc, SkippedN, TotN])};
76return_results2({NewFail, NewSuccess, NewMal, NewNotMal}, NumSucc, SkippedN, TotN) ->
77    NFComm = case NewFail of
78                 [] -> "";
79                 _ -> io_lib:format("These ~p tests are new failures: ~p~n",
80                                    [length(NewFail), NewFail])
81             end,
82    NSComm = case NewSuccess of
83                 [] -> "";
84                 _ -> io_lib:format("These ~p skipped tests are new succeeding cases: ~p~n",
85                                    [length(NewSuccess), NewSuccess])
86             end,
87    NMComm = case NewMal of
88                 [] -> "";
89                 _ -> io_lib:format("These ~p tests are now malicious: ~p~n",
90                                    [length(NewMal), NewMal])
91             end,
92    NNMComm = case NewNotMal of
93                  [] -> "";
94                  _ -> io_lib:format("These ~p  skipped tests were malicious, but succeeds now: ~p~n",
95                                     [length(NewNotMal), NewNotMal])
96              end,
97    ct:comment(io_lib:format("~p successful tests, ~p skipped tests of totally ~p test cases. ~n~ts",
98                             [NumSucc, SkippedN, TotN,
99                              NFComm ++ NSComm ++ NMComm ++ NNMComm])),
100    [] = NewFail.
101
102%% return_results2(Diff,{STErrs,STOther},{ITErrs,ITOther},TotN) ->
103%%     {comment,io_lib:format("Total number of test cases: ~p~n The following ~p test cases failed: ~p~nThe following ~p test cases was malicious ~p~~n",[TotN,length(STErrs++ITErrs),STErrs++ITErrs,length(STOther++ITOther),STOther++ITOther])}.
104
105
106process_reference_results(Config, {ErrsST, MalST}, {ErrsIT, MalIT}) ->
107    {RefFailed, RefMalicious} = xsd_reference_log(Config),
108    io:format("A: ~p : ~p\n\n",[RefFailed, RefMalicious]),
109    AllErrs = ErrsST ++ ErrsIT,
110    AllMals = MalST ++ MalIT,
111    %% test cases failed now but succeeded in reference results.
112    NewFailures = [X||X<-AllErrs, lists:member(X, RefFailed) == false],
113    %% test cases succeeded now but failed in reference results.
114    NewSucceeds = [X||X<-RefFailed, lists:member(X, AllErrs) == false],
115    %% test cases malicious now but succeeded in reference results.
116    NewMalicious = [X||X<-AllMals, lists:member(X, RefMalicious) == false],
117    %% test cases succeeded now but malicious in reference results.
118    NewNotMal = [X||X<-RefMalicious, lists:member(X, AllMals) == false],
119    write_in_log(Config, AllErrs, AllMals),
120    % io:format("process_reference_results:~n  AllErrs = ~p~n  NewFailures = ~p~n",[AllErrs,NewFailures]),
121    {length(RefFailed) + length(RefMalicious), {NewFailures, NewSucceeds, NewMalicious, NewNotMal}}.
122
123xsd_reference_log(Config) ->
124    DataDir = datadir(Config),
125    Suite = proplists:get_value(suite, Config),
126    SuiteReferenceLog =
127    filename:join([DataDir,lists:concat([Suite,"_failed_cases.log"])]),
128    io:format("B: ~p\n\n",[SuiteReferenceLog]),
129    case file:consult(SuiteReferenceLog) of
130        {ok,List} when is_list(List) ->
131            io:format("C: ~p\n\n",[List]),
132            case lists:keysearch(proplists:get_value(testcase, Config), 1, List) of
133                {value,{_, TCRefFails}} ->
134                    io:format("D: ~p\n\n",[TCRefFails]),
135                    TCRefFails;
136                _ ->
137                    io:format("D: ~no result\n\n",[]),
138                    {[], []}
139            end;
140        _ ->
141            {[], []}
142    end.
143
144write_in_log(_Config, [], []) ->
145    ok;
146write_in_log(Config, AllErrs, AllMals) ->
147    LogFileName = proplists:get_value(xmerl_error_log, Config),
148    {ok,IO}=file:open(LogFileName, [append]),
149    TestCase = proplists:get_value(testcase, Config),
150    io:format(IO,"{~p,{~p,~p}}.~n", [TestCase, AllErrs, AllMals]),
151    file:close(IO),
152    ok.
153
154schema_test(Config,FileName,XsdBase,Validity) ->
155    ModuleName = filename:basename(FileName),
156    DataDir = datadir(Config),
157    case xmerl_xsd:process_schema(filename:join([DataDir, FileName]),
158                                  [{xsdbase,filename:join([DataDir, XsdBase])}]) of
159	{error, enoent} ->
160	    {{ModuleName, enoent},#xsd_state{}};
161	{Ok, S} ->
162	    case Validity of
163		valid when Ok == ok ->
164%%		    io:format("schema_test1: Validity=valid,Ok=ok,S=~p~n",[S]),
165		    {{ModuleName, S#xsd_state.errors == []}, S};
166		invalid when Ok == error -> %% S is in this case an error reason
167		    {{ModuleName, no_internal_error(S)}, #xsd_state{}};
168		notKnown ->
169		    {{ModuleName, true}, #xsd_state{}};
170		valid ->
171		    io:format("schema_test2: Validity=valid,Ok=~p,S=~p~n", [Ok, S]),
172%%		    io:format("FileName: ~p~n",[FileName]),
173		    {{ModuleName, false}, #xsd_state{}};
174		_ -> %% invalid Ok == ok
175		    io:format("schema_test3: Validity=~p,Ok=~p,S=~p~n", [Validity, Ok, S]),
176		    {{ModuleName, false}, S}
177	    end
178    end.
179schema_test(Config, FileName, XsdBase, Validity, AccState) ->
180    ModuleName = filename:basename(FileName),
181    DataDir = datadir(Config),
182    case xmerl_xsd:process_schema(filename:join([DataDir, FileName]),
183                                  [{xsdbase,filename:join([DataDir, XsdBase])}, AccState]) of
184        {error, enoent} ->
185            {{ModuleName, enoent}, AccState};
186        {Ok, S} ->
187            case Validity of
188                valid when Ok == ok ->
189                    {{ModuleName, S#xsd_state.errors == []}, S};
190                invalid when Ok == error ->
191                    {{ModuleName, no_internal_error(S)}, AccState};
192                notKnown ->
193                    {{ModuleName, true}, AccState};
194                valid ->
195                    {{ModuleName, false}, AccState};
196                _ ->
197                    {{ModuleName, false}, S}
198            end
199    end.
200instance_test(Config, FileName, XMLBase, Validity, State) ->
201    ModuleName = filename:basename(FileName),
202    DataDir = datadir(Config),
203    case xmerl_scan:file(filename:join([DataDir, FileName]),
204                         [{xmlbase,filename:join([DataDir, XMLBase])}]) of
205        {error, enoent} ->
206            {ModuleName, enoent};
207        {E, _} ->
208            {VE, S2} = xmerl_xsd:validate(E, State),
209            case Validity of
210                valid when is_record(VE, xmlElement) ->
211                    case S2#xsd_state.errors of
212                        [] -> ok;
213                        _ -> io:format("test case ~p failed.~nValidity: ~p~nValidation result:~p~n", [FileName, Validity, VE])
214                    end,
215                    {ModuleName, S2#xsd_state.errors == []};
216                invalid when VE == error ->
217                    {ModuleName, no_internal_error(S2)};
218                notKnown ->
219                    {ModuleName, true};
220                _ ->
221                    io:format("test case ~p failed.~nValidity: ~p~nValidation result:~p~n", [FileName, Validity, VE]),
222                    {ModuleName,false}
223            end
224    end.
225
226no_internal_error(R) ->
227    case lists:keymember(internal_error,1,R) of
228	true ->
229	    false;
230	_ ->
231	    true
232    end.
233
234unpack(Config, Suite) ->
235    TarFile = suite_tar(Suite),
236    file:set_cwd(datadir(Config)),
237    ok=erl_tar:extract(TarFile, [compressed]),
238    change_mode(filename:rootname(TarFile, ".tar.gz")).
239
240suite_tar(sun) ->
241    "suntest.tar.gz";
242suite_tar(msx) ->
243    "msxsdtest.tar.gz";
244suite_tar(nist) ->
245    "nisttest.tar.gz".
246
247change_mode(Files) ->
248    change_mode3(Files).
249change_mode2(Dir)->
250    {ok, CWD} = file:get_cwd(),
251    {ok, FileList} = file:list_dir(Dir),
252    file:set_cwd(filename:join([CWD, Dir])),
253    change_mode3(FileList),
254    file:set_cwd(CWD).
255change_mode3([]) ->
256    ok;
257change_mode3([F |Fs]) ->
258    case filelib:is_dir(F) of
259        true ->
260            chmod(F),
261            change_mode2(F);
262        _ ->
263            chmod(F)
264    end,
265    change_mode3(Fs).
266
267chmod(F) ->
268    case file:read_file_info(F) of
269        {ok, FileInfo} ->
270            Mode= FileInfo#file_info.mode,
271            file:write_file_info(F, FileInfo#file_info{mode=8#00777 bor Mode});
272        _ ->
273            ok
274    end.
275
276rmdir(Config, Suite) ->
277    file:set_cwd(datadir(Config)),
278    SuiteDir = filename:rootname(suite_tar(Suite), ".tar.gz"),
279    ok=rm_f_(SuiteDir).
280
281%% Dir is a directory
282rm_f_(Dir) ->
283    {ok, CWD} = file:get_cwd(),
284    {ok, FileList} = file:list_dir(Dir),
285    file:set_cwd(filename:join([CWD, Dir])),
286    rm_files(FileList),
287    file:set_cwd(CWD),
288    ok = file:del_dir(Dir).
289
290rm_files([])->
291    ok;
292rm_files([F |Fs]) ->
293    case filelib:is_dir(F) of
294	true ->
295	    rm_f_(F);
296	_ ->
297	    io:format("rm_files: ~p~n", [F]),
298	    ok = file:delete(F)
299    end,
300    rm_files(Fs).
301
302create_error_log_file(Config, Suite) ->
303    {{Y, M, D}, {H, Min, S}} = calendar:local_time(),
304    DTString=lists:concat([Y, "-", M,"-", D, "_", H, ".", Min, ".", S]),
305    FileName = lists:concat([Suite, "_", DTString, ".errorlog"]),
306%%    {ok,_IO} = file:open(filename:join([privdir(Config),
307%% 					      FileName]),[append]).
308
309%%    {ok,_IO} = file:open(FileName,[append]).
310    io:format("error log file: ~p~n", [filename:join([privdir(Config), FileName])]),
311    {ok, filename:join([privdir(Config), FileName])}.
312
313close_error_log_file(Config) ->
314    case lists:keysearch(xmerl_error_log, 1, Config) of
315        {value,{_, IO}} ->
316            file:close(IO);
317        _ ->
318            ok
319    end.
320
321privdir(Config) ->
322    proplists:get_value(priv_dir, Config).
323datadir(Config) ->
324    proplists:get_value(data_dir, Config).
325