Skip to content

Commit cdf56a2

Browse files
authored
Merge branch 'main' into gradle-beta-indicator
2 parents 936109c + 3d40d09 commit cdf56a2

File tree

5 files changed

+164
-33
lines changed

5 files changed

+164
-33
lines changed

app/build.gradle.kts

+16
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ sourceSets{
3535
srcDirs("resources", listOf("languages", "fonts", "theme").map { "../build/shared/lib/$it" })
3636
}
3737
}
38+
test{
39+
kotlin{
40+
srcDirs("test")
41+
}
42+
}
3843
}
3944

4045
compose.desktop {
@@ -106,6 +111,17 @@ dependencies {
106111
implementation(libs.kaml)
107112
implementation(libs.markdown)
108113
implementation(libs.markdownJVM)
114+
115+
testImplementation(kotlin("test"))
116+
testImplementation(libs.mockitoKotlin)
117+
testImplementation(libs.junitJupiter)
118+
testImplementation(libs.junitJupiterParams)
119+
}
120+
121+
tasks.test {
122+
useJUnitPlatform()
123+
workingDir = file("build/test")
124+
workingDir.mkdirs()
109125
}
110126

111127
tasks.compileJava{

app/src/processing/app/Base.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -1364,10 +1364,10 @@ private File moveLikeSketchFolder(File pdeFile, String baseName) throws IOExcept
13641364
* @param schemeUri the full URI, including pde://
13651365
*/
13661366
public Editor handleScheme(String schemeUri) {
1367-
// var result = Schema.handleSchema(schemeUri, this);
1368-
// if (result != null) {
1369-
// return result;
1370-
// }
1367+
var result = Schema.handleSchema(schemeUri, this);
1368+
if (result != null) {
1369+
return result;
1370+
}
13711371

13721372
String location = schemeUri.substring(6);
13731373
if (location.length() > 0) {

app/src/processing/app/Schema.kt

+26-28
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ class Schema {
5353
private fun handleSketchUrl(uri: URI): Editor?{
5454
val url = File(uri.path.replace("/url/", ""))
5555

56-
val tempSketchFolder = File(Base.untitledFolder, url.nameWithoutExtension)
56+
val rand = (1..6)
57+
.map { (('a'..'z') + ('A'..'Z')).random() }
58+
.joinToString("")
59+
60+
val tempSketchFolder = File(File(Base.untitledFolder, rand), url.nameWithoutExtension)
5761
tempSketchFolder.mkdirs()
5862
val tempSketchFile = File(tempSketchFolder, "${tempSketchFolder.name}.pde")
5963

@@ -71,7 +75,7 @@ class Schema {
7175
?.map { it.split("=") }
7276
?.associate {
7377
URLDecoder.decode(it[0], StandardCharsets.UTF_8) to
74-
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
78+
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
7579
}
7680
?: emptyMap()
7781
options["data"]?.let{ data ->
@@ -81,15 +85,15 @@ class Schema {
8185
downloadFiles(uri, code, File(sketchFolder, "code"))
8286
}
8387
options["pde"]?.let{ pde ->
84-
downloadFiles(uri, pde, sketchFolder)
88+
downloadFiles(uri, pde, sketchFolder, "pde")
8589
}
8690
options["mode"]?.let{ mode ->
8791
val modeFile = File(sketchFolder, "sketch.properties")
8892
modeFile.writeText("mode.id=$mode")
8993
}
9094

9195
}
92-
private fun downloadFiles(uri: URI, urlList: String, targetFolder: File){
96+
private fun downloadFiles(uri: URI, urlList: String, targetFolder: File, extension: String = ""){
9397
Thread{
9498
targetFolder.mkdirs()
9599

@@ -101,37 +105,31 @@ class Schema {
101105
val files = urlList.split(",")
102106

103107
files.filter { it.isNotBlank() }
104-
.map{ it.split(":", limit = 2) }
105-
.map{ segments ->
106-
if(segments.size == 2){
107-
if(segments[0].isBlank()){
108-
return@map listOf(null, segments[1])
109-
}
110-
return@map segments
111-
}
112-
return@map listOf(null, segments[0])
108+
.map {
109+
if (it.contains(":")) it
110+
else "$it:$it"
113111
}
112+
.map{ it.split(":", limit = 2) }
114113
.forEach { (name, content) ->
114+
var target = File(targetFolder, name)
115+
if(extension.isNotBlank() && target.extension != extension){
116+
target = File(targetFolder, "$name.$extension")
117+
}
115118
try{
116-
// Try to decode the content as base64
117119
val file = Base64.getDecoder().decode(content)
118-
if(name == null){
120+
if(name.isBlank()){
119121
Messages.err("Base64 files needs to start with a file name followed by a colon")
120122
return@forEach
121123
}
122-
File(targetFolder, name).writeBytes(file)
124+
target.writeBytes(file)
123125
}catch(_: IllegalArgumentException){
124-
// Assume it's a URL and download it
125-
var url = URI.create(content)
126-
if(url.host == null){
127-
url = URI.create("https://$base/$content")
128-
}
129-
if(url.scheme == null){
130-
url = URI.create("https://$content")
131-
}
132-
133-
val target = File(targetFolder, name ?: url.path.split("/").last())
134-
url.toURL().openStream().use { input ->
126+
val url = URL(when{
127+
content.startsWith("https://") -> content
128+
content.startsWith("http://") -> content.replace("http://", "https://")
129+
URL("https://$content").path.isNotBlank() -> "https://$content"
130+
else -> "https://$base/$content"
131+
})
132+
url.openStream().use { input ->
135133
target.outputStream().use { output ->
136134
input.copyTo(output)
137135
}
@@ -148,7 +146,7 @@ class Schema {
148146
?.map { it.split("=") }
149147
?.associate {
150148
URLDecoder.decode(it[0], StandardCharsets.UTF_8) to
151-
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
149+
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
152150
}
153151
?: emptyMap()
154152
for ((key, value) in options){

app/test/processing/app/SchemaTest.kt

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package processing.app
2+
3+
import org.junit.jupiter.params.ParameterizedTest
4+
import org.junit.jupiter.params.provider.ValueSource
5+
import org.mockito.ArgumentCaptor
6+
import org.mockito.MockedStatic
7+
import org.mockito.Mockito.mockStatic
8+
import org.mockito.kotlin.mock
9+
import org.mockito.kotlin.verify
10+
import java.io.File
11+
import kotlin.io.encoding.Base64
12+
import kotlin.io.encoding.ExperimentalEncodingApi
13+
import kotlin.test.Test
14+
15+
16+
class SchemaTest {
17+
private val base: Base = mock<Base>{
18+
19+
}
20+
companion object {
21+
val preferences: MockedStatic<Preferences> = mockStatic(Preferences::class.java)
22+
}
23+
24+
25+
@Test
26+
fun testLocalFiles() {
27+
val file = "/this/is/a/local/file"
28+
Schema.handleSchema("pde://$file", base)
29+
verify(base).handleOpen(file)
30+
}
31+
32+
@Test
33+
fun testNewSketch() {
34+
Schema.handleSchema("pde://sketch/new", base)
35+
verify(base).handleNew()
36+
}
37+
38+
@OptIn(ExperimentalEncodingApi::class)
39+
@Test
40+
fun testBase64SketchAndExtraFiles() {
41+
val sketch = """
42+
void setup(){
43+
44+
}
45+
void draw(){
46+
47+
}
48+
""".trimIndent()
49+
50+
val base64 = Base64.encode(sketch.toByteArray())
51+
Schema.handleSchema("pde://sketch/base64/$base64?pde=AnotherFile:$base64", base)
52+
val captor = ArgumentCaptor.forClass(String::class.java)
53+
54+
verify(base).handleOpenUntitled(captor.capture())
55+
56+
val file = File(captor.value)
57+
assert(file.exists())
58+
assert(file.readText() == sketch)
59+
60+
val extra = file.parentFile.resolve("AnotherFile.pde")
61+
assert(extra.exists())
62+
assert(extra.readText() == sketch)
63+
file.parentFile.deleteRecursively()
64+
}
65+
66+
@Test
67+
fun testURLSketch() {
68+
Schema.handleSchema("pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/Array/Array.pde", base)
69+
70+
val captor = ArgumentCaptor.forClass(String::class.java)
71+
verify(base).handleOpenUntitled(captor.capture())
72+
val output = File(captor.value)
73+
assert(output.exists())
74+
assert(output.name == "Array.pde")
75+
assert(output.extension == "pde")
76+
assert(output.parentFile.name == "Array")
77+
78+
output.parentFile.parentFile.deleteRecursively()
79+
}
80+
81+
@ParameterizedTest
82+
@ValueSource(strings = [
83+
"Module.pde:https://github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/Module.pde",
84+
"Module.pde",
85+
"Module:Module.pde",
86+
"Module:https://github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/Module.pde",
87+
"Module.pde:github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/Module.pde"
88+
])
89+
fun testURLSketchWithFile(file: String){
90+
Schema.handleSchema("pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/ArrayObjects.pde?pde=$file", base)
91+
92+
val captor = ArgumentCaptor.forClass(String::class.java)
93+
verify(base).handleOpenUntitled(captor.capture())
94+
95+
// wait for threads to resolve
96+
Thread.sleep(1000)
97+
98+
val output = File(captor.value)
99+
assert(output.parentFile.name == "ArrayObjects")
100+
assert(output.exists())
101+
assert(output.parentFile.resolve("Module.pde").exists())
102+
output.parentFile.parentFile.deleteRecursively()
103+
}
104+
105+
@Test
106+
fun testPreferences() {
107+
Schema.handleSchema("pde://preferences?test=value", base)
108+
preferences.verify {
109+
Preferences.set("test", "value")
110+
Preferences.save()
111+
}
112+
}
113+
}

gradle/libs.versions.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@
22
kotlin = "2.0.20"
33
compose-plugin = "1.7.1"
44
jogl = "2.5.0"
5+
jupiter = "5.12.0"
56

67
[libraries]
78
jogl = { module = "org.jogamp.jogl:jogl-all-main", version.ref = "jogl" }
89
gluegen = { module = "org.jogamp.gluegen:gluegen-rt-main", version.ref = "jogl" }
9-
flatlaf = { module = "com.formdev:flatlaf", version = "3.4.1" }
10+
flatlaf = { module = "com.formdev:flatlaf", version = "2.4" }
1011
jna = { module = "net.java.dev.jna:jna", version = "5.12.1" }
1112
jnaplatform = { module = "net.java.dev.jna:jna-platform", version = "5.12.1" }
1213
compottie = { module = "io.github.alexzhirkevich:compottie", version = "2.0.0-rc02" }
1314
kaml = { module = "com.charleskorn.kaml:kaml", version = "0.65.0" }
1415
junit = { module = "junit:junit", version = "4.13.2" }
16+
junitJupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "jupiter" }
17+
junitJupiterParams = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "jupiter" }
1518
mockito = { module = "org.mockito:mockito-core", version = "4.11.0" }
19+
mockitoKotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "5.4.0" }
1620
antlr = { module = "org.antlr:antlr4", version = "4.7.2" }
1721
eclipseJDT = { module = "org.eclipse.jdt:org.eclipse.jdt.core", version = "3.16.0" }
1822
eclipseJDTCompiler = { module = "org.eclipse.jdt:org.eclipse.jdt.compiler.apt", version = "1.3.400" }

0 commit comments

Comments
 (0)