+ * The returned string follows the same format as the tree's display method, + * where each node is represented in the form "left=>value<=right" and "END" + * denotes an empty (null) child. + * @return a string representing the expected tree structure + */ + String getExpectedTree() { + return "10=>20<=30END=>10<=ENDEND=>30<=END2"; + } + + /** + * Returns the actual string representation of the tree as printed + * by the display method. + * + *
This method captures the console output via the interceptor and
+ * removes all newline characters, returning a single-line string
+ * suitable for comparison with the expected tree string.
+ *
+ * @return the actual tree structure as a single-line string
+ */
+ String getActualTree() {
+ return interceptor.getAndClearConsoleOutput().replaceAll("[\\r\\n]", "");
+ }
+
+ /* ========================
+ Tests
+ ======================== */
+
+ @Test
+ @DisplayName("Creates a longer simple AVL tree that matches the expected layout")
+ void testTreeCreation() {
+ tree.insert(25);
+ tree.insert(30);
+ tree.insert(10);
+ tree.insert(5);
+ tree.insert(15);
+ tree.insert(27);
+ tree.insert(20);
+ tree.insert(19);
+ tree.insert(16);
+
+ tree.display();
+
+ String expectedTree = "15=>25<=3010=>15<=195=>10<=ENDEND=>5<=END16=>19<=20END=>16<=ENDEND=>20<=END27=>30<=ENDEND=>27<=END4";
+
+ assertEquals(expectedTree, getActualTree());
+ }
+
+ @Test
+ @DisplayName("A test where an empty tree should return the string \"Tree is empty\".")
+ void testEmptyTree() {
+ AVLSimple tree = new AVLSimple();
+
+ tree.display();
+
+ assertEquals("Tree is empty", getActualTree());
+ }
+
+ @ParameterizedTest
+ @MethodSource("getTreeNodesValues")
+ @DisplayName("Test to ensure all rotation paths are covered")
+ void testAllRotations(int node1, int node2, int node3, String errorMessage) {
+ assertAll(() -> assertDoesNotThrow(() -> tree.insert(node1), "inserting: " + node1), () -> assertDoesNotThrow(() -> tree.insert(node2), "inserting: " + node2), () -> assertDoesNotThrow(() -> tree.insert(node3), "inserting: " + node3));
+
+ tree.display();
+
+ assertEquals(getExpectedTree(), getActualTree(), errorMessage);
+ }
+
+ public static Stream
+ * This method captures the console output via the interceptor and
+ * removes all newline characters, returning a single-line string
+ * suitable for comparison with the expected tree string.
+ * @return the actual tree structure as a single-line string
+ */
+ String getConsoleOutput() {
+ return interceptor.getAndClearConsoleOutput().replaceAll("[\\r\\n]", "");
+ }
+
+ /* ========================
+ Tests
+ ======================== */
+
+ @Test
+ @DisplayName("Asserts the created tree have the expected structure")
+ void testValidTree() {
+ tree.display();
+
+ Assertions.assertEquals(getExpectedTree(), getConsoleOutput());
+ }
+
+ @Test
+ @DisplayName("Creating GenericTree with invalid input throws InputMismatchException")
+ void testCreateInValidTree() {
+ String invalidTreeValues = "a\nb\nc\n";
+ interceptor.mockInput(invalidTreeValues);
+
+ Assertions.assertThrows(InputMismatchException.class, GenericTree::new);
+ }
+
+ @Test
+ @DisplayName("Gets the correct number of nodes in the tree")
+ void testGettingCorrectSizeOfTree() {
+ Assertions.assertEquals(9, tree.size2call());
+ }
+
+ @Test
+ @DisplayName("Gets the highest value among the trees nodes")
+ void testGettingTheMaximalValueOfTreesNodes() {
+ Assertions.assertEquals(342, tree.maxcall());
+ }
+
+ @Test
+ @DisplayName("Returns the correct height of the tree")
+ void testGettingTheHeightOfTree() {
+ Assertions.assertEquals(2, tree.heightcall());
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {40, 34, 1, 32, 342, 123, 98, 35})
+ @DisplayName("Returns true when searching for values that is in the tree")
+ void testFindingAllPresentValuesInTree(int value) {
+ Assertions.assertTrue(tree.findcall(value), "The expected value " + value + " wasn't in the tree");
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {41, 31, 2, 52, 542, 223, 92, 38})
+ @DisplayName("Returns false when searching for values that isn't in the tree")
+ void testFindingAbsentValuesInTree(int value) {
+ Assertions.assertFalse(tree.findcall(value), "The value " + value + " was unexpectedly in the tree");
+ }
+
+ @Test
+ @DisplayName("Outputs all nodes at each tree level from left to right")
+ void testFindingAllNodesOfCertainDepthInTree() {
+ int height = tree.heightcall();
+
+ for (int i = 0; i <= height; i++) {
+ tree.depthcaller(i);
+ }
+
+ Assertions.assertEquals("4034132342121239835", getConsoleOutput());
+ }
+
+ @Test
+ @DisplayName("Prints all node values in pre order")
+ void testPreOrderPrintsAsExpected() {
+ tree.preordercall();
+ Assertions.assertEquals("40 34 1 32 123 342 98 35 12 .", getConsoleOutput());
+ }
+
+ @Test
+ @DisplayName("Prints all node values in post order")
+ void testPostOrderPrintsAsExpected() {
+ tree.postordercall();
+ Assertions.assertEquals("34 1 123 32 98 35 342 12 40 .", getConsoleOutput());
+ }
+
+ @Test
+ @DisplayName("Prints all node values in level order")
+ void testLevelOrderPrintsAsExpected() {
+ tree.levelorder();
+ Assertions.assertEquals("40 34 1 32 342 12 123 98 35 .", getConsoleOutput());
+ }
+
+ @Test
+ @DisplayName("Removes all leaves from the tree")
+ void testLeavesAreRemoved() {
+ tree.removeleavescall();
+ tree.display();
+
+ Assertions.assertEquals("40=>32 342 .32=>.342=>.", getConsoleOutput());
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/trees/LCATest.java b/src/test/java/com/thealgorithms/datastructures/trees/LCATest.java
new file mode 100644
index 000000000000..7b42d3c61fb2
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/trees/LCATest.java
@@ -0,0 +1,52 @@
+package com.thealgorithms.datastructures.trees;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.thealgorithms.devutils.ConsoleInterceptor;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class LCATest {
+ /**
+ * This input creates the following tree:
+ *
+ * Recommended usage:
+ *
+ * Each line of user input must end with a newline character (
+ * Example input:
+ *
+ * "This is input line one\nAnd this is the second line of input\nAnd so on...\n"
+ */
+ public void mockInput(String mockedInput) {
+ System.setIn(new ByteArrayInputStream(mockedInput.getBytes()));
+ }
+
+ /* ===========================
+ Output
+ =========================== */
+
+ /**
+ * Start capturing System.out by replacing stdout with a custom PrintStream.
+ * All printed data will be stored in outContent - available via {@link #getAndClearConsoleOutput}, for later retrieval.
+ */
+ public void captureOutput() {
+ if (!isCapturing) {
+ System.setOut(new PrintStream(outContent));
+ isCapturing = true;
+ }
+ }
+
+ /**
+ * Get current captured output and clears the buffer.
+ * @return the captured output as a string
+ * @throws IllegalStateException if output hasn't been captured yet
+ */
+ public String getAndClearConsoleOutput() {
+ if (isCapturing && outContent.size() > 0) {
+ String output = outContent.toString();
+ outContent.reset();
+ return output;
+ } else {
+ throw new IllegalStateException("Output hasn't been captured yet.");
+ }
+ }
+
+ /** Clears the output buffer. */
+ public void clearConsoleOutput() {
+ outContent.reset();
+ }
+
+ /* ===========================
+ Input And Output
+ =========================== */
+ /**
+ * {@inheritDoc}
+ *
+ * This override restores the original System.out and System.in streams,
+ * resets the captured output stored in the internal OutputStream,
+ * and sets the {@code isCapturing} flag to {@code false} to indicate
+ * that capturing has stopped and prevent further access to {@code outContent}.
+ */
+ @Override
+ public void close() {
+ System.setOut(originalOut);
+ System.setIn(originalIn);
+ outContent.reset();
+ isCapturing = false;
+ }
+}
diff --git a/src/test/java/com/thealgorithms/maths/MathBuilderTest.java b/src/test/java/com/thealgorithms/maths/MathBuilderTest.java
index dc381bfca5d3..6e1a0ebe4bb4 100644
--- a/src/test/java/com/thealgorithms/maths/MathBuilderTest.java
+++ b/src/test/java/com/thealgorithms/maths/MathBuilderTest.java
@@ -1,28 +1,32 @@
package com.thealgorithms.maths;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
class MathBuilderTest {
@Test
void simpleMath() {
double result = new MathBuilder.Builder(100).add(200).multiply(10).print().divideIf(20, (a, b) -> a % 2 == 0).sqrt().print().ceil().build().get();
- assertEquals(13, result);
+ Assertions.assertEquals(13, result);
}
@Test
void memoryTest() {
long result = new MathBuilder.Builder().set(100).sqrt().remember().add(21).recallIf(a -> a < 50, true).mod(2).build().toLong();
- assertEquals(0, result);
+ Assertions.assertEquals(0, result);
}
@Test
void freeFallDistance() {
long distance = new MathBuilder.Builder(9.81).multiply(0.5).multiply(5 * 5).round().build().toLong();
- assertEquals(123, distance); // Expected result: 0.5 * 9.81 * 25 = 122.625 ≈ 123
+ Assertions.assertEquals(123, distance); // Expected result: 0.5 * 9.81 * 25 = 122.625 ≈ 123
}
@Test
@@ -33,20 +37,208 @@ void batchSalaryProcessing() {
processedSalaries[i] = new MathBuilder.Builder(salaries[i]).addIf(salaries[i] * 0.1, (sal, bonus) -> sal > 2500).multiply(0.92).round().build().toLong();
}
long[] expectedSalaries = {1840, 3036, 4048, 5060};
- assertArrayEquals(expectedSalaries, processedSalaries);
+ Assertions.assertArrayEquals(expectedSalaries, processedSalaries);
}
@Test
void parenthesis() {
// 10 + (20*5) - 40 + (100 / 10) = 80
double result = new MathBuilder.Builder(10).openParenthesis(20).multiply(5).closeParenthesisAndPlus().minus(40).openParenthesis(100).divide(10).closeParenthesisAndPlus().build().get();
- assertEquals(80, result);
+ Assertions.assertEquals(80, result);
}
@Test
void areaOfCircle() {
// Radius is 4
double area = new MathBuilder.Builder().pi().openParenthesis(4).multiply(4).closeParenthesisAndMultiply().build().get();
- assertEquals(Math.PI * 4 * 4, area);
+ Assertions.assertEquals(Math.PI * 4 * 4, area);
+ }
+
+ @Test
+ @DisplayName("Floor Test")
+ void floorTest() {
+ // floor(10.5 + (20+2.1))
+ double actual = new MathBuilder.Builder(10.5).openParenthesis(20).add(2.1).closeParenthesisAndPlus().floor().build().get();
+ double expected = Math.floor(10.5 + 20 + 2.1);
+
+ // 10.5 + floor((20+2.1))
+ double actual2 = new MathBuilder.Builder(10.5).openParenthesis(20).add(2.1).floor().closeParenthesisAndPlus().build().get();
+ double expected2 = 10.5 + Math.floor(20 + 2.1);
+
+ Assertions.assertEquals(expected, actual);
+ Assertions.assertEquals(expected2, actual2);
+ }
+
+ @Test
+ @DisplayName("Close parenthesis Test")
+ void closeParenthesisTest() {
+ // 10.5 - (20+2.1)
+ double actual = new MathBuilder.Builder(10.5).openParenthesis(20).add(2.1).closeParenthesisAndMinus().build().get();
+ double expected = 10.5 - (20 + 2.1);
+
+ // 10.5 / (20+2.1)
+ double actual2 = new MathBuilder.Builder(10.5).openParenthesis(20).add(2.1).closeParenthesisAndDivide().build().get();
+ double expected2 = 10.5 / (20 + 2.1);
+
+ Assertions.assertEquals(expected, actual);
+ Assertions.assertEquals(expected2, actual2);
+ }
+
+ @Test
+ @DisplayName("open parenthesis Test")
+ void openParenthesisAndABSTest() {
+ // 10.5 - (20+2.1)
+ double actual = new MathBuilder.Builder(10.5).openParenthesis(20).minus(2.1).closeParenthesisAndMinus().build().get();
+ double expected = 10.5 - (20 - 2.1);
+
+ // 10.5 / (20+2.1)
+ double actual2 = new MathBuilder.Builder(10.5).openParenthesis(20).add(2.2).abs().closeParenthesisAndPlus().abs().build().get();
+ double expected2 = 10.5 + (20 + 2.2);
+
+ Assertions.assertEquals(expected, actual);
+ Assertions.assertEquals(expected2, actual2);
+ }
+
+ @Test
+ @DisplayName("Runtime Errors Tests")
+ void runtimeErrorTest() {
+ MathBuilder.Builder actual = new MathBuilder.Builder(10.5);
+
+ Executable randCheck = () -> Assertions.assertThrows(RuntimeException.class, () -> actual.rand(1));
+ Executable rangeCheck = () -> Assertions.assertThrows(RuntimeException.class, () -> actual.randomInRange(1, 10));
+ Executable piCheck = () -> Assertions.assertThrows(RuntimeException.class, actual::pi);
+ Executable eCheck = () -> Assertions.assertThrows(RuntimeException.class, actual::e);
+ Executable setCheck = () -> Assertions.assertThrows(RuntimeException.class, () -> actual.set(1));
+
+ Assertions.assertAll(randCheck, rangeCheck, piCheck, eCheck, setCheck);
+ }
+
+ @Test
+ @DisplayName("Should divide 10 by 2")
+ void divideByNum() {
+ double actual = new MathBuilder.Builder(10).divide(2).build().get();
+
+ double expected = 10.0 / 2.0;
+ double expected2 = 10.0 / 4.0;
+
+ Assertions.assertEquals(expected, actual);
+ Assertions.assertNotEquals(expected2, actual);
+ }
+
+ @ParameterizedTest
+ @MethodSource("divideDoubleByZeroHelper")
+ @DisplayName("Test that ensures dividing a double by zero follows IEEE 754")
+ void divideDoubleByZero(double expected, MathBuilder.Builder actual, String error) {
+ Executable noThrowOnBuild = () -> Assertions.assertDoesNotThrow(() -> actual.build().get(), "Dividing a double with zero should not throw");
+ Executable noThrowOnDivideZero = () -> Assertions.assertDoesNotThrow(() -> actual.divide(0).build().get(), "Dividing infinity with 0 should not throw");
+ Executable resultIsInfinite = () -> Assertions.assertTrue(Double.isInfinite(actual.build().get()), "Dividing a double by zero should result in infinity");
+ Executable equalsExpected = () -> Assertions.assertEquals(expected, actual.build().get(), error);
+
+ Assertions.assertAll(noThrowOnBuild, noThrowOnDivideZero, resultIsInfinite, equalsExpected);
+ }
+
+ static List
+ *
+ */
+ @BeforeEach
+ void setup() {
+ String treeValues = "40\n5\n34\n0\n1\n0\n32\n1\n123\n0\n342\n2\n98\n0\n35\n0\n12\n0";
+ interceptor.mockInput(treeValues);
+
+ tree = new GenericTree();
+
+ interceptor.captureOutput();
+ }
+
+ /** Cleans up after each test by closing the interceptor. */
+ @AfterEach
+ void tearDown() {
+ interceptor.close();
+ }
+
+ /* ========================
+ Helper methods
+ ======================== */
+
+ /**
+ * Returns the string representation of the generic tree that's created in the {@link #setup()} method.
+ * It looks something like this:
+ *
+ *
+ * 40
+ * / / | \ \
+ * / / | \ \
+ * 34 1 32 324 12
+ * | | \
+ * 123 98 35
+ *
+ * @return The tree in a string format mimicking what the display() method gives.
+ */
+ String getExpectedTree() {
+ return "40=>34 1 32 342 12 .34=>.1=>.32=>123 .123=>.342=>98 35 .98=>.35=>.12=>.";
+ }
+
+ /**
+ * Returns the actual string representation of the tree as printed
+ * by the display method.
+ *
+ * 0
+ * / \
+ * 1 2
+ * | / \
+ * 5 4 3
+ * | |
+ * 6 7
+ * / \
+ * 9 8
+ *
+ */
+ private static final String TREE = "10\n0\n1\n0\n2\n1\n5\n5\n6\n2\n4\n2\n3\n3\n7\n7\n9\n7\n8\n";
+
+ /* ========================
+ Tests
+ ======================== */
+
+ @ParameterizedTest
+ @MethodSource("getSimulatedInputAndExpectedParent")
+ @DisplayName("Should return correct common ancestor for any two nodes in the tree")
+ void shouldReturnCorrectLCAThroughMain(String simulatedInput, String expectedParent) {
+ try (ConsoleInterceptor interceptor = new ConsoleInterceptor()) {
+ interceptor.mockInput(simulatedInput);
+ interceptor.captureOutput();
+
+ LCA.main(new String[0]);
+
+ String actualParent = interceptor.getAndClearConsoleOutput().replaceAll("[\\r\\n]", "");
+
+ assertEquals(expectedParent, actualParent, "The two nodes Lowest Common Ancestor wasn't the expected one.");
+ }
+ }
+
+ public static Stream
+ *
+ */
+public class ConsoleInterceptor implements AutoCloseable {
+ private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+ /** Saved reference to stdout */
+ private final PrintStream originalOut = System.out;
+ /** Saved reference to stdin */
+ private final InputStream originalIn = System.in;
+ private boolean isCapturing;
+
+ /* ===========================
+ Input
+ =========================== */
+
+ /**
+ * Mock System.in with the provided input.
+ * Used in test, mainly for simulating user input from the keyboard for scanners.
+ * \n),
+ * because {@link java.util.Scanner#nextLine()} and similar methods read input line by line.
+ *