Skip to content

Commit 3e81723

Browse files
committed
Merge branch 'main' into fix-processing-plugin-test
2 parents aff6b7c + 7b8f4a2 commit 3e81723

8 files changed

Lines changed: 567 additions & 11 deletions

File tree

app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,12 @@ tasks.register<Copy>("includeJavaMode") {
430430
from(java.configurations.runtimeClasspath)
431431
into(composeResources("modes/java/mode"))
432432
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
433-
dirPermissions { unix("rwx------") }
434433
}
435434
tasks.register<Copy>("includeJdk") {
436435
from(Jvm.current().javaHome.absolutePath)
437436
destinationDir = composeResources("jdk").get().asFile
438437

438+
dirPermissions { unix("rwx------") }
439439
fileTree(destinationDir).files.forEach { file ->
440440
file.setWritable(true, false)
441441
file.setReadable(true, false)

app/src/processing/app/ui/theme/Window.kt

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package processing.app.ui.theme
22

33
import androidx.compose.foundation.background
4-
import androidx.compose.foundation.layout.*
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.fillMaxHeight
6+
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.foundation.layout.fillMaxWidth
58
import androidx.compose.material3.Text
6-
import androidx.compose.runtime.Composable
7-
import androidx.compose.runtime.CompositionLocalProvider
8-
import androidx.compose.runtime.DisposableEffect
9-
import androidx.compose.runtime.compositionLocalOf
10-
import androidx.compose.runtime.remember
9+
import androidx.compose.runtime.*
1110
import androidx.compose.ui.Alignment
1211
import androidx.compose.ui.Modifier
1312
import androidx.compose.ui.awt.ComposeWindow
@@ -21,9 +20,7 @@ import androidx.compose.ui.window.rememberWindowState
2120
import com.formdev.flatlaf.util.SystemInfo
2221
import processing.app.ui.Toolkit
2322
import java.awt.Dimension
24-
2523
import javax.swing.JFrame
26-
import javax.swing.JRootPane
2724
import kotlin.reflect.KClass
2825

2926
val LocalWindow = compositionLocalOf<JFrame> { error("No Window Set") }
@@ -120,7 +117,7 @@ private fun PDEWindowContent(
120117
window.rootPane.putClientProperty("apple.awt.transparentTitleBar", mac && fullWindowContent)
121118
Toolkit.setIcon(window)
122119
}
123-
if(unique != null && windows.contains(unique) && windows[unique] != null){
120+
if (unique != null && windows.contains(unique) && windows[unique] != null && windows[unique] != window) {
124121
windows[unique]?.toFront()
125122
window.dispose()
126123
return
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
package processing.app.ui
2+
3+
import androidx.compose.ui.test.*
4+
import org.junit.jupiter.api.Test
5+
import org.mockito.kotlin.mock
6+
import org.mockito.kotlin.verify
7+
import org.mockito.kotlin.verifyNoInteractions
8+
import processing.app.Base
9+
import processing.app.ui.theme.PDETheme
10+
import processing.app.api.Sketch.Companion.Sketch
11+
12+
@OptIn(ExperimentalTestApi::class)
13+
class PDEWelcomeTest {
14+
15+
// Critical Function Tests
16+
17+
@Test
18+
fun testWelcomeScreenRendersWithoutBase() = runComposeUiTest {
19+
setContent {
20+
PDETheme { PDEWelcome(base = null) }
21+
}
22+
waitForIdle()
23+
}
24+
25+
@Test
26+
fun testWelcomeScreenRendersWithBase() = runComposeUiTest {
27+
val base: Base = mock()
28+
setContent {
29+
PDETheme { PDEWelcome(base = base) }
30+
}
31+
waitForIdle()
32+
}
33+
34+
35+
// Action button visibility
36+
37+
@Test
38+
fun testNewSketchButtonIsDisplayed() = runComposeUiTest {
39+
setContent { PDETheme { PDEWelcome(base = mock()) } }
40+
onNodeWithText(Labels.NEW_SKETCH, substring = true).assertIsDisplayed()
41+
}
42+
43+
@Test
44+
fun testSketchbookButtonIsDisplayed() = runComposeUiTest {
45+
setContent { PDETheme { PDEWelcome(base = mock()) } }
46+
onNodeWithText(Labels.SKETCHBOOK, substring = true).assertIsDisplayed()
47+
}
48+
49+
@Test
50+
fun testExamplesButtonIsDisplayed() = runComposeUiTest {
51+
setContent { PDETheme { PDEWelcome(base = mock()) } }
52+
onNodeWithText(Labels.EXAMPLES, substring = true).assertIsDisplayed()
53+
}
54+
55+
// Action button clicks
56+
57+
@Test
58+
fun testNewSketchButtonCallsHandleNew() = runComposeUiTest {
59+
val base: Base = mock()
60+
setContent { PDETheme { PDEWelcome(base = base) } }
61+
onNodeWithText(Labels.NEW_SKETCH, substring = true).performClick()
62+
verify(base).handleNew()
63+
}
64+
65+
@Test
66+
fun testSketchbookButtonCallsShowSketchbookFrame() = runComposeUiTest {
67+
val base: Base = mock()
68+
setContent { PDETheme { PDEWelcome(base = base) } }
69+
onNodeWithText(Labels.SKETCHBOOK, substring = true).performClick()
70+
verify(base).showSketchbookFrame()
71+
}
72+
73+
@Test
74+
fun debugSemanticTree() = runComposeUiTest {
75+
setContent { PDETheme { PDEWelcome(base = mock()) } }
76+
waitForIdle()
77+
onRoot().printToLog("PDEWelcome")
78+
}
79+
80+
@Test
81+
fun testExamplesButtonCallsShowExamplesFrame() = runComposeUiTest {
82+
val base: Base = mock()
83+
setContent { PDETheme { PDEWelcome(base = base) } }
84+
onNodeWithText(Labels.EXAMPLES, substring = true).performClick()
85+
verify(base).showExamplesFrame()
86+
}
87+
88+
// Null-base safety
89+
90+
@Test
91+
fun testNewSketchWithNullBaseDoesNotCrash() = runComposeUiTest {
92+
setContent { PDETheme { PDEWelcome(base = null) } }
93+
onNodeWithText(Labels.NEW_SKETCH, substring = true).performClick()
94+
waitForIdle()
95+
}
96+
97+
@Test
98+
fun testSketchbookWithNullBaseDoesNotCrash() = runComposeUiTest {
99+
setContent { PDETheme { PDEWelcome(base = null) } }
100+
onNodeWithText(Labels.SKETCHBOOK, substring = true).performClick()
101+
waitForIdle()
102+
}
103+
104+
@Test
105+
fun testExamplesWithNullBaseDoesNotCrash() = runComposeUiTest {
106+
setContent { PDETheme { PDEWelcome(base = null) } }
107+
onNodeWithText(Labels.EXAMPLES, substring = true).performClick()
108+
waitForIdle()
109+
}
110+
111+
@Test
112+
fun testNoBaseMethodsCalledWhenBaseIsNull() = runComposeUiTest {
113+
val base: Base = mock()
114+
setContent { PDETheme { PDEWelcome(base = null) } }
115+
onNodeWithText(Labels.NEW_SKETCH, substring = true).performClick()
116+
onNodeWithText(Labels.SKETCHBOOK, substring = true).performClick()
117+
onNodeWithText(Labels.EXAMPLES, substring = true).performClick()
118+
verifyNoInteractions(base)
119+
}
120+
121+
// Show on startup checkbox
122+
123+
@Test
124+
fun testShowOnStartupCheckboxIsDisplayed() = runComposeUiTest {
125+
setContent { PDETheme { PDEWelcome(base = mock()) } }
126+
onNodeWithText(Labels.SHOW_ON_STARTUP, substring = true).assertIsDisplayed()
127+
}
128+
129+
@Test
130+
fun testShowOnStartupCheckboxTogglesPreference() = runComposeUiTest {
131+
setContent { PDETheme { PDEWelcome(base = mock()) } }
132+
onNodeWithText(Labels.SHOW_ON_STARTUP, substring = true).performClick()
133+
waitForIdle()
134+
// Row must still be present after toggling
135+
onNodeWithText(Labels.SHOW_ON_STARTUP, substring = true).assertIsDisplayed()
136+
}
137+
138+
// Resource & community links
139+
140+
@Test
141+
fun testGetStartedLinkIsDisplayed() = runComposeUiTest {
142+
setContent { PDETheme { PDEWelcome(base = mock()) } }
143+
onNodeWithText(Labels.GET_STARTED, substring = true).assertIsDisplayed()
144+
}
145+
146+
@Test
147+
fun testTutorialsLinkIsDisplayed() = runComposeUiTest {
148+
setContent { PDETheme { PDEWelcome(base = mock()) } }
149+
onNodeWithText(Labels.TUTORIALS, substring = true).assertIsDisplayed()
150+
}
151+
152+
@Test
153+
fun testDocumentationLinkIsDisplayed() = runComposeUiTest {
154+
setContent { PDETheme { PDEWelcome(base = mock()) } }
155+
onNodeWithText(Labels.DOCUMENTATION, substring = true).assertIsDisplayed()
156+
}
157+
158+
@Test
159+
fun testForumLinkIsDisplayed() = runComposeUiTest {
160+
setContent { PDETheme { PDEWelcome(base = mock()) } }
161+
onNodeWithText(Labels.FORUM, substring = true).assertIsDisplayed()
162+
}
163+
164+
@Test
165+
fun testDiscordLinkIsDisplayed() = runComposeUiTest {
166+
setContent { PDETheme { PDEWelcome(base = mock()) } }
167+
onNodeWithText("Discord", substring = true).assertIsDisplayed()
168+
}
169+
170+
@Test
171+
fun testGithubLinkIsDisplayed() = runComposeUiTest {
172+
setContent { PDETheme { PDEWelcome(base = mock()) } }
173+
onNodeWithText("GitHub", substring = true).assertIsDisplayed()
174+
}
175+
176+
@Test
177+
fun testInstagramLinkIsDisplayed() = runComposeUiTest {
178+
setContent { PDETheme { PDEWelcome(base = mock()) } }
179+
onNodeWithText("Instagram", substring = true).assertIsDisplayed()
180+
}
181+
182+
// Examples list
183+
184+
@Test
185+
fun testExamplesListIsDisplayed() = runComposeUiTest {
186+
setContent { PDETheme { PDEWelcome(base = mock()) } }
187+
waitForIdle()
188+
onAllNodesWithText(Labels.OPEN_SKETCH, substring = true)
189+
.onFirst()
190+
.assertExists()
191+
}
192+
193+
@Test
194+
fun testExamplesListFallsBackToDefaultsWhenNoSketches() = runComposeUiTest {
195+
// When listAllExamples() yields nothing, PDEWelcome falls back to the
196+
// 4 hard-coded sketches. Either way at least one card must exist.
197+
setContent { PDETheme { PDEWelcome(base = mock()) } }
198+
waitForIdle()
199+
onAllNodesWithText(Labels.OPEN_SKETCH, substring = true)
200+
.onFirst()
201+
.assertExists()
202+
}
203+
204+
@Test
205+
fun testSketchCardOpenButtonTriggersCallback() = runComposeUiTest {
206+
var opened = false
207+
setContent {
208+
PDETheme {
209+
val sketch = Sketch(path = "/tmp", name = "test")
210+
sketch.card(onOpen = { opened = true })
211+
}
212+
}
213+
// Hover to reveal the overlay
214+
onRoot().performMouseInput { moveTo(center) }
215+
waitForIdle()
216+
onNodeWithText(Labels.OPEN_SKETCH, substring = true).performClick()
217+
assert(opened)
218+
}
219+
220+
@Test
221+
fun testSketchCardHoverRevealsBanner() = runComposeUiTest {
222+
setContent {
223+
PDETheme {
224+
val sketch = Sketch(path = "/tmp", name = "MySketch")
225+
sketch.card()
226+
}
227+
}
228+
onRoot().performMouseInput { moveTo(center) }
229+
waitForIdle()
230+
onNodeWithText("MySketch", substring = true).assertIsDisplayed()
231+
}
232+
233+
@Test
234+
fun testPDEWelcomeWithSurveyRendersWithoutCrash() = runComposeUiTest {
235+
setContent { PDETheme { PDEWelcomeWithSurvey(base = mock()) } }
236+
waitForIdle()
237+
}
238+
239+
@Test
240+
fun testPDEWelcomeWithSurveyRendersWithNullBase() = runComposeUiTest {
241+
setContent { PDETheme { PDEWelcomeWithSurvey(base = null) } }
242+
waitForIdle()
243+
}
244+
245+
// Label constants. Update if anything is changed
246+
247+
private object Labels {
248+
const val NEW_SKETCH = "New Sketch"
249+
const val SKETCHBOOK = "My Sketches" // was "Sketchbook"
250+
const val EXAMPLES = "Open Examples" // was "Examples"
251+
const val SHOW_ON_STARTUP = "Show this window at startup" // was "Show on startup"
252+
const val GET_STARTED = "Get Started"
253+
const val TUTORIALS = "Tutorials"
254+
const val DOCUMENTATION = "Reference" // was "Documentation"
255+
const val FORUM = "Forum"
256+
const val OPEN_SKETCH = "Open"
257+
}
258+
}

core/src/processing/data/IntList.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.Iterator;
77
import java.util.Random;
88

9+
import org.jetbrains.annotations.TestOnly;
910
import processing.core.PApplet;
1011

1112

@@ -164,13 +165,14 @@ public void clear() {
164165
* @webBrief Get an entry at a particular index
165166
*/
166167
public int get(int index) {
167-
if (index >= this.count) {
168+
if (index >= this.count || index < 0) {
168169
throw new ArrayIndexOutOfBoundsException(index);
169170
}
170171
return data[index];
171172
}
172173

173174

175+
174176
/**
175177
* Set the entry at a particular index.
176178
*
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package processing.core;
2+
3+
import java.awt.Font; //Javas built in font class
4+
import org.junit.Test; //Using Junit4 test implementation
5+
import static org.junit.Assert.assertEquals; //To compare expected vs actual values
6+
7+
public class PFontTest {
8+
9+
@Test
10+
public void testGetFontNameCorrectly() {
11+
Font trueFont = new Font("Name", Font.PLAIN, 16);
12+
PFont font = new PFont(trueFont, true, null);
13+
assertEquals("Name", font.getName());
14+
}
15+
16+
17+
@Test
18+
public void testGetCorrectPSName() {
19+
Font awtFont = new Font("Dialog", Font.PLAIN, 16); //Truth, what PFont size should be
20+
PFont font = new PFont(awtFont, true, null);
21+
assertEquals(awtFont.getPSName(), font.getPostScriptName()); //test, expecting
22+
}
23+
24+
@Test
25+
public void testGetCorrectSize() {
26+
Font awtFont = new Font("Dialog", Font.PLAIN, 16); //Truth, what PFont size should be
27+
PFont font = new PFont(awtFont, true, null);
28+
assertEquals(awtFont.getSize(), font.getSize()); //test, expecting
29+
}
30+
31+
@Test
32+
public void testGetNative() {
33+
Font awtFont = new Font("Dialog", Font.PLAIN, 16);
34+
PFont font = new PFont(awtFont, true, null);
35+
assertEquals(awtFont, font.getNative());
36+
}
37+
}

0 commit comments

Comments
 (0)