Skip to content

Commit

Permalink
Add samples and validation to prepare for releases
Browse files Browse the repository at this point in the history
  • Loading branch information
lopcode committed Aug 11, 2024
1 parent 4426f7e commit 409a90c
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 63 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*.{kt,kts}]
ktlint_code_style = ktlint_official
ktlint_standard_multiline-expression-wrapping = disabled
ktlint_standard_chain-method-continuation = disabled
ktlint_standard_function-signature = disabled
ktlint_standard_function-expression-body = disabled
max_line_length = 120
8 changes: 7 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,10 @@ jobs:
gradle-version: 8.9

- name: Run checks
run: ./gradlew build check shadowJar
run: ./gradlew build check shadowJar

- name: Run samples
run: |
sudo apt install imagemagick
magick --version
./run_samples.sh
13 changes: 4 additions & 9 deletions .run/ImFfm.run.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ImFfm" type="Application" factoryName="Application" nameIsGenerated="true">
<configuration default="false" name="ImFfm" type="JetRunConfigurationType" nameIsGenerated="true">
<envs>
<env name="DYLD_LIBRARY_PATH" value="native_libs" />
</envs>
<option name="MAIN_CLASS_NAME" value="app.photofox.imffm.ImFfm" />
<module name="im-ffm.core" />
<option name="MAIN_CLASS_NAME" value="imffm.ImFfm" />
<module name="im-ffm.sample.main" />
<shortenClasspath name="NONE" />
<option name="VM_PARAMETERS" value="--enable-native-access=ALL-UNNAMED" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="app.photofox.imffm.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
Expand Down
52 changes: 0 additions & 52 deletions core/src/main/java/app/photofox/imffm/ImFfm.java

This file was deleted.

12 changes: 12 additions & 0 deletions run_samples.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -eou pipefail

echo "building samples..."
./gradlew sample:clean sample:shadowJar

if [[ "$OSTYPE" == "darwin"* ]]; then
export DYLD_LIBRARY_PATH=native_libs
fi

echo "running samples..."
java -jar sample/build/libs/sample-all.jar
62 changes: 62 additions & 0 deletions sample/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@file:Suppress("UnstableApiUsage")

import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
kotlin("jvm") version "2.0.10"
id("com.github.johnrengelman.shadow") version "8.1.1"
application
}

repositories {
mavenCentral()
}

dependencies {
implementation(project(":core"))
implementation(platform("org.slf4j:slf4j-bom:2.0.16"))
implementation("org.slf4j:slf4j-api")
implementation("org.slf4j:slf4j-simple")
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(22))
}
}

testing {
suites {
val test by getting(JvmTestSuite::class) {
useKotlinTest("2.0.0")
testType = TestSuiteType.UNIT_TEST
}
}
}

tasks.withType<Test> {
testLogging {
events = TestLogEvent.values().toSet() - TestLogEvent.STARTED
exceptionFormat = TestExceptionFormat.FULL
}
environment(mapOf("DYLD_LIBRARY_PATH" to "native_libs"))
outputs.upToDateWhen { false }
}

tasks.withType<JavaExec>().configureEach {
environment(mapOf("DYLD_LIBRARY_PATH" to "native_libs"))
javaLauncher.set(project.javaToolchains.launcherFor(java.toolchain))
}

tasks.withType<Jar>().configureEach {
manifest {
attributes("Enable-Native-Access" to "ALL-UNNAMED")
}
}

application {
mainClass = "imffm.ImFfm"
applicationDefaultJvmArgs = listOf("--enable-native-access=ALL-UNNAMED")
// todo: figure out how to set DYLD_LIBRARY_PATH here
}
52 changes: 52 additions & 0 deletions sample/src/main/kotlin/imffm/ImFfm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package imffm

import org.slf4j.LoggerFactory
import java.lang.foreign.Arena
import java.nio.file.Files
import java.nio.file.Paths
import java.util.Locale
import kotlin.system.exitProcess

object ImFfm {

val logger = LoggerFactory.getLogger(ImFfm::class.java)

@JvmStatic
fun main(args: Array<String>) {
val samples = listOf(
ResizeImageSample
)
val sampleParentRunPath = Paths.get("sample_run")
if (Files.exists(sampleParentRunPath)) {
logger.info("clearing sample run directory at path \"$sampleParentRunPath\"")
sampleParentRunPath.toFile().deleteRecursively()
}
Files.deleteIfExists(sampleParentRunPath)
Files.createDirectory(sampleParentRunPath)

Arena.ofConfined().use { arena ->
samples.forEach { sample ->
logger.info("running sample \"${sample.name}\"...")
val sampleDirectoryName = makeSampleDirectoryName(sample.name)
val sampleRunPath = sampleParentRunPath.resolve(sampleDirectoryName)
Files.createDirectory(sampleRunPath)

sample.run(arena, sampleRunPath)
val validationResult = sample.validate(sampleRunPath)
if (validationResult.isFailure) {
logger.error("validation failed ❌", validationResult.exceptionOrNull())
exitProcess(1)
}
logger.info("validation succeeded ✅")
}
}
logger.info("all samples ran successfully 🎉")
exitProcess(0)
}

private fun makeSampleDirectoryName(original: String): String {
return original
.replace(oldValue = " ", "_")
.lowercase(Locale.ENGLISH)
}
}
70 changes: 70 additions & 0 deletions sample/src/main/kotlin/imffm/ResizeImageSample.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package imffm

import app.photofox.imffm.generated.MagickWand_h.*
import java.io.File
import java.lang.foreign.Arena
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.absolutePathString

// https://imagemagick.org/MagickWand/resize.htm
object ResizeImageSample : RunnableSample {

override val name = "Image resizing"

override fun run(arena: Arena, workingDirectory: Path) {
MagickWandGenesis()

val wand = NewMagickWand()
// Read the image - all you need to do is change "logo:" to some other
// filename to have this resize and, if necessary, convert a different file
MagickReadImage(wand, arena.allocateFrom("logo:"))

// Get the image's width and height
var width = MagickGetImageWidth(wand)
var height = MagickGetImageHeight(wand)

// Cut them in half but make sure they don't underflow
width = (width / 2).coerceAtLeast(1)
height = (height / 2).coerceAtLeast(1)

// Resize the image using the Lanczos filter
// The blur factor is a "double", where > 1 is blurry, < 1 is sharp
// I haven't figured out how you would change the blur parameter of MagickResizeImage
// on the command line so I have set it to its default of one.
MagickResizeImage(wand, width, height, LanczosFilter())

// Set the compression quality to 95 (high quality = low compression)
MagickSetImageCompressionQuality(wand, 95)

/* Write the new image */
val resizedImagePath = workingDirectory.resolve("logo_resize.jpg")
MagickWriteImage(wand, arena.allocateFrom(resizedImagePath.absolutePathString()))

/* Clean up */
DestroyMagickWand(wand)

MagickWandTerminus()
}

override fun validate(workingDirectory: Path): Result<Unit> {
val resizedImagePath = workingDirectory.resolve("logo_resize.jpg")
if (!Files.exists(resizedImagePath)) {
return Result.failure(
RuntimeException("expected file does not exist")
)
}
val minSizeKb = 20
val maxSizeKb = 40
return runCatching {
Files.size(resizedImagePath) / 1000.0
}.map { size ->
if (size < minSizeKb || size > maxSizeKb) {
return Result.failure(
RuntimeException("image out of bounds (min $minSizeKb, max $maxSizeKb, actual $size")
)
}
}
}
}
11 changes: 11 additions & 0 deletions sample/src/main/kotlin/imffm/RunnableSample.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package imffm

import java.lang.foreign.Arena
import java.nio.file.Path

interface RunnableSample {

val name: String
fun run(arena: Arena, workingDirectory: Path)
fun validate(workingDirectory: Path): Result<Unit>
}
Empty file.
3 changes: 2 additions & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ plugins {
}

rootProject.name = "im-ffm"
include("core")
include("core", "sample")
include("sample")

0 comments on commit 409a90c

Please sign in to comment.