aboutsummaryrefslogblamecommitdiffstats
path: root/lib/erl_interface/test/runner.erl
blob: 484890006edf1bd90d584e193c654a4e7a905d1b (plain) (tree)
1
2
3
4
5


                   
                                                        
   










                                                                           






                 


                                                                

                                  
                               





                                                                     

                                       
 

                             







                                                                         
                              







                                                          


                                                               



                                        





































                                                                                          






                                                                     





                                          











                                                           
                                                     








                                                                       
                              



                                   
                                                         








                                     
                             
                      
                            










                                                                      
                                                      




                                              


                                                                  
                    
                                                 
        
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%

%%
-module(runner).

-export([test/2, test/3,
         init_per_testcase/3,
	 start/2, send_term/2, finish/1, send_eot/1, recv_eot/1,
	 get_term/1, get_term/2]).

-define(default_timeout, 5000).

%% Executes a test case in a C program.
%%
%% This function is useful for test cases written in C which requires
%% no further input, and only returns a result by calling report().

test(Config, Tc) ->
    test(Config, Tc, ?default_timeout).

test(Config, Tc, Timeout) ->
    Port = start(Config, Tc),
    
    case get_term(Port, Timeout) of
	eot ->
	    ok;
	Other ->
	    io:format("In this test case, a success/failure result was"),
	    io:format("expected from the C program.\n"),
	    io:format("Received: ~p", [Other]),
	    ct:fail(badresult)
    end.

%% Executes a test case in a C program.  Returns the port.
%%
%% Use get_term/1,2.
%%
%% Returns: {ok, Port}

start(Config, {Prog, Tc}) when is_list(Prog), is_integer(Tc) ->
    Port = open_port({spawn, prog_cmd(Config, Prog)},
                     [{packet, 4}, exit_status]),
    Command = [Tc div 256, Tc rem 256],
    Port ! {self(), {command, Command}},
    Port.

prog_cmd(Config, Prog) ->
    case proplists:get_value(valgrind_cmd_fun, Config) of
        undefined ->
            Prog;
        Fun when is_function(Fun) ->
            Fun(Prog)
    end.

init_per_testcase(Suite, Case, Config) ->
    case os:getenv("VALGRIND_LOG_DIR") of
        false ->
            Config;
        LogDir ->
            Valgrind = case os:find_executable("valgrind") of
                           false ->
                               ct:fail("VALGRIND_LOG_DIR set, "
                                       "but no valgrind executable found");
                           VG -> VG
                       end,

            LogFileOpt = case os:getenv("VALGRIND_LOG_XML") of
                             false ->
                                 " --log-file=";
                             "yes" ->
                                 " --xml=yes --xml-file="
                         end,
            Fun = fun(Prog) ->
                          LogFile = io_lib:format("erl_interface-~w.~w-~s.log.%p",
                                                  [Suite, Case, filename:basename(Prog)]),
                          Valgrind
                              ++ LogFileOpt ++ filename:join(LogDir,LogFile)
                              ++ " " ++ os:getenv("VALGRIND_MISC_FLAGS","")
                              ++ " " ++ Prog
                  end,
            [{valgrind_cmd_fun, Fun} | Config]
    end.


%% Finishes a test case by send an 'eot' message to the C program
%% and waiting for an 'eot'.
%%
%% If the C program doesn't require an 'eot', use recv_eot/1 instead.

finish(Port) when is_port(Port) ->
    send_eot(Port),
    ok = recv_eot(Port),
    0 = receive
            {Port,{exit_status,Status}} ->
                Status
        end,
    ok.

%% Sends an Erlang term to a C program.

send_term(Port, Term) when is_port(Port) ->
    Port ! {self(), {command, [$t, term_to_binary(Term)]}}.

%% Sends an 'eot' (end-of-test) indication to a C progrm.

send_eot(Port) when is_port(Port) ->
    Port ! {self(), {command, [$e]}}.

%% Waits for an 'eot' indication from the C program.
%% Either returns 'ok' or invokes ct:fail(badresult).

recv_eot(Port) when is_port(Port) ->    
    case get_term(Port) of
	eot ->
	    ok;
	Other ->
	    io:format("Error finishing test case.  Expected eof from"),
	    io:format("C program, but got:"),
	    io:format("~p", [Other]),
	    ct:fail(badresult)
    end.

%% Reads a term from the C program.
%%
%% Returns: {term, Term}|eot|'NULL' or calls ct:fail/1,2.

get_term(Port) ->
    get_term(Port, ?default_timeout).

get_term(Port, Timeout) ->
    case get_reply(Port, Timeout) of
	[$b|Bytes] ->
	    {bytes, Bytes};
	[$f] ->
	    ct:fail(failure);
	[$f|Reason] ->
	    ct:fail(Reason);
	[$t|Term] ->
	    {term, binary_to_term(list_to_binary(Term))};
	[$N] ->
	    'NULL';
	[$e] ->
	    eot;
	[$m|Message] ->
	    io:format("~s", [Message]),
	    get_term(Port, Timeout);
	Other ->
	    io:format("Garbage received from C program: ~p", [Other]),
	    ct:fail("Illegal response from C program")
    end.

get_reply(Port, Timeout) when is_port(Port) ->
    receive
	{Port, {data, Reply}} ->
	    Reply;
        Fail when element(1, Fail) == Port ->
            ct:fail("Got unexpected message from port: ~p",[Fail])
    after Timeout ->
	    ct:fail("No response from C program")
    end.