From 7081e78fe3e6875cda7564bc3393e0752eb30381 Mon Sep 17 00:00:00 2001 From: ericahin <129220875+ericahin@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:45:50 -0400 Subject: [PATCH 1/2] added testing for Sort.java --- core/test/processing/data/SortTest.java | 165 ++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 core/test/processing/data/SortTest.java diff --git a/core/test/processing/data/SortTest.java b/core/test/processing/data/SortTest.java new file mode 100644 index 0000000000..7dab624f68 --- /dev/null +++ b/core/test/processing/data/SortTest.java @@ -0,0 +1,165 @@ +package processing.data; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Sort.java is an abstract class implementing quicksort with three abstract methods: + * 1. size() - returns the number of elements + * 2. compare(int a, int b) - compares elements at two indices + * 3. swap(int a, int b) - swaps elements at two indices + */ +public class SortTest { + + /** + * Concrete implementation of Sort for testing using an int array. + */ + private static class IntArraySort extends Sort { + int[] data; + + IntArraySort(int[] data) { + this.data = data; + } + + @Override + public int size() { + return data.length; + } + + @Override + public int compare(int a, int b) { + return Integer.compare(data[a], data[b]); + } + + @Override + public void swap(int a, int b) { + int temp = data[a]; + data[a] = data[b]; + data[b] = temp; + } + } + + @Test + public void testSortAlreadySorted() { + int[] data = {1, 2, 3, 4, 5}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{1, 2, 3, 4, 5}, data); + } + + @Test + public void testSortReversed() { + int[] data = {5, 4, 3, 2, 1}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{1, 2, 3, 4, 5}, data); + } + + @Test + public void testSortUnsorted() { + int[] data = {3, 1, 4, 1, 5, 9, 2, 6}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{1, 1, 2, 3, 4, 5, 6, 9}, data); + } + + @Test + public void testSortSingleElement() { + int[] data = {42}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{42}, data); + } + + @Test + public void testSortEmptyArray() { + int[] data = {}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{}, data); + } + + @Test + public void testSortTwoElements() { + int[] data = {2, 1}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{1, 2}, data); + } + + @Test + public void testSortTwoElementsAlreadySorted() { + int[] data = {1, 2}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{1, 2}, data); + } + + @Test + public void testSortWithDuplicates() { + int[] data = {3, 3, 3, 3}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{3, 3, 3, 3}, data); + } + + @Test + public void testSortWithNegativeNumbers() { + int[] data = {0, -3, 5, -1, 2}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{-3, -1, 0, 2, 5}, data); + } + + @Test + public void testSortWithMixedDuplicatesAndNegatives() { + int[] data = {4, -2, 4, 0, -2}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{-2, -2, 0, 4, 4}, data); + } + + @Test + public void testSizeReflectsArrayLength() { + int[] data = {10, 20, 30}; + IntArraySort sorter = new IntArraySort(data); + assertEquals(3, sorter.size()); + } + + @Test + public void testSwapExchangesElements() { + int[] data = {10, 20, 30}; + IntArraySort sorter = new IntArraySort(data); + sorter.swap(0, 2); + assertArrayEquals(new int[]{30, 20, 10}, data); + } + + @Test + public void testCompareReturnsNegativeWhenLess() { + int[] data = {1, 5}; + IntArraySort sorter = new IntArraySort(data); + assertTrue(sorter.compare(0, 1) < 0); + } + + @Test + public void testCompareReturnsPositiveWhenGreater() { + int[] data = {5, 1}; + IntArraySort sorter = new IntArraySort(data); + assertTrue(sorter.compare(0, 1) > 0); + } + + @Test + public void testCompareReturnsZeroWhenEqual() { + int[] data = {3, 3}; + IntArraySort sorter = new IntArraySort(data); + assertEquals(0, sorter.compare(0, 1)); + } + + @Test + public void testSortDescendingLargeArray() { + int[] data = {6, 5, 4, 3, 2, 1}; + IntArraySort sorter = new IntArraySort(data); + sorter.run(); + assertArrayEquals(new int[]{1, 2, 3, 4, 5, 6}, data); + } +} \ No newline at end of file From 8ec9cbb5f015629c4e46cf3bb4924feb1e88f673 Mon Sep 17 00:00:00 2001 From: ericahin <129220875+ericahin@users.noreply.github.com> Date: Wed, 22 Apr 2026 15:33:09 -0400 Subject: [PATCH 2/2] added tests for PDEWelcome --- app/test/processing/app/PDEWelcomeTest.kt | 258 ++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 app/test/processing/app/PDEWelcomeTest.kt diff --git a/app/test/processing/app/PDEWelcomeTest.kt b/app/test/processing/app/PDEWelcomeTest.kt new file mode 100644 index 0000000000..d09b0f6fa7 --- /dev/null +++ b/app/test/processing/app/PDEWelcomeTest.kt @@ -0,0 +1,258 @@ +package processing.app.ui + +import androidx.compose.ui.test.* +import org.junit.jupiter.api.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoInteractions +import processing.app.Base +import processing.app.ui.theme.PDETheme +import processing.app.api.Sketch.Companion.Sketch + +@OptIn(ExperimentalTestApi::class) +class PDEWelcomeTest { + + // Critical Function Tests + + @Test + fun testWelcomeScreenRendersWithoutBase() = runComposeUiTest { + setContent { + PDETheme { PDEWelcome(base = null) } + } + waitForIdle() + } + + @Test + fun testWelcomeScreenRendersWithBase() = runComposeUiTest { + val base: Base = mock() + setContent { + PDETheme { PDEWelcome(base = base) } + } + waitForIdle() + } + + + // Action button visibility + + @Test + fun testNewSketchButtonIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText(Labels.NEW_SKETCH, substring = true).assertIsDisplayed() + } + + @Test + fun testSketchbookButtonIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText(Labels.SKETCHBOOK, substring = true).assertIsDisplayed() + } + + @Test + fun testExamplesButtonIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText(Labels.EXAMPLES, substring = true).assertIsDisplayed() + } + + // Action button clicks + + @Test + fun testNewSketchButtonCallsHandleNew() = runComposeUiTest { + val base: Base = mock() + setContent { PDETheme { PDEWelcome(base = base) } } + onNodeWithText(Labels.NEW_SKETCH, substring = true).performClick() + verify(base).handleNew() + } + + @Test + fun testSketchbookButtonCallsShowSketchbookFrame() = runComposeUiTest { + val base: Base = mock() + setContent { PDETheme { PDEWelcome(base = base) } } + onNodeWithText(Labels.SKETCHBOOK, substring = true).performClick() + verify(base).showSketchbookFrame() + } + + @Test + fun debugSemanticTree() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + waitForIdle() + onRoot().printToLog("PDEWelcome") + } + + @Test + fun testExamplesButtonCallsShowExamplesFrame() = runComposeUiTest { + val base: Base = mock() + setContent { PDETheme { PDEWelcome(base = base) } } + onNodeWithText(Labels.EXAMPLES, substring = true).performClick() + verify(base).showExamplesFrame() + } + + // Null-base safety + + @Test + fun testNewSketchWithNullBaseDoesNotCrash() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = null) } } + onNodeWithText(Labels.NEW_SKETCH, substring = true).performClick() + waitForIdle() + } + + @Test + fun testSketchbookWithNullBaseDoesNotCrash() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = null) } } + onNodeWithText(Labels.SKETCHBOOK, substring = true).performClick() + waitForIdle() + } + + @Test + fun testExamplesWithNullBaseDoesNotCrash() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = null) } } + onNodeWithText(Labels.EXAMPLES, substring = true).performClick() + waitForIdle() + } + + @Test + fun testNoBaseMethodsCalledWhenBaseIsNull() = runComposeUiTest { + val base: Base = mock() + setContent { PDETheme { PDEWelcome(base = null) } } + onNodeWithText(Labels.NEW_SKETCH, substring = true).performClick() + onNodeWithText(Labels.SKETCHBOOK, substring = true).performClick() + onNodeWithText(Labels.EXAMPLES, substring = true).performClick() + verifyNoInteractions(base) + } + + // Show on startup checkbox + + @Test + fun testShowOnStartupCheckboxIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText(Labels.SHOW_ON_STARTUP, substring = true).assertIsDisplayed() + } + + @Test + fun testShowOnStartupCheckboxTogglesPreference() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText(Labels.SHOW_ON_STARTUP, substring = true).performClick() + waitForIdle() + // Row must still be present after toggling + onNodeWithText(Labels.SHOW_ON_STARTUP, substring = true).assertIsDisplayed() + } + + // Resource & community links + + @Test + fun testGetStartedLinkIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText(Labels.GET_STARTED, substring = true).assertIsDisplayed() + } + + @Test + fun testTutorialsLinkIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText(Labels.TUTORIALS, substring = true).assertIsDisplayed() + } + + @Test + fun testDocumentationLinkIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText(Labels.DOCUMENTATION, substring = true).assertIsDisplayed() + } + + @Test + fun testForumLinkIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText(Labels.FORUM, substring = true).assertIsDisplayed() + } + + @Test + fun testDiscordLinkIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText("Discord", substring = true).assertIsDisplayed() + } + + @Test + fun testGithubLinkIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText("GitHub", substring = true).assertIsDisplayed() + } + + @Test + fun testInstagramLinkIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + onNodeWithText("Instagram", substring = true).assertIsDisplayed() + } + + // Examples list + + @Test + fun testExamplesListIsDisplayed() = runComposeUiTest { + setContent { PDETheme { PDEWelcome(base = mock()) } } + waitForIdle() + onAllNodesWithText(Labels.OPEN_SKETCH, substring = true) + .onFirst() + .assertExists() + } + + @Test + fun testExamplesListFallsBackToDefaultsWhenNoSketches() = runComposeUiTest { + // When listAllExamples() yields nothing, PDEWelcome falls back to the + // 4 hard-coded sketches. Either way at least one card must exist. + setContent { PDETheme { PDEWelcome(base = mock()) } } + waitForIdle() + onAllNodesWithText(Labels.OPEN_SKETCH, substring = true) + .onFirst() + .assertExists() + } + + @Test + fun testSketchCardOpenButtonTriggersCallback() = runComposeUiTest { + var opened = false + setContent { + PDETheme { + val sketch = Sketch(path = "/tmp", name = "test") + sketch.card(onOpen = { opened = true }) + } + } + // Hover to reveal the overlay + onRoot().performMouseInput { moveTo(center) } + waitForIdle() + onNodeWithText(Labels.OPEN_SKETCH, substring = true).performClick() + assert(opened) + } + + @Test + fun testSketchCardHoverRevealsBanner() = runComposeUiTest { + setContent { + PDETheme { + val sketch = Sketch(path = "/tmp", name = "MySketch") + sketch.card() + } + } + onRoot().performMouseInput { moveTo(center) } + waitForIdle() + onNodeWithText("MySketch", substring = true).assertIsDisplayed() + } + + @Test + fun testPDEWelcomeWithSurveyRendersWithoutCrash() = runComposeUiTest { + setContent { PDETheme { PDEWelcomeWithSurvey(base = mock()) } } + waitForIdle() + } + + @Test + fun testPDEWelcomeWithSurveyRendersWithNullBase() = runComposeUiTest { + setContent { PDETheme { PDEWelcomeWithSurvey(base = null) } } + waitForIdle() + } + + // Label constants. Update if anything is changed + + private object Labels { + const val NEW_SKETCH = "New Sketch" + const val SKETCHBOOK = "My Sketches" // was "Sketchbook" + const val EXAMPLES = "Open Examples" // was "Examples" + const val SHOW_ON_STARTUP = "Show this window at startup" // was "Show on startup" + const val GET_STARTED = "Get Started" + const val TUTORIALS = "Tutorials" + const val DOCUMENTATION = "Reference" // was "Documentation" + const val FORUM = "Forum" + const val OPEN_SKETCH = "Open" + } +}