testsuite : type.

testcase : testsuite -> prop.

%extend TODO.
testcase : testsuite -> prop.
%end.

run_tests : testsuite -> prop.
run_tests : list testsuite -> prop.

verbose_run_tests : prop.
run_tests (X : testsuite) :- run_tests [ X ] .

%extend testing_private.

handle_result : list clause -> list clause -> prop.
handle_result Successes [] :-
  length Successes Tests, tostring Tests TestsS,
  print_string `\n\n=== TESTS SUCCESSFUL (${TestsS} passed, 0 failed) ===\n\n`.

handle_result Successes Failures when not(eq Failures []) :-
  length Successes SuccessN, tostring SuccessN SuccessS,
  length Failures FailureN, tostring FailureN FailureS,
  print_string `\n\n=== TESTS FAILED (${SuccessS} passed, ${FailureS} failed) ===\n\n`,
  failure.

handle_failure : clause -> prop.
handle_failure TestCase :-
  log_error TestCase `Test failure for testcase`.

run_test : list testsuite -> list clause -> list clause -> prop -> list clause -> prop.
run_test Suites Successes Failures (testcase X) [] :- handle_result Successes Failures.
run_test Suites Successes Failures (testcase X) (TestCase :: TL) :-
  if (once(find (fun suite => clause.applies (testcase suite) TestCase) Suites _))
  then (
    if (once(verbose_run_tests)) then (
      tostring TestCase TestCaseS,
      log_info_do TestCase "Testcase" `${TestCaseS}`
    ) else success,
    if (once(clause.premise TestCase))
    then (run_test Suites (TestCase :: Successes) Failures (testcase X) TL)
    else (handle_failure TestCase, run_test Suites Successes (TestCase :: Failures) (testcase X) TL)
  )
  else (run_test Suites Successes Failures (testcase X) TL).

print_tests_aux : prop -> list clause -> prop.
print_tests_aux (testcase Suite) [].
print_tests_aux (testcase Suite) (HD :: TL) :-
 if (eq HD (clause (testcase Suite) Prem))
 then (print Prem)
 else success,
 print_tests_aux (testcase Suite) TL.

%end.

print_tests : testsuite -> prop.

print_tests Suite :-
  once(demand.aux_demand testing_private.print_tests_aux (testcase Suite)).

run_tests Suites :-
  if (refl.isunif Suites)
  then (eq SuitesStr " all")
  else (map tostring Suites (SuitesStrs : list string),
        foldl (pfun cur elm res => [X] expansion.str `${cur} - ${elm}` res) "" SuitesStrs SuitesStr),
  print_string `\n\n=== RUNNING TESTS (test suites:${SuitesStr})\n`,
  once(demand.aux_demand (testing_private.run_test Suites [] []) (testcase X)).
