From ccc6a98379c34b1832a2d4af5c6e4718e7860b35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hallvard=20Tr=C3=A6tteberg?= <hal@ntnu.no>
Date: Sat, 14 Aug 2021 16:29:22 +0000
Subject: [PATCH] Some more tests and improved API

---
 .../src/main/java/app/AppController.java      |  5 ++
 javafx-template/src/main/java/app/Calc.java   | 52 +++++++++----------
 .../src/main/resources/app/App.fxml           |  7 ++-
 .../src/test/java/app/CalcTest.java           | 39 ++++++++++++++
 4 files changed, 75 insertions(+), 28 deletions(-)
 create mode 100644 javafx-template/src/test/java/app/CalcTest.java

diff --git a/javafx-template/src/main/java/app/AppController.java b/javafx-template/src/main/java/app/AppController.java
index 1cbbd29..d47da69 100644
--- a/javafx-template/src/main/java/app/AppController.java
+++ b/javafx-template/src/main/java/app/AppController.java
@@ -100,6 +100,11 @@ public class AppController {
         // TODO clear operand
     }
 
+    @FXML
+    void handleSwap() {
+        // TODO clear operand
+    }
+
     private void performOperation(UnaryOperator<Double> op) {
         // TODO
     }
diff --git a/javafx-template/src/main/java/app/Calc.java b/javafx-template/src/main/java/app/Calc.java
index 6102e31..8ff70b8 100644
--- a/javafx-template/src/main/java/app/Calc.java
+++ b/javafx-template/src/main/java/app/Calc.java
@@ -7,10 +7,13 @@ import java.util.function.UnaryOperator;
 
 public class Calc {
     
-    private List<Double> operandStack = new ArrayList<>();
+    private final List<Double> operandStack;
 
-    public Calc(Double... operands) {
-        operandStack.addAll(List.of(operands));
+    public Calc(double... operands) {
+        operandStack = new ArrayList<>(operands.length + 2);
+        for (var d : operands) {
+            operandStack.add(d);
+        }
     }
 
     /**
@@ -20,29 +23,24 @@ public class Calc {
         return operandStack.size();
     }
 
-    /**
-     * Pushes a new operand into the n'th place from the top.
-     * @param n the place to push
-     * @param d the new operand
-     */
-    public void pushOperand(int n, double d) {
-        operandStack.add(operandStack.size() - n, d);
-    }
-
     /**
      * Pushes a new operand onto top of the stack.
      * @param d the new operand
      */
     public void pushOperand(double d) {
-        pushOperand(0, d);
+        operandStack.add(d);
     }
 
     /**
      * @param n the place (from the top) to peek
      * @return the n'th operand from the top
+     * @throws IllegalArgumentException if n is larger than the operand count
      */
     public double peekOperand(int n) {
-        return operandStack.get(operandStack.size() - n - 1);
+        if (n > getOperandCount()) {
+            throw new IllegalArgumentException("Cannot peek at position " + n + " when the operand count is " + getOperandCount());
+        }
+        return operandStack.get(getOperandCount() - n - 1);
     }
 
     /**
@@ -52,30 +50,26 @@ public class Calc {
         return peekOperand(0);
     }
 
-    /**
-     * Removes and returns the n'th operand from the top.
-     * @param n the place from the top to remove
-     * @return the n'th operand from the top
-     */
-    public double popOperand(int n) {
-        return operandStack.remove(operandStack.size() - n - 1);
-    }
-
     /**
      * Removes and returns the top operand.
      * @return the top operand
+     * @throws IllegalStateException if the stack is empty
      */
     public double popOperand() {
-        return popOperand(0);
+        if (getOperandCount() == 0) {
+            throw new IllegalStateException("Cannot pop from an empty stack");
+        }
+        return operandStack.remove(operandStack.size() - 1);
     }
-
+    
     /**
      * Performs the provided operation in the top operand, and
      * replaces it with the result.
      * @param op the operation to perform
      * @return the result of performing the operation
+     * @throws IllegalStateException if the operand stack is empty
      */
-    public double performOperation(UnaryOperator<Double> op) {
+    public double performOperation(UnaryOperator<Double> op) throws IllegalStateException {
         // TODO
         return 0.0;
     }
@@ -85,8 +79,12 @@ public class Calc {
      * replaces them with the result.
      * @param op the operation to perform
      * @return the result of performing the operation
+     * @throws IllegalStateException if the operand count is less than two
      */
-    public double performOperation(BinaryOperator<Double> op) {
+    public double performOperation(BinaryOperator<Double> op) throws IllegalStateException {
+        if (getOperandCount() < 2) {
+            throw new IllegalStateException("Too few operands (" + getOperandCount() + ") on the stack");
+        }
         var op1 = popOperand();
         var op2 = popOperand();
         var result = op.apply(op1, op2);
diff --git a/javafx-template/src/main/resources/app/App.fxml b/javafx-template/src/main/resources/app/App.fxml
index 41f4e4f..7356474 100644
--- a/javafx-template/src/main/resources/app/App.fxml
+++ b/javafx-template/src/main/resources/app/App.fxml
@@ -15,7 +15,7 @@
 
    <!-- multi-line button label with XML entity for newline -->
    <Button text="E&#10;n&#10;t&#10;e&#10;r" onAction="#handleEnter"
-      GridPane.rowIndex="2" GridPane.columnIndex="3" GridPane.rowSpan="4"/>
+      GridPane.rowIndex="2" GridPane.columnIndex="3" GridPane.rowSpan="3"/>
 
    <Button text="7" onAction="#handleDigit"
       GridPane.rowIndex="2" GridPane.columnIndex="0"/>
@@ -23,24 +23,29 @@
       GridPane.rowIndex="2" GridPane.columnIndex="1"/>
    <Button text="9" onAction="#handleDigit"
       GridPane.rowIndex="2" GridPane.columnIndex="2"/>
+   
    <Button text="4" onAction="#handleDigit"
       GridPane.rowIndex="3" GridPane.columnIndex="0"/>
    <Button text="5" onAction="#handleDigit"
       GridPane.rowIndex="3" GridPane.columnIndex="1"/>
    <Button text="6" onAction="#handleDigit"
       GridPane.rowIndex="3" GridPane.columnIndex="2"/>
+   
    <Button text="1" onAction="#handleDigit"
       GridPane.rowIndex="4" GridPane.columnIndex="0"/>
    <Button text="2" onAction="#handleDigit"
       GridPane.rowIndex="4" GridPane.columnIndex="1"/>
    <Button text="3" onAction="#handleDigit"
       GridPane.rowIndex="4" GridPane.columnIndex="2"/>
+   
    <Button text="0" onAction="#handleDigit"
       GridPane.rowIndex="5" GridPane.columnIndex="0"/>
    <Button text="." onAction="#handlePoint"
       GridPane.rowIndex="5" GridPane.columnIndex="1"/>
    <Button text="C" onAction="#handleClear"
       GridPane.rowIndex="5" GridPane.columnIndex="2"/>
+   <Button text="~" onAction="#handleSwap"
+      GridPane.rowIndex="5" GridPane.columnIndex="3"/>
 
    <Button text="+" onAction="#handleOpAdd"
       GridPane.rowIndex="6" GridPane.columnIndex="0"/>
diff --git a/javafx-template/src/test/java/app/CalcTest.java b/javafx-template/src/test/java/app/CalcTest.java
new file mode 100644
index 0000000..9a7c0df
--- /dev/null
+++ b/javafx-template/src/test/java/app/CalcTest.java
@@ -0,0 +1,39 @@
+package app;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class CalcTest {
+
+    private static void checkCalc(Calc calc, double... operands) {
+        Assertions.assertEquals(operands.length, calc.getOperandCount(), "Wrong operand count");
+        for (int i = 0; i < operands.length; i++) {
+            Assertions.assertEquals(operands[i], calc.peekOperand(i), "Wrong value at #" + i + " of operand stack");
+        }
+    }
+
+    @Test
+    public void testCalc() {
+        checkCalc(new Calc());
+        checkCalc(new Calc(1.0), 1.0);
+        checkCalc(new Calc(3.14, 1.0), 1.0, 3.14);
+    }
+
+    @Test
+    public void testPushOperand() {
+        Calc calc = new Calc();
+        calc.pushOperand(1.0);
+        checkCalc(calc, 1.0);
+        calc.pushOperand(3.14);
+        checkCalc(calc, 3.14, 1.0);
+    }
+
+    @Test
+    public void testPopOperand() {
+        Calc calc = new Calc(1.0, 3.14);
+        Assertions.assertEquals(3.14, calc.popOperand());
+        checkCalc(calc, 1.0);
+        Assertions.assertEquals(1.0, calc.popOperand());
+        checkCalc(calc);
+    }
+}
-- 
GitLab