/* AutoTest.PL Shelved on the 21st of December 1987 */ /* PORTABILITY */ /* '$log_files'( List ): The name(s) of the log file. */ '$log_files'( [user,'AUTOTEST.LOG'] ). /* UTILITIES */ /* member( X+, L+ ): X is a member of list L. */ '$member'( X, [X|_] ) :- !. '$member'( X, [_|T] ) :- '$member'( X, T ), !. /* OUTPUT */ ?- op( 40, xfy, <> ). ?- op( 40, xfy, ... ). /* '$writef'( V+ ): Write V to the COS. If V = nl, take a newline. If V = A<>B, '$writef' A, then B. Treat A...B as A<> ' ' <>B. If V = '$'(L), assume L is a list of ASCII codes, turn into an atom, and write that. Else, write V as it is. */ '$writef'( V ) :- var( V ), !, write(V). '$writef'( A<>B ) :- !, '$writef'(A), '$writef'(B). '$writef'( A...B ) :- !, '$writef'(A), '$writef'(' '), '$writef'(B). '$writef'( '$'(L) ) :- !, name( A, L ), write( A ). '$writef'( nl ) :- !, nl. '$writef'( X ) :- write( X ). /* '$writef_to'( File+, Text+ ): Do '$writef'(Text), but tell(File) first, and restore the old COS after. */ '$writef_to'( File, Text ) :- telling( COS ), tell( File ), '$writef'( Text ), tell( COS ). /* LOG FILES */ /* '$writef_to_log'( Text+ ): writef(Text) to each log file. */ '$writef_to_log'( Text ) :- '$log_files'( LogFiles ), '$writef_to_logs'( LogFiles, Text ). '$writef_to_logs'( [], Text ) :- !. '$writef_to_logs'( [Log|Others], Text ) :- '$writef_to'( Log, Text ), '$writef_to_logs'( Others, Text ), !. /* switch_log_files_to( NewFiles ): Close any log files not named in NewFiles, and reset so that future '$writef_to_log's write to the new files. */ '$switch_log_files_to'( NewFiles ) :- '$close_log_files_except'( NewFiles ), '$set_new_log_files'( NewFiles ). '$set_new_log_files'( NewFile ) :- atom( NewFile ), asserta( '$log_files'([NewFile]) ), !. '$set_new_log_files'( NewFiles ) :- retract( '$log_files'(OldFiles) ), asserta( '$log_files'(NewFiles) ). /* '$close_log_files_except'( L+ ): Close any log files whose names are not in L. */ '$close_log_files_except'( NewFiles ) :- '$log_files'(OldFiles), '$close_log_files_except'( OldFiles, NewFiles ). '$close_log_files_except'( [], _ ) :- !. '$close_log_files_except'( [Old|OtherOlds], NewFiles ) :- member( Old, NewFiles ), '$close_log_files_except'( OtherOlds, NewFiles ), !. '$close_log_files_except'( [Old|OtherOlds], NewFiles ) :- telling( COS ), tell( Old ), told, tell( COS ), '$close_log_files_except'( OtherOlds, NewFiles ), !. /* MAIN PROGRAM */ /* '$run'( F+ ): MAIN PREDICATE. Read filenames from steering file F, and run the tests in each file. Close any open log files. */ '$run'( F ) :- '$read_and_obey_steering_file'( F ), '$close_log_files_except'( [] ). /* '$run_test'( F+ ): MAIN PREDICATE. Run the tests in F. Close any open log files. */ '$run_test'( F ) :- '$run_test_file'( F ), '$close_log_files_except'( [] ). '$read_and_obey_steering_file'( F ) :- seeing( CIS ), see( F ), seen, see( F ), '$read_and_obey_steering_file', seen, see( CIS ). /* '$read_and_obey_steering_file': Read the next line from the steering file. Unless at end-of-file, run the test file so named, and repeat. */ '$read_and_obey_steering_file' :- read( F ), F \= end_of_file, '$switch_files?'( F ). '$read_and_obey_steering_file'. /* Here if end of steering file. */ '$switch_files?'( stay ) :- '$run_test_file'. '$switch_files?'( D ) :- '$directive'( D ), '$obey_directive'( D ), '$read_and_obey_steering_file'. '$switch_files?'( F ) :- '$run_test_file'( F ), '$read_and_obey_steering_file', !. /* '$run_test_file'( F+ ): F names a file of tests. Open it, run each test, and close, resetting CIS to its original value. */ '$run_test_file'( F ) :- seeing( CIS ), see( F ), seen, see( F ), '$run_test_file', seen, see( CIS ). '$run_test_file' :- read( Term ), Term \= end_of_file, '$process_test_term'( Term ), '$run_test_file', !. '$run_test_file'. /* Here on end of test file. */ /* '$directive'( Term+ ): Term is a directive. */ '$directive'( log_all_tests(_) ) :- !. '$directive'( log_to(_) ) :- !. /* obey_directive( D+ ): Obey directive D, and succeed. */ '$obey_directive'( log_to(F) ) :- '$switch_log_files_to'(F), !. '$obey_directive'( log_all_tests(on) ) :- asserta( '$log_all_tests' ), !. '$obey_directive'( log_all_tests(off) ) :- retractall( '$log_all_tests' ), !. /* Operator used to join a goal and its specification. */ ?- op( 255, xfx, :: ). /* '$process_test_term'( Term+ ): Term is one term from a test file. If it's a term of the form Goal::Spec, pass to '$test'. If it's a directive, obey it. Else, assume it's to be written. */ '$process_test_term'( D ) :- '$directive'( D ), '$obey_directive'( D ), !. '$process_test_term'( Goal::Spec ) :- '$test'( Goal::Spec ), !. '$process_test_term'( Term ) :- '$writef_to_log'( Term ). /* '$test'( Term+ ): Term is a goal plus its specified (expected) action. Extract the specification, call the goal, and match its behaviour against the specification. */ '$test'( Goal_and_Spec ) :- '$specification'( Goal_and_Spec, Spec, Goal ), '$test_against_specification'( Goal, Spec ), !. /* '$specification'( Term+, Spec-, Goal- ): Separates a term into its goal and specification. */ '$specification'( (G::s), succeeds, G ) :- !. '$specification'( (G::succeeds), succeeds, G ) :- !. '$specification'( (G::f), fails, G ) :- !. '$specification'( (G::fails), fails, G ) :- !. '$specification'( (G::c), crashes, G ) :- !. '$specification'( (G::crashes), crashes, G ) :- !. '$specification'( (G::c(ErrorType)), crashes_with_error_code(ErrorType), G ) :- !. '$specification'( (G::crashes(ErrorType)), crashes_with_error_code(ErrorType), G ) :- !. '$specification'( (G::PostTests), must_pass(PostTests), G ) :- !. /* '$test_against_specification'( Goal+, Spec+ ): Call Goal, and match its effect against Spec. */ '$test_against_specification'( Goal, Spec ) :- '$call_giving_sfe'( Goal, SFE ), '$match_against_sfe'( Goal, Spec, SFE ), !. /* '$call_giving_sfe'( Goal+, SFE- ): Call Goal. Set SFE to: 'succeeds' if Goal succeeds; 'fails' if it fails; 'crashes_with_error_code(ErrorKind)' if it gives an error of kind ErrorKind; 'crashes' if it gives an error whose kind can't be determined. */ '$call_giving_sfe'( Goal, succeeds ) :- call( Goal ), !. '$call_giving_sfe'( Goal, fails ) :- !. /* '$matches_sfe'( Expected+, Actual+, Answer- ): Always succeeds. Answer is some English text specifying how the Actual result of a call matched the Expected result. If they matched OK, then Answer = ok. */ '$matches_sfe'( must_pass(Tests), succeeds, Text ) :- '$post_tests_ok'( Tests, Text ), !. '$matches_sfe'( must_pass(Tests), failed_tests(FE), Text ) :- '$english_for_action'( FE, Actually ), Text = ('should have passed tests' ... Tests ... 'but' ... Actually <> nl ), !. '$matches_sfe'( must_pass(Tests), FE, Text ) :- '$matches_sfe'( succeeds, FE, Text ), !. '$matches_sfe'( SFE, SFE, ok ) :- !. '$matches_sfe'( crashes, crashes_with_error_code(_), ok ) :- !. '$matches_sfe'( crashes_with_error_code(_), crashes, ok ) :- !. '$matches_sfe'( crashes_with_error_code(C), crashes_with_error_code(C), ok ) :- !. '$matches_sfe'( Expected, Actual, Text ) :- '$english_for_action'( Actual, Actually ), '$english_for_action'( Expected, Expectedly ), Text = ('should have' ... Expectedly ... 'but' ... Actually <> nl ), !. /* '$match_against_sfe'( Goal+, Expected+, Actual+ ): If the expected result differs from the actual, then write to the log, saying so. Write to the log also if the test succeeded and 'log_all_tests' was on. */ '$match_against_sfe'( Goal, Expected, Actual ) :- '$matches_sfe'( Expected, Actual, What ), '$report_match'( Goal, What ). '$report_match'( Goal, ok ) :- '$log_all_tests', '$writef_to_log'( 'Goal'...Goal...'passed test'<>nl ), !. '$report_match'( Goal, ok ) :- !. '$report_match'( Goal, NotOK ) :- '$writef_to_log'( 'Goal'...Goal...NotOK ), !. '$english_for_action'( succeeds, succeeded ) :- !. '$english_for_action'( fails, failed ) :- !. '$english_for_action'( crashes, crashed ) :- !. '$english_for_action'( crashes_with_error_code(ErrorKind), 'crashed with error' ... ErrorKind ) :- !. '$english_for_action'( must_pass(Tests), 'passed tests'...Tests ) :- !. /* '$post_tests_ok'( Tests+, Text- ): */ '$post_tests_ok'( Tests, Text ) :- '$call_giving_sfe'( Tests, TestsSFE ), ( TestsSFE = succeeds, Text = ok ; '$matches_sfe'( must_pass(Tests), failed_tests(TestsSFE), Text ) ), !.