diff --git a/.project b/.project new file mode 100644 index 0000000000000000000000000000000000000000..4e131b84e98990f4e589ca736ea57a09b458378c --- /dev/null +++ b/.project @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>test2021</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + </buildSpec> + <natures> + </natures> +</projectDescription> diff --git a/simeonc-calc/src/main/java/simeonc/Calc.java b/simeonc-calc/src/main/java/simeonc/Calc.java deleted file mode 100644 index eb59b2faf597513f8d806580cc03298090187429..0000000000000000000000000000000000000000 --- a/simeonc-calc/src/main/java/simeonc/Calc.java +++ /dev/null @@ -1,132 +0,0 @@ -package app; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.BinaryOperator; -import java.util.function.UnaryOperator; - -public class Calc { - - private final List<Double> operandStack; - - public Calc(double... operands) { - operandStack = new ArrayList<>(operands.length + 2); - for (var d : operands) { - operandStack.add(d); - } - } - - /** - * @return the number of operands on the stack - */ - public int getOperandCount() { - return operandStack.size(); - } - - /** - * Pushes a new operand onto top of the stack. - * - * @param d the new operand - */ - public void pushOperand(double 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) { - if (n >= getOperandCount()) { - throw new IllegalArgumentException("Cannot peek at position " + n + " when the operand count is " + getOperandCount()); - } - return operandStack.get(getOperandCount() - n - 1); - } - - /** - * @return the top operand - */ - public double peekOperand() { - return peekOperand(0); - } - - /** - * Removes and returns the top operand. - * - * @return the top operand - * @throws IllegalStateException if the stack is empty - */ - public double popOperand() { - 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) throws IllegalStateException { - if (getOperandCount() == 0) { - throw new IllegalStateException(); - } - double n1 = op.apply(peekOperand()); - - popOperand(); - pushOperand(n1); - - return n1; - } - - /** - * Performs the provided operation in the two topmost operands, and - * 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) throws IllegalStateException { - if (getOperandCount() < 2) { - throw new IllegalStateException("Too few operands (" + getOperandCount() + ") on the stack"); - } - var op2 = popOperand(); - var op1 = popOperand(); - var result = op.apply(op1, op2); - pushOperand(result); - return result; - } - - /** - * Swaps the two topmost operands. - * - * @throws IllegalStateException if the operand count is less than two - */ - public void swap() { - if (getOperandCount() < 2) { - throw new IllegalStateException(); - } - double n1 = popOperand(); - operandStack.add(getOperandCount() - 1, n1); - - } - - /** - * Duplicates the top operand. - * - * @throws IllegalStateException if the operand stack is empty - */ - public void dup() { - if (getOperandCount() == 0) { - throw new IllegalStateException(); - } - popOperand(); - } - -} \ No newline at end of file diff --git a/simeonc-calc/src/main/java/simeonc/CalcApp.java b/simeonc-calc/src/main/java/simeonc/CalcApp.java deleted file mode 100644 index cec1706c3a6a8dc4e321f3f91fcad8702a736e93..0000000000000000000000000000000000000000 --- a/simeonc-calc/src/main/java/simeonc/CalcApp.java +++ /dev/null @@ -1,27 +0,0 @@ -package app; - -import javafx.application.Application; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.stage.Stage; - -import java.io.IOException; - -/** - * JavaFX App - */ -public class CalcApp extends Application { - - @Override - public void start(Stage stage) throws IOException { - FXMLLoader fxmlLoader = new FXMLLoader(this.getClass().getResource("Calc.fxml")); - Parent parent = fxmlLoader.load(); - stage.setScene(new Scene(parent)); - stage.show(); - } - - public static void main(String[] args) { - launch(); - } -} \ No newline at end of file diff --git a/simeonc-calc/src/main/java/simeonc/CalcController.java b/simeonc-calc/src/main/java/simeonc/CalcController.java deleted file mode 100644 index 1f589575a4549fb6f1dd10af97cc6056ef37f6da..0000000000000000000000000000000000000000 --- a/simeonc-calc/src/main/java/simeonc/CalcController.java +++ /dev/null @@ -1,159 +0,0 @@ -package app; - -import java.util.List; -import java.util.function.BinaryOperator; -import java.util.function.UnaryOperator; - -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.control.Labeled; -import javafx.scene.control.ListView; - -public class CalcController { - - private Calc calc; - - public CalcController() { - calc = new Calc(0.0, 0.0, 0.0); - } - - public Calc getCalc() { - return calc; - } - - public void setCalc(Calc calc) { - this.calc = calc; - updateOperandsView(); - } - - @FXML - private ListView<Double> operandsView; - - @FXML - private Label operandView; - - @FXML - void initialize() { - updateOperandsView(); - } - - private void updateOperandsView() { - List<Double> operands = operandsView.getItems(); - operands.clear(); - int elementCount = Math.min(calc.getOperandCount(), 3); - for (int i = 0; i < elementCount; i++) { - operands.add(calc.peekOperand(elementCount - i - 1)); - } - } - - private String getOperandString() { - return operandView.getText(); - } - - private boolean hasOperand() { - return ! getOperandString().isBlank(); - } - - private double getOperand() { - return Double.valueOf(operandView.getText()); - } - - private void setOperand(String operandString) { - operandView.setText(operandString); - } - - @FXML - void handleEnter() { - if (hasOperand()) { - calc.pushOperand(getOperand()); - } else { - calc.dup(); - } - setOperand(""); - updateOperandsView(); - } - - private void appendToOperand(String string) { - setOperand(getOperandString() + string); - } - - @FXML - void handleDigit(ActionEvent ae) { - if (ae.getSource() instanceof Labeled l) { - appendToOperand(l.getText()); - } - } - - @FXML - void handlePoint() { - var operandString = getOperandString(); - if (operandString.contains(".")) { - String s = operandString.substring(0, operandString.indexOf(".")) + "."; - setOperand(s); - } else { - appendToOperand("."); - } - } - - @FXML - void handleClear() { - setOperand(""); - } - - @FXML - void handleSwap() { - calc.swap(); - updateOperandsView(); - } - - private void performOperation(UnaryOperator<Double> op) { - calc.performOperation(op); - updateOperandsView(); - } - - private void performOperation(boolean swap, BinaryOperator<Double> op) { - if (hasOperand()) { - handleEnter(); - } - if (swap) { - calc.swap(); - calc.performOperation(op); - updateOperandsView(); - } else { - calc.performOperation(op); - updateOperandsView(); - } - } - - @FXML - void handleOpAdd() { - performOperation(false, (a,b) -> a + b); - } - - @FXML - void handleOpSub() { - performOperation(false, (a,b) -> a - b); - } - - @FXML - void handleOpMult() { - performOperation(false, (a,b) -> a * b); - } - - @FXML - void handleOpDiv() { - performOperation(false, (a,b) -> a / b); - } - - @FXML - void handleOpSqrt() { - performOperation(a -> Math.sqrt(a)); - } - - @FXML - void handlePi() { - calc.pushOperand(Math.PI); - updateOperandsView(); - } -} \ No newline at end of file diff --git a/simeonc-calc/src/main/resources/simeonc/Calc.fxml b/simeonc-calc/src/main/resources/simeonc/Calc.fxml deleted file mode 100644 index 42b3b8288b33290cc97b52630421b761a235223c..0000000000000000000000000000000000000000 --- a/simeonc-calc/src/main/resources/simeonc/Calc.fxml +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<?import javafx.scene.layout.GridPane?> -<?import javafx.scene.control.Label?> -<?import javafx.scene.control.Button?> -<?import javafx.scene.control.ListView?> - -<GridPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.CalcController" - alignment="CENTER" hgap="10.0" vgap="10.0" > - - <ListView fx:id="operandsView" prefHeight="80.0" - GridPane.rowIndex="0" GridPane.columnIndex="0" GridPane.columnSpan="4"/> - <Label text="" fx:id="operandView" - GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.columnSpan="4"/> - - <!-- multi-line button label with XML entity for newline --> - <Button text="E n t e r" onAction="#handleEnter" - GridPane.rowIndex="2" GridPane.columnIndex="3" GridPane.rowSpan="3"/> - - <Button text="7" onAction="#handleDigit" - GridPane.rowIndex="2" GridPane.columnIndex="0"/> - <Button text="8" onAction="#handleDigit" - 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"/> - <Button text="-" onAction="#handleOpSub" - GridPane.rowIndex="6" GridPane.columnIndex="1"/> - <Button text="*" onAction="#handleOpMult" - GridPane.rowIndex="6" GridPane.columnIndex="2"/> - - <!-- TODO --> - <Button text="/" onAction="#handleOpDiv" - GridPane.rowIndex="6" GridPane.columnIndex="3"/> - <Button text="√" onAction="#handleOpSqrt" - GridPane.rowIndex="7" GridPane.columnIndex="0"/> - <Button text="π" onAction="#handlePi" - GridPane.rowIndex="7" GridPane.columnIndex="1"/> -</GridPane> diff --git a/simeonc-calc/src/test/java/simeonc/AppTest.java b/simeonc-calc/src/test/java/simeonc/AppTest.java deleted file mode 100644 index 73f81004dad8327a31ec23d861aeea9777ec39fb..0000000000000000000000000000000000000000 --- a/simeonc-calc/src/test/java/simeonc/AppTest.java +++ /dev/null @@ -1,130 +0,0 @@ -package app; - -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.control.ListView; -import javafx.stage.Stage; - -import java.io.IOException; -import java.util.List; -import java.util.stream.Stream; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.testfx.framework.junit5.ApplicationTest; -import org.testfx.matcher.control.LabeledMatchers; - -/** - * TestFX App test - */ -public class AppTest extends ApplicationTest { - - private CalcController controller; - private Parent root; - - @Override - public void start(Stage stage) throws IOException { - FXMLLoader fxmlLoader = new FXMLLoader(this.getClass().getResource("Calc.fxml")); - root = fxmlLoader.load(); - controller = fxmlLoader.getController(); - stage.setScene(new Scene(root)); - stage.show(); - } - - public Parent getRootNode() { - return root; - } - - private String enterLabel = """ - E - n - t - e - r - """.stripTrailing(); - - private void click(String... labels) { - for (var label : labels) { - clickOn(LabeledMatchers.hasText(label)); - } - } - - private String getOperandString() { - return ((Label) getRootNode().lookup("#operandView")).getText(); - } - - private ListView<Double> getOperandsView() { - return (ListView<Double>) getRootNode().lookup("#operandsView"); - } - - private void checkView(double... operands) { - for (int i = 0; i < operands.length; i++) { - Assertions.assertEquals(operands[i], controller.getCalc().peekOperand(i), "Wrong value at #" + i + " of operand stack"); - } - List<Double> viewItems = getOperandsView().getItems(); - for (int i = 0; i < operands.length; i++) { - Assertions.assertEquals(operands[i], viewItems.get(viewItems.size() - i - 1), "Wrong value at #" + i + " of operands view"); - } - } - - private void checkView(String operandString, double... operands) { - Assertions.assertEquals(operandString, getOperandString()); - checkView(operands); - } - - // see https://www.baeldung.com/parameterized-tests-junit-5 - // about @ParameterizedTest - - @ParameterizedTest - @MethodSource - public void testClicksOperand(String labels, String operandString) { - for (var label : labels.split(" ")) { - click(label); - } - checkView(operandString); - } - - private static Stream<Arguments> testClicksOperand() { - return Stream.of( - Arguments.of("2 7", "27"), - Arguments.of("2 7 .", "27."), - Arguments.of("2 7 . 5", "27.5"), - Arguments.of("2 7 . 5 .", "27.") - ); - } - - @ParameterizedTest - @MethodSource - public void testClicksOperands(String labels, String operandsString) { - for (var label : labels.split(" ")) { - click(label.equals("\n") ? enterLabel : label); - } - checkView("", Stream.of(operandsString.split(" ")).mapToDouble(Double::valueOf).toArray()); - } - - private static Stream<Arguments> testClicksOperands() { - return Stream.of( - Arguments.of("2 7 . 5 \n", "27.5"), - Arguments.of("2 7 \n", "27.0"), - Arguments.of("2 \n 7 \n 5 \n", "5.0", "7.0", "2.0"), - Arguments.of("2 7 . \n", "27.0"), - Arguments.of("2 7 . 5 \n", "27.5"), - Arguments.of("2 \n 7 +", "9.0"), - Arguments.of("2 \n 7 -", "-5.0"), - Arguments.of("2 \n 7 *", "14.0"), - Arguments.of("6 \n 3 /", "2.0"), - Arguments.of("2 5 \n √", "5.0") - ); - } - - @Test - public void testPi() { - click("π"); - checkView("", Math.PI); - } -} diff --git a/simeonc-calc/src/test/java/simeonc/CalcTest.java b/simeonc-calc/src/test/java/simeonc/CalcTest.java deleted file mode 100644 index 150778d7a4ae829cbe155aeff824f279b2192294..0000000000000000000000000000000000000000 --- a/simeonc-calc/src/test/java/simeonc/CalcTest.java +++ /dev/null @@ -1,115 +0,0 @@ -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 testPeekOperand() { - Calc calc = new Calc(1.0, 3.14); - Assertions.assertEquals(3.14, calc.peekOperand()); - Assertions.assertThrows(IllegalArgumentException.class, () -> new Calc().peekOperand()); - } - - @Test - public void testPeekOperandN() { - Calc calc = new Calc(1.0, 3.14); - Assertions.assertEquals(3.14, calc.peekOperand(0)); - Assertions.assertEquals(1.0, calc.peekOperand(1)); - Assertions.assertThrows(IllegalArgumentException.class, () -> calc.peekOperand(2)); - } - - @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); - } - - @Test - public void testPopOperand_emptyStack() { - Assertions.assertThrows(IllegalStateException.class, () -> new Calc().popOperand()); - } - - @Test - public void testPerformOperation1() { - Calc calc = new Calc(1.0); - Assertions.assertEquals(-1.0, calc.performOperation(n -> -n)); - checkCalc(calc, -1.0); - } - - @Test - public void testPerformOperation1_emptyOperandStack() { - Assertions.assertThrows(IllegalStateException.class, () -> new Calc().performOperation(n -> -n)); - } - - - @Test - public void testPerformOperation2() { - Calc calc = new Calc(1.0, 3.0); - Assertions.assertEquals(-2.0, calc.performOperation((n1, n2) -> n1 - n2)); - checkCalc(calc, -2.0); - } - - @Test - public void testPerformOperation2_lessThanTwoOperands() { - Assertions.assertThrows(IllegalStateException.class, () -> new Calc(1.0).performOperation((n1, n2) -> n1 - n2)); - Assertions.assertThrows(IllegalStateException.class, () -> new Calc().performOperation((n1, n2) -> n1 - n2)); - } - - @Test - public void testSwap() { - Calc calc = new Calc(1.0, 3.14); - checkCalc(calc, 3.14, 1.0); - calc.swap(); - checkCalc(calc, 1.0, 3.14); - calc.swap(); - checkCalc(calc, 3.14, 1.0); - } - - @Test - public void testSwap_lessThanTwoOperands() { - Assertions.assertThrows(IllegalStateException.class, () -> new Calc(1.0).swap()); - Assertions.assertThrows(IllegalStateException.class, () -> new Calc().swap()); - } - - @Test - public void testDup() { - 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); - } - - @Test - public void testDup_emptyOperandStack() { - Assertions.assertThrows(IllegalStateException.class, () -> new Calc().dup()); - } -} diff --git a/simeonc-calc/src/test/java/simeonc/README.md b/simeonc-calc/src/test/java/simeonc/README.md deleted file mode 100644 index 0cc59802a04720e2dfddaa5c14c029328b91ef33..0000000000000000000000000000000000000000 --- a/simeonc-calc/src/test/java/simeonc/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Tests for the RPN calculator - -This folder/package contains tests based on TestFX for the RPN Calculator (currently only one test class). - -As can be seen when launching, the app contains a list (top) showing the operands -(topmost operand at the bottom), a text field (below list, initially empty) for a new operand and -the buttons for digits, enter, decimal point, operations etc. - -## What is tested - -The tests simulate clicks on the buttons and checks that the underlying Calc object, -the list (a view of the Calc object's operand stack) and the text field are updated as expected. -E.g. if you click buttons `2 3 . 5` the string `23.5` should be shown, -while the list is not affected. If you then click `enter`, the text field should be emptied, the operand stack should have `23.5` at the top and the list should have `23.5` at the bottom -(logically the top of the operand stack). - -Below are the specific cases that are tested. - -buttons to click `=>` text field content: - -- `2 7` => `27` -- `2 7 .` => `27.` -- `2 7 . 5` => `27.5` -- `2 7 . 5 .` => `27.` (cut at decimal point) - -buttons to click `=>` operand stack/list content (from the bottom): - -- `2 7 . 5 enter"` => `27.5` -- `2 7 enter` => `27.0"` -- `2 enter 7 enter 5 enter` => `5.0 7.0 2.0` -- `2 7 . enter` => `27.0` -- `2 7 . 5 enter` => `27.5` -- `2 enter 7 +` => `9.0` -- `2 enter 7 -` => `-5.0` -- `2 enter 7 *` => `14.0` -- `6 enter 3 /` => `2.0` -- `2 5 enter √` => `5.0` -- `π` => `3.1415...` (the value of the `Math.PI` constant)