diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c20c2ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__ + diff --git a/README.md b/fifth/README.md similarity index 58% rename from README.md rename to fifth/README.md index 4bf3365..d8a776c 100644 --- a/README.md +++ b/fifth/README.md @@ -20,26 +20,38 @@ python fifth.py ### Example ``` -stack is: [] -PUSH 10 -stack is: [10] -PUSH 5 -stack is: [10, 5] +stack is [] +PUSH 3 +stack is [3] +PUSH 11 +stack is [3, 11] + -stack is: [15] -PUSH 45 -stack is: [15, 45] +stack is [14] +DUP +stack is [14, 14] +PUSH 2 +stack is [14, 14, 2] +* +stack is [14, 28] +SWAP +stack is [28, 14] / -stack is: [3] +stack is [2] ++ +ERROR: two numbers required +POP +stack is [] EXIT ``` ## Tests ```bash -python -m pytest test_stack_calculator.py +pip install pytest +pytest -v test_fifth.py ``` ## Requirements - Python 3.10+ +- pytest diff --git a/fifth.py b/fifth/fifth.py similarity index 95% rename from fifth.py rename to fifth/fifth.py index 0f6cda7..6cb7088 100644 --- a/fifth.py +++ b/fifth/fifth.py @@ -82,9 +82,9 @@ def main(): fifth = FifthStack() while True: - print(f"stack is: {fifth.stack}") + print(f"stack is {fifth.stack}") try: - if command := input().strip().lower() == "exit": + if (command := input().strip().lower()) == "exit": break if command: fifth.execute(command) diff --git a/fifth/requirements.txt b/fifth/requirements.txt new file mode 100644 index 0000000..8e0e884 --- /dev/null +++ b/fifth/requirements.txt @@ -0,0 +1 @@ +pytest==8.4.1 diff --git a/fifth/test_fifth.py b/fifth/test_fifth.py new file mode 100644 index 0000000..c70433d --- /dev/null +++ b/fifth/test_fifth.py @@ -0,0 +1,166 @@ +import pytest +from fifth import FifthStack + + +# Test the functions +def test_push_and_pop(): + stack = FifthStack() + stack.push(10) + stack.push(20) + assert stack.stack == [10, 20] + stack.pop() + assert stack.stack == [10] + stack.pop() + assert stack.stack == [] + + +def test_push_invalid_value(capsys): + stack = FifthStack() + stack.push("abc") + captured = capsys.readouterr() + assert "ERROR: integer required" in captured.out + assert stack.stack == [] + + +def test_pop_empty_stack(capsys): + stack = FifthStack() + stack.pop() + captured = capsys.readouterr() + assert "ERROR: stack empty" in captured.out + + +def test_swap(): + stack = FifthStack() + stack.push(1) + stack.push(2) + stack.swap() + assert stack.stack == [2, 1] + + +def test_swap_insufficient_elements(capsys): + stack = FifthStack() + stack.push(1) + stack.swap() + captured = capsys.readouterr() + assert "ERROR: two numbers required" in captured.out + assert stack.stack == [1] + + +def test_dup(): + stack = FifthStack() + stack.push(5) + stack.dup() + assert stack.stack == [5, 5] + + +def test_dup_empty_stack(capsys): + stack = FifthStack() + stack.dup() + captured = capsys.readouterr() + assert "ERROR: stack empty" in captured.out + + +def test_addition(): + stack = FifthStack() + stack.push(2) + stack.push(3) + stack.execute("+") + assert stack.stack == [5] + + +def test_subtraction(): + stack = FifthStack() + stack.push(5) + stack.push(2) + stack.execute("-") + assert stack.stack == [3] + + +def test_multiplication(): + stack = FifthStack() + stack.push(4) + stack.push(3) + stack.execute("*") + assert stack.stack == [12] + + +def test_division(): + stack = FifthStack() + stack.push(8) + stack.push(2) + stack.execute("/") + assert stack.stack == [4] + + +def test_division_by_zero(capsys): + stack = FifthStack() + stack.push(8) + stack.push(0) + stack.execute("/") + captured = capsys.readouterr() + assert "ERROR: division by zero" in captured.out + assert stack.stack == [8, 0] + + +def test_binary_op_insufficient_elements(capsys): + stack = FifthStack() + stack.push(1) + stack.execute("+") + captured = capsys.readouterr() + assert "ERROR: two numbers required" in captured.out + assert stack.stack == [1] + + +# Test executions +def test_execute_unknown_command(capsys): + stack = FifthStack() + stack.execute("foobar") + captured = capsys.readouterr() + assert "ERROR: unknown command" in captured.out + + +def test_execute_push_command(): + stack = FifthStack() + stack.execute("push 42") + assert stack.stack == [42] + + +def test_execute_pop_command(): + stack = FifthStack() + stack.push(99) + stack.execute("pop") + assert stack.stack == [] + + +def test_execute_swap_command(): + stack = FifthStack() + stack.push(1) + stack.push(2) + stack.execute("swap") + assert stack.stack == [2, 1] + + +def test_execute_dup_command(): + stack = FifthStack() + stack.push(7) + stack.execute("dup") + assert stack.stack == [7, 7] + + +# Test edge cases +def test_execute_empty_command(): + stack = FifthStack() + stack.execute("") # Should do nothing + assert stack.stack == [] + + +def test_execute_whitespace_command(): + stack = FifthStack() + stack.execute(" ") # Should do nothing + assert stack.stack == [] + + +def test_case_insensitivity(): + stack = FifthStack() + stack.execute("PUSH 42") + assert stack.stack == [42]