note description: "Testable components consisting out of a single test." legal: "See notice at end of class." status: "See notice at end of class." date: "$Date$" revision: "$Revision$" deferred class SINGLE_TEST inherit TESTABLE EXCEPTIONS export {NONE} all end feature -- Access test_results: TEST_CASE_RESULT -- Results of test feature -- Measurement run_count: INTEGER -- Number of runs total_run_count: INTEGER -- Number of runs -- (Equal to `run_count' for single tests.) do Result := run_count end feature -- Status report Is_test_container: BOOLEAN = False -- is test a container? (Answer: no) Has_random_generator: BOOLEAN = False -- Does current object have access to a random number generator? -- (Answer: no) is_rescue_enabled: BOOLEAN -- Is exception trapping enabled? do Result := not rescue_disabled_flag end is_prefix_set: BOOLEAN -- Is prefix set? do Result := prefix_string /= Void end is_reason_set: BOOLEAN -- Is reason set? do Result := reason /= Void end has_current_result: BOOLEAN -- Has a current result been recorded? do Result := test_results /= Void and then test_results.has_current_result end is_ready: BOOLEAN -- Is test ready to be executed? do Result := (not Top_level_allowed implies is_container_set) and is_number_set and is_name_set end has_passed (n: INTEGER): BOOLEAN -- Has run `n' of test passed? local old_run: INTEGER do old_run := test_results.run test_results.select_run (n) Result := test_results.has_passed test_results.select_run (old_run) end is_exception (n: INTEGER): BOOLEAN -- Did run `n' of test throw an exception? local old_run: INTEGER do if has_results then old_run := test_results.run test_results.select_run (n) Result := test_results.is_exception test_results.select_run (old_run) end end has_execution_time (n: INTEGER): BOOLEAN -- Has run `n' of test a recorded execution time? local old_run: INTEGER do old_run := test_results.run test_results.select_run (n) Result := test_results.has_execution_time test_results.select_run (old_run) end has_any_execution_time: BOOLEAN -- Does any test have a recorded execution time? local old_run: INTEGER i: INTEGER do old_run := test_results.run from i := 1 until Result or i > run_count loop test_results.select_run (i) Result := test_results.has_any_execution_time i := i + 1 end test_results.select_run (old_run) ensure then run_unchanged: test_results.run = old test_results.run end feature -- Status setting set_prefix (s: STRING) -- Set prefix to `s'. require not_set: not is_prefix_set non_empty_string: s /= Void and then not s.is_empty do prefix_string := s ensure prefix_set: prefix_string = s end set_reason (s: STRING) -- Set reason to `s'. require not_set: not is_reason_set non_empty_string: s /= Void and then not s.is_empty do reason := s ensure reason_set: reason = s end reset_messages -- Reset optional messages. do prefix_string := Void reason := Void ensure no_prefix: not is_prefix_set no_reason: not is_reason_set end enable_rescue -- Enable exception trapping. do rescue_disabled_flag := False ensure enabled: is_rescue_enabled end disable_rescue -- Disable exception trapping. do rescue_disabled_flag := True ensure disabled: not is_rescue_enabled end clear_results -- Clear results. do test_results.clear_results end feature -- Basic operations execute -- Execute test. do run_count := run_count + 1 if is_rescue_enabled then run_with_rescue else run_without_rescue end check result_available_if_expected: produces_result implies has_current_result -- Because if test produces a result, one has been -- established by now end if produces_result then test_results.finish_recording end if has_standard_output then standard_output.put_character ('.') end ensure then recording_finished: not has_current_result one_more_run: produces_result implies run_count = old run_count + 1 run_count_consistent: produces_result implies (run_count = test_results.run_count) end do_test -- Do test action. deferred end feature {NONE} -- Basic operations assert (a: BOOLEAN; n: STRING) -- Check if assertion `a' with name `n' holds. require produces_result: produces_result name_exists: n /= Void reason_or_name: is_reason_set xor not name.is_empty do check_assertion (a, n) ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end assert_equal (a, b: ANY) -- Assert that `a' and `b' are equal. require produces_result: produces_result do check_assertion (equal (a, b), "expected: <" + b.out + "> but was: <" + a.out + ">") ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end assert_not_equal (a, b: ANY) -- Assert that `a' and `b' are not equal. require produces_result: produces_result do check_assertion (not equal (a, b), "Unexpected value: <" + a.out + ">") ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end assert_equal_double (a, b, delta: DOUBLE) -- Assert that floating point number `a' and `b' are equal. require produces_result: produces_result local minval: DOUBLE maxval: DOUBLE do minval := a.min (b) maxval := a.max (b) check_assertion ((maxval - minval <= delta), "DOUBLEs not equal, expected delta: " + delta.out + " actual delta: " + (maxval - minval).out) ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end assert_void (a: ANY; n: STRING) -- Assert that `a' is Void and supply name `n'. require produces_result: produces_result name_exists: n /= Void do check_assertion (a = Void, "<" + n + "> is not Void!") ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end assert_not_void (a: ANY; n: STRING) -- Assert that `a' is not Void and supply name `n'. require produces_result: produces_result name_exists: n /= Void do check_assertion (a /= Void, "<" + n + "> is Void!") ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end assert_same (a, b: ANY) -- Assert that `a' and `b' are the same objects. require produces_result: produces_result do check_assertion (a = b, "<" + a.out + "> is not the same object as <" + b.out + ">!") ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end assert_not_same (a, b: ANY) -- Assert that `a' and `b' are not the same objects. require produces_result: produces_result do check_assertion (a = b, "<" + a.out + "> is the same object as <" + b.out + ">!") ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end assert_exception -- Assert that an exception is thrown. -- ATTENTION: Has to be called *before* the exception occurs. require produces_result: produces_result do exception_expected := True ensure exception_flag_set: exception_expected end pass -- Add a pass. require produces_result: produces_result do check_assertion (True, "") ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end fail (r: STRING) -- Add a failure with reason `r'. require produces_result: produces_result non_empty_reason: r /= Void and then not r.is_empty do check_assertion (False, r) ensure prefix_reset: not is_prefix_set reason_reset: not is_reason_set end feature -- Output put_summary (f: LOG_FACILITY) -- Output test summary to `f'. do f.put_summary (Current) end put_failure_information (f: LOG_FACILITY; n: INTEGER) -- Output failure information for run `n' to `f'. do f.put_failure_information (Current, n) end put_timing_information (f: LOG_FACILITY; n: INTEGER) -- Output timing information for run `n' to `f'. do f.put_timing_information (Current, n) end feature {NONE} -- Inapplicable seed: INTEGER -- Random seed do end set_seed (s: INTEGER) -- Set seed to `s'. do end feature {NONE} -- Implementation reason: STRING -- Optional alternate reason rescue_disabled_flag: BOOLEAN -- Is exception trapping disabled? exception_expected: BOOLEAN -- Is expected that test throws an exception? prefix_string: STRING -- Optional prefix check_assertion (a: BOOLEAN; m: STRING) -- Check if assertion `a' holds. If not, register failure with -- failure message `m' or alternate message `reason', if set. require produces_result: produces_result message_exists: m /= Void local msg: STRING do if a then test_results.add_pass else if reason /= Void then msg := reason.twin else msg := m.twin end if prefix_string /= Void then msg.prepend (": ") msg.prepend (prefix_string) end test_results.add_failure (msg) reset_messages end ensure no_prefix: not is_prefix_set no_reason: not is_reason_set end run_with_rescue -- Run test with exception trapping. require trapping_enabled: is_rescue_enabled local retried: BOOLEAN e: EXCEPTION_INFO do if not retried then run_without_rescue if exception_expected then fail ("Expected exception was not thrown!") end end rescue if exception_expected then test_results.clear_current_result pass else create e if is_developer_exception then e.set_type (developer_exception_name.twin) e.set_tag_name ("[No tag]") else e.set_type (meaning (original_exception).twin) e.set_tag_name (original_tag_name.twin) end e.set_origin_class (original_class_name.twin) e.set_origin_feature (original_recipient_name.twin) check complete: e.complete -- Because all information has been filled in. end test_results.add_exception (e) tear_down end retried := True retry end run_without_rescue -- Run test without exception trapping. do set_up do_test tear_down end compare_exception_info (src, dest: EXCEPTION_INFO): BOOLEAN -- Does `src' denote the same exception as `dest'? require no_void_source: src /= Void destination_complete: dest /= Void and then dest.complete local src_list: ARRAYED_LIST [STRING] dest_list: ARRAYED_LIST [STRING] do create src_list.make (4) create dest_list.make (4) src_list.extend (src.type) dest_list.extend (dest.type) if not src.origin_class.is_empty then src_list.extend (src.origin_class) dest_list.extend (dest.origin_class) end if not src.origin_feature.is_empty then src_list.extend (src.origin_feature) dest_list.extend (dest.origin_feature) end if not src.tag_name.is_empty then src_list.extend (src.tag_name) dest_list.extend (dest.tag_name) end check count_equal: src_list.count = dest_list.count -- Because items have been inserted pairwise into the list. end from src_list.start dest_list.start Result := True until not Result or else src_list.after loop Result := equal (src_list.item, dest_list.item) src_list.forth dest_list.forth end end invariant no_empty_prefix: prefix_string /= Void implies not prefix_string.is_empty no_empty_reason: reason /= Void implies not reason.is_empty run_count_equality: run_count = total_run_count note copyright: "Copyright (c) 1984-2019, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software 5949 Hollister Ave., Goleta, CA 93117 USA Telephone 805-685-1006, Fax 805-685-6869 Website http://www.eiffel.com Customer support http://support.eiffel.com ]" end