Merge branch 'test-schema' into gradle-welcome-screen

This commit is contained in:
Stef Tervelde
2025-03-11 20:42:46 +01:00
7 changed files with 167 additions and 37 deletions

View File

@@ -35,6 +35,11 @@ sourceSets{
srcDirs("resources", listOf("languages", "fonts", "theme").map { "../build/shared/lib/$it" })
}
}
test{
kotlin{
srcDirs("test")
}
}
}
compose.desktop {
@@ -105,6 +110,17 @@ dependencies {
implementation(libs.compottie)
implementation(libs.kaml)
testImplementation(kotlin("test"))
testImplementation(libs.mockitoKotlin)
testImplementation(libs.junitJupiter)
testImplementation(libs.junitJupiterParams)
}
tasks.test {
useJUnitPlatform()
workingDir = file("build/test")
workingDir.mkdirs()
}
tasks.compileJava{

View File

@@ -1364,10 +1364,10 @@ public class Base {
* @param schemeUri the full URI, including pde://
*/
public Editor handleScheme(String schemeUri) {
// var result = Schema.handleSchema(schemeUri, this);
// if (result != null) {
// return result;
// }
var result = Schema.handleSchema(schemeUri, this);
if (result != null) {
return result;
}
String location = schemeUri.substring(6);
if (location.length() > 0) {

View File

@@ -1,6 +1,5 @@
package processing.app;
import java.awt.EventQueue;
import java.io.*;
import java.util.*;
import java.util.zip.ZipFile;
@@ -330,7 +329,7 @@ public class Library extends LocalContribution {
* imports to specific libraries.
* @param importToLibraryTable mapping from package names to Library objects
*/
static boolean instruced = false;
static boolean instructed = false;
// public void addPackageList(HashMap<String,Library> importToLibraryTable) {
public void addPackageList(Map<String, List<Library>> importToLibraryTable) {
// PApplet.println(packages);
@@ -343,8 +342,8 @@ public class Library extends LocalContribution {
libraries = new ArrayList<>();
importToLibraryTable.put(pkg, libraries);
} else {
if(!instruced) {
instruced = true;
if(!instructed) {
instructed = true;
Messages.err("The library found in");
Messages.err(getPath());
Messages.err("conflicts with");

View File

@@ -53,7 +53,11 @@ class Schema {
private fun handleSketchUrl(uri: URI): Editor?{
val url = File(uri.path.replace("/url/", ""))
val tempSketchFolder = File(Base.untitledFolder, url.nameWithoutExtension)
val rand = (1..6)
.map { (('a'..'z') + ('A'..'Z')).random() }
.joinToString("")
val tempSketchFolder = File(File(Base.untitledFolder, rand), url.nameWithoutExtension)
tempSketchFolder.mkdirs()
val tempSketchFile = File(tempSketchFolder, "${tempSketchFolder.name}.pde")
@@ -71,7 +75,7 @@ class Schema {
?.map { it.split("=") }
?.associate {
URLDecoder.decode(it[0], StandardCharsets.UTF_8) to
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
}
?: emptyMap()
options["data"]?.let{ data ->
@@ -81,7 +85,7 @@ class Schema {
downloadFiles(uri, code, File(sketchFolder, "code"))
}
options["pde"]?.let{ pde ->
downloadFiles(uri, pde, sketchFolder)
downloadFiles(uri, pde, sketchFolder, "pde")
}
options["mode"]?.let{ mode ->
val modeFile = File(sketchFolder, "sketch.properties")
@@ -89,7 +93,7 @@ class Schema {
}
}
private fun downloadFiles(uri: URI, urlList: String, targetFolder: File){
private fun downloadFiles(uri: URI, urlList: String, targetFolder: File, extension: String = ""){
Thread{
targetFolder.mkdirs()
@@ -101,37 +105,31 @@ class Schema {
val files = urlList.split(",")
files.filter { it.isNotBlank() }
.map{ it.split(":", limit = 2) }
.map{ segments ->
if(segments.size == 2){
if(segments[0].isBlank()){
return@map listOf(null, segments[1])
}
return@map segments
}
return@map listOf(null, segments[0])
.map {
if (it.contains(":")) it
else "$it:$it"
}
.map{ it.split(":", limit = 2) }
.forEach { (name, content) ->
var target = File(targetFolder, name)
if(extension.isNotBlank() && target.extension != extension){
target = File(targetFolder, "$name.$extension")
}
try{
// Try to decode the content as base64
val file = Base64.getDecoder().decode(content)
if(name == null){
if(name.isBlank()){
Messages.err("Base64 files needs to start with a file name followed by a colon")
return@forEach
}
File(targetFolder, name).writeBytes(file)
target.writeBytes(file)
}catch(_: IllegalArgumentException){
// Assume it's a URL and download it
var url = URI.create(content)
if(url.host == null){
url = URI.create("https://$base/$content")
}
if(url.scheme == null){
url = URI.create("https://$content")
}
val target = File(targetFolder, name ?: url.path.split("/").last())
url.toURL().openStream().use { input ->
val url = URL(when{
content.startsWith("https://") -> content
content.startsWith("http://") -> content.replace("http://", "https://")
URL("https://$content").path.isNotBlank() -> "https://$content"
else -> "https://$base/$content"
})
url.openStream().use { input ->
target.outputStream().use { output ->
input.copyTo(output)
}
@@ -148,7 +146,7 @@ class Schema {
?.map { it.split("=") }
?.associate {
URLDecoder.decode(it[0], StandardCharsets.UTF_8) to
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
URLDecoder.decode(it[1], StandardCharsets.UTF_8)
}
?: emptyMap()
for ((key, value) in options){

View File

@@ -0,0 +1,113 @@
package processing.app
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import org.mockito.ArgumentCaptor
import org.mockito.MockedStatic
import org.mockito.Mockito.mockStatic
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import java.io.File
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.test.Test
class SchemaTest {
private val base: Base = mock<Base>{
}
companion object {
val preferences: MockedStatic<Preferences> = mockStatic(Preferences::class.java)
}
@Test
fun testLocalFiles() {
val file = "/this/is/a/local/file"
Schema.handleSchema("pde://$file", base)
verify(base).handleOpen(file)
}
@Test
fun testNewSketch() {
Schema.handleSchema("pde://sketch/new", base)
verify(base).handleNew()
}
@OptIn(ExperimentalEncodingApi::class)
@Test
fun testBase64SketchAndExtraFiles() {
val sketch = """
void setup(){
}
void draw(){
}
""".trimIndent()
val base64 = Base64.encode(sketch.toByteArray())
Schema.handleSchema("pde://sketch/base64/$base64?pde=Module:$base64", base)
val captor = ArgumentCaptor.forClass(String::class.java)
verify(base).handleOpenUntitled(captor.capture())
val file = File(captor.value)
assert(file.exists())
assert(file.readText() == sketch)
val extra = file.parentFile.resolve("Module.pde")
assert(extra.exists())
assert(extra.readText() == sketch)
file.parentFile.deleteRecursively()
}
@Test
fun testURLSketch() {
Schema.handleSchema("pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/Array/Array.pde", base)
val captor = ArgumentCaptor.forClass(String::class.java)
verify(base).handleOpenUntitled(captor.capture())
val output = File(captor.value)
assert(output.exists())
assert(output.name == "Array.pde")
assert(output.extension == "pde")
assert(output.parentFile.name == "Array")
output.parentFile.parentFile.deleteRecursively()
}
@ParameterizedTest
@ValueSource(strings = [
"Module.pde:https://github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/Module.pde",
"Module.pde",
"Module:Module.pde",
"Module:https://github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/Module.pde",
"Module.pde:github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/Module.pde"
])
fun testURLSketchWithFile(file: String){
Schema.handleSchema("pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/ArrayObjects/ArrayObjects.pde?pde=$file", base)
val captor = ArgumentCaptor.forClass(String::class.java)
verify(base).handleOpenUntitled(captor.capture())
// wait for threads to resolve
Thread.sleep(1000)
val output = File(captor.value)
assert(output.parentFile.name == "ArrayObjects")
assert(output.exists())
assert(output.parentFile.resolve("Module.pde").exists())
output.parentFile.parentFile.deleteRecursively()
}
@Test
fun testPreferences() {
Schema.handleSchema("pde://preferences?test=value", base)
preferences.verify {
Preferences.set("test", "value")
Preferences.save()
}
}
}

View File

@@ -63,7 +63,7 @@ dependencies {
```
## Developing for Core
The easiest way to develop for core, without the need to build the whole project, is to use the `examples/src` sketches. In
The easiest way to develop for core, without the need to build the whole project, is to use the `examples/src` sketches.
## PGraphics Modes
Documentation on how to develop graphics modes as a library should go here.

View File

@@ -2,6 +2,7 @@
kotlin = "2.0.20"
compose-plugin = "1.7.1"
jogl = "2.5.0"
jupiter = "5.12.0"
[libraries]
jogl = { module = "org.jogamp.jogl:jogl-all-main", version.ref = "jogl" }
@@ -12,7 +13,10 @@ jnaplatform = { module = "net.java.dev.jna:jna-platform", version = "5.12.1" }
compottie = { module = "io.github.alexzhirkevich:compottie", version = "2.0.0-rc02" }
kaml = { module = "com.charleskorn.kaml:kaml", version = "0.65.0" }
junit = { module = "junit:junit", version = "4.13.2" }
junitJupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "jupiter" }
junitJupiterParams = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "jupiter" }
mockito = { module = "org.mockito:mockito-core", version = "4.11.0" }
mockitoKotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "5.4.0" }
antlr = { module = "org.antlr:antlr4", version = "4.7.2" }
eclipseJDT = { module = "org.eclipse.jdt:org.eclipse.jdt.core", version = "3.16.0" }
eclipseJDTCompiler = { module = "org.eclipse.jdt:org.eclipse.jdt.compiler.apt", version = "1.3.400" }