% Copyright (C) 2003-2019 Olivier Boudeville % % This file is part of the Ceylan-Myriad library. % % This library is free software: you can redistribute it and/or modify % it under the terms of the GNU Lesser General Public License or % the GNU General Public License, as they are published by the Free Software % Foundation, either version 3 of these Licenses, or (at your option) % any later version. % You can also redistribute it and/or modify it under the terms of the % Mozilla Public License, version 1.1 or later. % % This library is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU Lesser General Public License and the GNU General Public License % for more details. % % You should have received a copy of the GNU Lesser General Public % License, of the GNU General Public License and of the Mozilla Public License % along with this library. % If not, see and % . % % Author: Olivier Boudeville [olivier (dot) boudeville (at) esperide (dot) com] % This module defines a few basic facilities for tests, at the level of the % 'Myriad' layer. % -module(test_facilities). % To be output before each displayed message: -define( display_prefix, " " ). -export([ start/1, stop/0, display/1, display/2, fail/1, fail/2, finished/0 ] ). % Starts a test; expected to be the first test statement. % % Here we disable explicitly the trapping of EXIT events, as a function run % through "erl -eval" (like our tests) or through "erl -run" will be executed in % a process which will silently trap EXIT events, which would mean that the % crash of any process created from the test, even thanks to spawn_link, would % most probably remain unnoticed (just leading to an EXIT message happily % sitting in the mailbox of the test process). % -spec start( module() | [ module() ] ) -> void(). start( Module ) when is_atom( Module ) -> start_common(), basic_utils:display( "~n~n--> Testing module ~s.~n", [ Module ] ); start( Modules ) when is_list( Modules ) -> start_common(), basic_utils:display( "~n~n--> Testing modules ~p.~n", [ Modules ] ). % (helper) start_common() -> erlang:process_flag( trap_exit, false ), % We want to be ensure that the standard logger behaves synchronously (ex: % not wanting an error trace to be lost because we crashed on purpose the VM % just after an error was reported, yet happened not to be notified yet as a % corresponding was not sent yet, or was received but not yet processed). ok = logger:set_handler_config( _HandlerId=default, _Key=sync_mode_qlen, _Value=0 ). % Stops a test; expected to be the last test statement in the normal case. -spec stop() -> no_return(). stop() -> basic_utils:display( "\n--> Successful end of test.\n" ), finished(). % Displays a test message. -spec display( string() ) -> void(). display( Message ) -> % Carriage return already added in basic_utils:display/1: % (empty list added so that ~n are automatically converted) basic_utils:display( lists:flatten( Message ), [] ). % Displays a test message, once formatted. % % FormatString is an io:format-style format string, ValueList is the % corresponding list of field values. % -spec display( string(), list() ) -> void(). display( FormatString, ValueList ) -> basic_utils:display( FormatString, ValueList ). % Comment out to be able to use the interpreter after the test: % % (as a result, the default is to immediately exit once a test is over) % -define( exit_after_test, ). -spec finished() -> no_return(). -ifdef(exit_after_test). finished() -> basic_utils:display( "(test finished, interpreter halted)" ), % Probably not that useful: system_utils:await_output_completion(), % Implies flushing as well: basic_utils:stop_on_success(), % Useless, but otherwise Dialyzer will complain that this function has no % local return: % test_success. -else. % exit_after_test finished() -> basic_utils:display( "(test finished, interpreter still running)~n" "(if the Erlang shell is not available, ensure that " "no '-noinput' VM command-line option is used;~n" " see EXEC_INTERNAL_OPTIONS in Ceylan-Myriad's " "GNUmakevars.inc for that)", _Necessary=[] ), %system_utils:await_output_completion(), test_success. -endif. % exit_after_test % To be called whenever a test is to fail (crash on error) immediately. % % Ex: test_facilities:fail( "server on strike" ) % -spec fail( string() ) -> no_return(). fail( Reason ) -> % For some reason erlang:error is unable to interpret strings as strings, % they are always output as unreadable lists. basic_utils:display( "~n!!!! Test failed, reason: ~s~n", [ Reason ] ), % Never returns: erlang:error( "Test failed" ), % Hence probably not that useful: system_utils:await_output_completion(), basic_utils:stop_on_failure(), % Useless, but otherwise Dialyzer will complain that this function has no % local return: test_failed. % To be called whenever a test is to fail (crash on error) immediately. % % FormatString is an io:format-style format string, ValueList is the % corresponding list of field values. % % Ex: test_facilities:fail( "server ~s on strike", [ "foobar.org" ] ) % -spec fail( string(), list() ) -> no_return(). fail( FormatString, ValueList ) -> % For some reason, erlang:error is unable to interpret strings as strings, % they are always output as unreadable lists. ReasonMessage = text_utils:format( FormatString, ValueList ), fail( ReasonMessage ).