"""
Lesson 25 — Exercise 3: Black Box vs White Box Testing
========================================================
Goal: Feel the difference between testing from a spec vs testing from the code.

The TWO halves of this exercise use the SAME function.
But the first half pretends you can't see the code.
"""


# ═══════════════════════════════════════════════════════════════
# PART 1 — BLACK BOX TEST
# ═══════════════════════════════════════════════════════════════
# Below is a "mystery function". You only know its SPECIFICATION:
#
#     SPEC:  mystery(a, b) takes two NON-NEGATIVE integers.
#            Returns their greatest common divisor (GCD).
#            If either input is 0, returns 0.
#
# Write test_mystery_black_box() based on the spec ALONE.
# ⚠️ Don't read the source code yet — that's the whole point of black box.

def mystery(a, b):
    # ⚠️ Scroll past this for Part 1. Come back to it for Part 2.
    if a == 0 or b == 0:
        return 0
    while b:
        a, b = b, a % b
    return a


def test_mystery_black_box():
    # TODO: based on the SPEC, list test cases.
    # Things to think about purely from "what should GCD do":
    #   - Both zero
    #   - One zero, one non-zero
    #   - Both equal
    #   - Two primes (e.g. 7 and 13)
    #   - One divides the other (e.g. 12 and 4)
    #   - Two larger numbers (e.g. 48 and 18)
    test_cases = [
        # (a, b, expected),
    ]
    for a, b, expected in test_cases:
        result = mystery(a, b)
        status = '✅' if result == expected else '❌'
        print(f"  {status} mystery({a}, {b}) = {result}  (expected {expected})")


# ═══════════════════════════════════════════════════════════════
# PART 2 — WHITE BOX TEST
# ═══════════════════════════════════════════════════════════════
# Now look at mystery()'s source code above.
#
# QUESTION: How many distinct BRANCHES / PATHS does it have?
# Identify them. (Hint: there's the early-return, and there's
# the while-loop behaviour — what cases force the loop to run
# many times vs zero times?)
#
# Write test_mystery_white_box() — pick one input PER branch
# that you identified by reading the code.

def test_mystery_white_box():
    # TODO: each test case should include WHY you chose it.
    test_cases = [
        # (a, b, expected, branch_or_path_being_exercised),
        # (0, 5, 0, "early-return: a == 0"),
        # ...
    ]
    for a, b, expected, reason in test_cases:
        result = mystery(a, b)
        status = '✅' if result == expected else '❌'
        print(f"  {status} mystery({a}, {b}) = {result}  ({reason})")


# ═══════════════════════════════════════════════════════════════
# DISCUSSION (write your answers in comments)
# ═══════════════════════════════════════════════════════════════
# 1. Which approach felt easier — black box or white box? Why?
# 2. Which one is more likely to MISS a bug? Why?
# 3. Which one might design tests that look fine but never exercise
#    a particular real-world input?


if __name__ == '__main__':
    print("─── BLACK BOX (spec only) ───")
    test_mystery_black_box()
    print()
    print("─── WHITE BOX (source-aware) ───")
    test_mystery_white_box()
