Skip to content

Commit 1092625

Browse files
Add jqwik, ArchUnit, and SpotBugs (#197)
* Add jqwik, ArchUnit, and SpotBugs to java-llama.cpp - Add LlamaParameterProperties (jqwik property-based tests for InferenceParameters) - Add LlamaArchitectureTest (ArchUnit rules: no java.util.logging, no test frameworks in prod) - Add spotbugs-maven-plugin with fb-contrib and findsecbugs - Add README badges for jqwik, ArchUnit, SpotBugs https://claude.ai/code/session_01Teo4XcbGFp8LmxeiY8N37h * Enable SpotBugs failOnError https://claude.ai/code/session_01Teo4XcbGFp8LmxeiY8N37h * Add JMH benchmark for InferenceParameters serialization https://claude.ai/code/session_01Teo4XcbGFp8LmxeiY8N37h --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7095c2d commit 1092625

6 files changed

Lines changed: 207 additions & 0 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
**Build:**
22
![Java 11+](https://img.shields.io/badge/Java-11%2B-informational)
33
![JUnit](https://img.shields.io/badge/tested%20with-JUnit4-yellow)
4+
[![jqwik](https://img.shields.io/badge/tested%20with-jqwik-1f6feb)](https://jqwik.net)
5+
[![ArchUnit](https://img.shields.io/badge/tested%20with-ArchUnit-c71a36)](https://www.archunit.org)
6+
[![SpotBugs](https://img.shields.io/badge/analyzed%20with-SpotBugs-3b5998)](https://spotbugs.github.io)
7+
[![JMH](https://img.shields.io/badge/benchmarked%20with-JMH-brightgreen)](https://github.com/openjdk/jmh)
48
[![llama.cpp b9333](https://img.shields.io/badge/llama.cpp-%23b9333-informational)](https://github.com/ggml-org/llama.cpp/releases/tag/b9333)
59
[![Publish](https://github.com/bernardladenthin/java-llama.cpp/actions/workflows/publish.yml/badge.svg)](https://github.com/bernardladenthin/java-llama.cpp/actions/workflows/publish.yml)
610
[![CodeQL](https://github.com/bernardladenthin/java-llama.cpp/actions/workflows/codeql.yml/badge.svg)](https://github.com/bernardladenthin/java-llama.cpp/actions/workflows/codeql.yml)

pom.xml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ SPDX-License-Identifier: MIT
6161
<version>6.1.0</version>
6262
<scope>test</scope>
6363
</dependency>
64+
<dependency>
65+
<groupId>net.jqwik</groupId>
66+
<artifactId>jqwik</artifactId>
67+
<version>1.9.2</version>
68+
<scope>test</scope>
69+
</dependency>
70+
<dependency>
71+
<groupId>com.tngtech.archunit</groupId>
72+
<artifactId>archunit-junit5</artifactId>
73+
<version>1.3.0</version>
74+
<scope>test</scope>
75+
</dependency>
6476
<dependency>
6577
<groupId>org.jetbrains</groupId>
6678
<artifactId>annotations</artifactId>
@@ -103,6 +115,18 @@ SPDX-License-Identifier: MIT
103115
<version>1.27</version>
104116
<scope>provided</scope>
105117
</dependency>
118+
<dependency>
119+
<groupId>org.openjdk.jmh</groupId>
120+
<artifactId>jmh-core</artifactId>
121+
<version>1.37</version>
122+
<scope>test</scope>
123+
</dependency>
124+
<dependency>
125+
<groupId>org.openjdk.jmh</groupId>
126+
<artifactId>jmh-generator-annprocess</artifactId>
127+
<version>1.37</version>
128+
<scope>test</scope>
129+
</dependency>
106130
</dependencies>
107131

108132
<build>
@@ -297,6 +321,48 @@ SPDX-License-Identifier: MIT
297321
<redirectTestOutputToFile>true</redirectTestOutputToFile>
298322
</configuration>
299323
</plugin>
324+
<plugin>
325+
<groupId>com.github.spotbugs</groupId>
326+
<artifactId>spotbugs-maven-plugin</artifactId>
327+
<version>4.8.6.6</version>
328+
<configuration>
329+
<effort>Default</effort>
330+
<threshold>Default</threshold>
331+
<failOnError>true</failOnError>
332+
<includeTests>false</includeTests>
333+
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
334+
<plugins>
335+
<plugin>
336+
<groupId>com.mebigfatguy.fb-contrib</groupId>
337+
<artifactId>fb-contrib</artifactId>
338+
<version>7.6.4</version>
339+
</plugin>
340+
<plugin>
341+
<groupId>com.h3xstream.findsecbugs</groupId>
342+
<artifactId>findsecbugs-plugin</artifactId>
343+
<version>1.13.0</version>
344+
</plugin>
345+
</plugins>
346+
</configuration>
347+
<executions>
348+
<execution>
349+
<id>spotbugs-check</id>
350+
<phase>verify</phase>
351+
<goals>
352+
<goal>check</goal>
353+
</goals>
354+
</execution>
355+
</executions>
356+
</plugin>
357+
<plugin>
358+
<groupId>org.codehaus.mojo</groupId>
359+
<artifactId>exec-maven-plugin</artifactId>
360+
<version>3.6.3</version>
361+
<configuration>
362+
<mainClass>org.openjdk.jmh.Main</mainClass>
363+
<classpathScope>test</classpathScope>
364+
</configuration>
365+
</plugin>
300366
</plugins>
301367
</build>
302368

spotbugs-exclude.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
SPDX-FileCopyrightText: 2026 Bernard Ladenthin <bernard.ladenthin@gmail.com>
4+
5+
SPDX-License-Identifier: MIT
6+
-->
7+
<FindBugsFilter
8+
xmlns="https://github.com/spotbugs/filter/3.0.0"
9+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/4.8.6/spotbugs/etc/findbugsfilter.xsd">
11+
12+
</FindBugsFilter>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-FileCopyrightText: 2026 Bernard Ladenthin <bernard.ladenthin@gmail.com>
2+
//
3+
// SPDX-License-Identifier: MIT
4+
package net.ladenthin.llama;
5+
6+
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
7+
8+
import com.tngtech.archunit.core.importer.ImportOption;
9+
import com.tngtech.archunit.junit.AnalyzeClasses;
10+
import com.tngtech.archunit.junit.ArchTest;
11+
import com.tngtech.archunit.lang.ArchRule;
12+
13+
@AnalyzeClasses(packages = "net.ladenthin.llama", importOptions = ImportOption.DoNotIncludeTests.class)
14+
public class LlamaArchitectureTest {
15+
16+
/**
17+
* Production code must not use java.util.logging directly; all logging goes through SLF4J.
18+
*/
19+
@ArchTest
20+
static final ArchRule noJavaUtilLogging = noClasses()
21+
.that().resideInAPackage("net.ladenthin.llama..")
22+
.should().dependOnClassesThat()
23+
.resideInAPackage("java.util.logging..");
24+
25+
/**
26+
* Test-framework classes must not appear in production code.
27+
*/
28+
@ArchTest
29+
static final ArchRule noTestFrameworksInProduction = noClasses()
30+
.that().resideInAPackage("net.ladenthin.llama..")
31+
.should().dependOnClassesThat()
32+
.resideInAnyPackage("org.junit..", "net.jqwik..", "com.tngtech.archunit..");
33+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// SPDX-FileCopyrightText: 2026 Bernard Ladenthin <bernard.ladenthin@gmail.com>
2+
//
3+
// SPDX-License-Identifier: MIT
4+
package net.ladenthin.llama;
5+
6+
import net.jqwik.api.ForAll;
7+
import net.jqwik.api.Property;
8+
import net.jqwik.api.constraints.FloatRange;
9+
10+
public class LlamaParameterProperties {
11+
12+
@Property
13+
boolean setTemperatureNeverThrows(@ForAll @FloatRange(min = 0.0f, max = 2.0f) float temperature) {
14+
String json = new InferenceParameters().setTemperature(temperature).toString();
15+
return json.contains("temperature");
16+
}
17+
18+
@Property
19+
boolean setTopPNeverThrows(@ForAll @FloatRange(min = 0.0f, max = 1.0f) float topP) {
20+
String json = new InferenceParameters().setTopP(topP).toString();
21+
return json.contains("top_p");
22+
}
23+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-FileCopyrightText: 2026 Bernard Ladenthin <bernard.ladenthin@gmail.com>
2+
//
3+
// SPDX-License-Identifier: MIT
4+
package net.ladenthin.llama.benchmark;
5+
6+
import java.util.concurrent.TimeUnit;
7+
import net.ladenthin.llama.InferenceParameters;
8+
import org.openjdk.jmh.annotations.Benchmark;
9+
import org.openjdk.jmh.annotations.BenchmarkMode;
10+
import org.openjdk.jmh.annotations.Fork;
11+
import org.openjdk.jmh.annotations.Measurement;
12+
import org.openjdk.jmh.annotations.Mode;
13+
import org.openjdk.jmh.annotations.OutputTimeUnit;
14+
import org.openjdk.jmh.annotations.Scope;
15+
import org.openjdk.jmh.annotations.State;
16+
import org.openjdk.jmh.annotations.Warmup;
17+
import org.openjdk.jmh.infra.Blackhole;
18+
19+
/**
20+
* Throughput benchmark for {@link InferenceParameters} JSON serialization.
21+
*
22+
* <p>{@link InferenceParameters#toString()} serializes the parameter map to a JSON string
23+
* that is passed across the JNI boundary on every inference call. This benchmark
24+
* measures the allocation and serialization cost of building a typical parameter set.</p>
25+
*
26+
* <p>Run locally:</p>
27+
* <pre>
28+
* mvn test-compile exec:java -Dexec.args="InferenceParametersBenchmark -prof gc"
29+
* </pre>
30+
*/
31+
@BenchmarkMode(Mode.Throughput)
32+
@OutputTimeUnit(TimeUnit.SECONDS)
33+
@State(Scope.Thread)
34+
@Warmup(iterations = 3, time = 1)
35+
@Measurement(iterations = 5, time = 2)
36+
@Fork(1)
37+
public class InferenceParametersBenchmark {
38+
39+
/**
40+
* Serializes a minimal {@link InferenceParameters} instance to JSON.
41+
*
42+
* <p>Baseline: measures the cost of constructing a parameter object with
43+
* no custom settings — the default path every inference call takes.</p>
44+
*
45+
* @param bh JMH blackhole to prevent dead-code elimination
46+
*/
47+
@Benchmark
48+
public void serializeDefault(Blackhole bh) {
49+
bh.consume(new InferenceParameters().toString());
50+
}
51+
52+
/**
53+
* Serializes a fully-populated {@link InferenceParameters} instance to JSON.
54+
*
55+
* <p>Measures the serialization overhead when callers set several sampling
56+
* parameters — representative of a typical chat inference request.</p>
57+
*
58+
* @param bh JMH blackhole to prevent dead-code elimination
59+
*/
60+
@Benchmark
61+
public void serializeWithSamplingParams(Blackhole bh) {
62+
bh.consume(new InferenceParameters()
63+
.setTemperature(0.7f)
64+
.setTopP(0.9f)
65+
.setNPredict(512)
66+
.setStop(java.util.Arrays.asList("</s>", "<|im_end|>"))
67+
.toString());
68+
}
69+
}

0 commit comments

Comments
 (0)