From 022af5bf40933f62548416db19d2a11c35d5340c Mon Sep 17 00:00:00 2001 From: Vinicius Queiroz Date: Wed, 1 Jul 2026 14:17:01 -0300 Subject: [PATCH] =?UTF-8?q?[issue-88]=20fix:=20Warn=20On=20Failure=20n?= =?UTF-8?q?=C3=A3o=20=C3=A9=20oracle,=20n=C3=A3o=20reprova=20o=20teste=20(?= =?UTF-8?q?#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run Keyword And Warn On Failure estava agrupado com Continue On Failure em is_verification, recursando no keyword interno e contando como oracle. Mas Warn On Failure só loga um WARN e o teste segue verde: a asserção interna nunca reprova o teste. Mesmo problema do Run Keyword And Ignore Error (swallow). Separado: Continue On Failure mantém a recursão (oracle se o inner for); Warn On Failure retorna False sempre (não é oracle). Assim o teste cuja única verificação está dentro de Warn On Failure passa a cair em C2b, e um user keyword nomeado como verificador com esse wrapper vira R2 (hollow oracle). Regressões distinguindo os dois wrappers em tests/test_scanner.py. Closes #88 --- src/falsegreen_robot/scanner.py | 15 ++++++++++----- tests/test_scanner.py | 28 +++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/falsegreen_robot/scanner.py b/src/falsegreen_robot/scanner.py index 005f2ba..475e36a 100644 --- a/src/falsegreen_robot/scanner.py +++ b/src/falsegreen_robot/scanner.py @@ -263,15 +263,20 @@ def is_verification(keyword, args, local_keywords=None, extra_verify=None): key, sep, val = (a or "").partition("=") if sep and _norm(key) == "expected_status": return _norm(val) not in _EXPECTED_STATUS_OFF and bool(val.strip()) - # Run Keyword And Continue On Failure / And Warn On Failure *args - - # the soft-assert wrappers: the wrapped keyword still runs and is verified, the - # wrapper only changes how a failure is reported (continue / warn). It is an - # oracle exactly when the wrapped keyword is one, so recurse on the inner call. - if n in ("run keyword and continue on failure", "run keyword and warn on failure"): + # Run Keyword And Continue On Failure *args - a soft-assert wrapper: + # the wrapped keyword still runs and a failure is recorded, so the test FAILS at + # the end. It is an oracle exactly when the wrapped keyword is one, so recurse. + if n == "run keyword and continue on failure": inner = list(args or ()) if inner: return is_verification(inner[0], inner[1:], local_keywords, extra_verify) return False + # Run Keyword And Warn On Failure *args - a failure only logs a WARN + # and the test stays green (BuiltIn semantics). The wrapped assertion can never + # reprove the test, so this is NOT an oracle regardless of the inner keyword - + # same swallow problem as Run Keyword And Ignore Error (#88). + if n == "run keyword and warn on failure": + return False # Run Keywords A AND B AND Should Be Equal ... — a chain that # runs each keyword in sequence. The verification can be any segment, so split # on the AND separator and check each segment's first token as a nested call. diff --git a/tests/test_scanner.py b/tests/test_scanner.py index ab2b889..a2fc52d 100644 --- a/tests/test_scanner.py +++ b/tests/test_scanner.py @@ -2275,14 +2275,36 @@ def test_no_r2_continue_on_failure_around_assertion(tmp_path): assert "R2" not in codes(tmp_path, body) -def test_no_r2_warn_on_failure_around_assertion(tmp_path): - # The Warn On Failure variant is the same soft-assert wrapper. +def test_r2_warn_on_failure_around_assertion_is_hollow(tmp_path): + # #88: Warn On Failure only logs a WARN, the test stays green - the wrapped + # assertion cannot reprove the test, so the verifier keyword is hollow (R2). body = """\ *** Keywords *** Verify X Run Keyword And Warn On Failure Should Be Equal ${a} ${b} """ - assert "R2" not in codes(tmp_path, body) + assert "R2" in codes(tmp_path, body) + + +# #88: Warn On Failure is not an oracle (the failure only logs a WARN); Continue +# On Failure still fails the test at the end and remains a valid oracle. +def test_warn_on_failure_is_not_an_oracle_c2b(tmp_path): + body = """\ +*** Test Cases *** +Falso Verde + Run Keyword And Warn On Failure Should Be Equal ${1} ${2} +""" + assert "C2b" in codes(tmp_path, body) + + +def test_continue_on_failure_stays_an_oracle(tmp_path): + body = """\ +*** Test Cases *** +Soft Assert + Continue On Failure + Run Keyword And Continue On Failure Should Be Equal ${1} ${2} +""" + assert "C2b" not in codes(tmp_path, body) # FP-3 (C9b): expected_status=any with a manual status assert on the next line.