Skip to content

Commit f6a2931

Browse files
Merge pull request #31 from jenspapenhagen/main
Adds Jacoco rules to the project
2 parents 28d8358 + 528c567 commit f6a2931

File tree

14 files changed

+253
-40
lines changed

14 files changed

+253
-40
lines changed

.github/badges/jacoco.svg

Lines changed: 1 addition & 0 deletions
Loading

.github/workflows/build.yml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ on:
1515
jobs:
1616
build:
1717
runs-on: ubuntu-latest
18-
18+
19+
permissions:
20+
contents: write
21+
pull-requests: write
22+
1923
steps:
2024
- name: Checkout code
2125
uses: actions/checkout@v5
@@ -40,7 +44,25 @@ jobs:
4044
name: test-results
4145
path: build/reports/tests/test/
4246
retention-days: 7
43-
47+
48+
- name: Add coverage to PR
49+
id: jacoco
50+
uses: madrapps/[email protected]
51+
with:
52+
paths: ${{ github.workspace }}/build/customJacocoReportDir/test/jacocoTestReport.xml
53+
token: ${{ secrets.GITHUB_TOKEN }}
54+
min-coverage-overall: 85
55+
min-coverage-changed-files: 75
56+
title: Code Coverage
57+
update-comment: true
58+
59+
- name: Fail PR if overall coverage is less than 85%
60+
if: ${{ steps.jacoco.outputs.coverage-overall < 85.0 }}
61+
uses: actions/github-script@v6
62+
with:
63+
script: |
64+
core.setFailed('Overall coverage is less than 85%!')
65+
4466
- name: Upload build artifacts
4567
uses: actions/upload-artifact@v5
4668
with:

.github/workflows/release.yml

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
with:
1818
ref: main
1919
fetch-depth: 0
20-
20+
2121
- name: Extract version from tag
2222
id: get_version
2323
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
@@ -29,20 +29,20 @@ jobs:
2929
echo "version=$VERSION" > gradle.properties
3030
echo "gradle.properties criado:"
3131
cat gradle.properties
32-
32+
3333
- name: Set up JDK 21
3434
uses: actions/setup-java@v5
3535
with:
3636
java-version: '21'
3737
distribution: 'temurin'
3838
cache: 'gradle'
39-
39+
4040
- name: Grant execute permission for gradlew
4141
run: chmod +x gradlew
42-
42+
4343
- name: Build with Gradle
4444
run: ./gradlew build
45-
45+
4646
- name: Validate Maven Central credentials
4747
run: |
4848
if [ -z "${{ secrets.MAVEN_CENTRAL_TOKEN }}" ]; then
@@ -52,23 +52,23 @@ jobs:
5252
echo "✅ Maven Central token is set (length: ${#MAVEN_CENTRAL_TOKEN})"
5353
env:
5454
MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
55-
55+
5656
- name: Check curl version
5757
run: curl --version
58-
58+
5959
- name: Publish to Maven Central
6060
run: ./gradlew publishToMavenCentral
6161
env:
6262
MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
6363
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
6464
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
65-
65+
6666
- name: Update README version
6767
run: |
6868
VERSION=${{ steps.get_version.outputs.VERSION }}
6969
sed -i "s/jtoon:[0-9]\+\.[0-9]\+\.[0-9]\+/jtoon:$VERSION/g" README.md
7070
sed -i "s/<version>[0-9]\+\.[0-9]\+\.[0-9]\+<\/version>/<version>$VERSION<\/version>/g" README.md
71-
71+
7272
- name: Commit README changes
7373
run: |
7474
git config user.name "github-actions[bot]"
@@ -78,7 +78,7 @@ jobs:
7878
git push origin main
7979
env:
8080
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
81-
81+
8282
- name: Create GitHub Release
8383
uses: softprops/action-gh-release@v2
8484
with:
@@ -88,7 +88,20 @@ jobs:
8888
prerelease: ${{ contains(steps.get_version.outputs.VERSION, '-') }}
8989
env:
9090
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
91-
91+
92+
- name: Generate JaCoCo Badge
93+
uses: cicirello/[email protected]
94+
with:
95+
generate-branches-badge: true
96+
jacoco-csv-file: build/customJacocoReportDir/test/jacocoTestReport.csv
97+
98+
- name: Update Jacoco Badge
99+
uses: test-room-7/[email protected]
100+
with:
101+
file-path: .github/badges/jacoco.svg
102+
commit-msg: Update Jacoco Badge
103+
github-token: ${{ secrets.GITHUB_TOKEN }}
104+
92105
- name: Upload build artifacts
93106
uses: actions/upload-artifact@v5
94107
if: always()

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
[![Build](https://github.com/felipestanzani/jtoon/actions/workflows/build.yml/badge.svg)](https://github.com/felipestanzani/jtoon/actions/workflows/build.yml)
66
[![Release](https://github.com/felipestanzani/jtoon/actions/workflows/release.yml/badge.svg)](https://github.com/felipestanzani/jtoon/actions/workflows/release.yml)
77
[![Maven Central](https://img.shields.io/maven-central/v/com.felipestanzani/jtoon.svg)](https://central.sonatype.com/artifact/com.felipestanzani/jtoon)
8+
![Coverage](.github/badges/jacoco.svg)
89

910
**Token-Oriented Object Notation** is a compact, human-readable format designed for passing structured data to Large Language Models with significantly reduced token usage.
1011

build.gradle

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
id 'java'
33
id 'maven-publish'
44
id 'signing'
5+
id 'jacoco'
56
}
67

78
group = 'com.felipestanzani'
@@ -28,6 +29,11 @@ repositories {
2829
mavenCentral()
2930
}
3031

32+
jacoco {
33+
toolVersion = "0.8.14"
34+
reportsDirectory = layout.buildDirectory.dir('customJacocoReportDir')
35+
}
36+
3137
dependencies {
3238
implementation 'tools.jackson.core:jackson-databind:3.0.2'
3339
implementation 'tools.jackson.module:jackson-module-afterburner:3.0.2'
@@ -38,4 +44,35 @@ dependencies {
3844

3945
test {
4046
useJUnitPlatform()
41-
}
47+
finalizedBy jacocoTestReport // report is always generated after tests run
48+
}
49+
50+
jacocoTestReport {
51+
dependsOn test
52+
reports {
53+
csv.required = true
54+
xml.required = true
55+
html.outputLocation = layout.buildDirectory.dir('jacocoHtml')
56+
}
57+
}
58+
59+
jacocoTestCoverageVerification {
60+
violationRules {
61+
rule {
62+
enabled = true
63+
element = "BUNDLE"
64+
limit {
65+
counter = "LINE"
66+
value = "COVEREDRATIO"
67+
minimum = "0.85".toBigDecimal()
68+
}
69+
70+
limit {
71+
counter = "BRANCH"
72+
value = "COVEREDRATIO"
73+
minimum = "0.80".toBigDecimal()
74+
}
75+
}
76+
}
77+
}
78+
check.dependsOn jacocoTestReport

src/main/java/com/felipestanzani/jtoon/decoder/PrimitiveDecoder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ static Object parse(String value) {
7171

7272
// Check for leading zeros (treat as string, except for "0", "-0", "0.0", etc.)
7373
String trimmed = value.trim();
74-
if (trimmed.length() > 1 && trimmed.matches("^-?0+[\\d].*")
74+
if (trimmed.length() > 1
75+
&& trimmed.matches("^-?0+[0-7].*") //octal number
7576
&& !trimmed.matches("^-?0+(\\.0+)?([eE][+-]?\\d+)?$")) {
7677
return value;
7778
}

src/main/java/com/felipestanzani/jtoon/decoder/ValueDecoder.java

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ private List<Object> parseTabularArray(String header, int depth, String arrayDel
398398
* Returns true if parsing should continue, false if array should terminate.
399399
*/
400400
private boolean processTabularArrayLine(int depth, List<String> keys, String arrayDelimiter,
401-
List<Object> result) {
401+
List<Object> result) {
402402
String line = lines[currentLine];
403403

404404
if (isBlankLine(line)) {
@@ -483,7 +483,7 @@ private boolean shouldTerminateTabularArray(String line, int lineDepth, int dept
483483
* false otherwise.
484484
*/
485485
private boolean processTabularRow(String line, int lineDepth, int depth, List<String> keys,
486-
String arrayDelimiter, List<Object> result) {
486+
String arrayDelimiter, List<Object> result) {
487487
if (lineDepth == depth + 1) {
488488
String rowContent = line.substring((depth + 1) * options.indent());
489489
Map<String, Object> row = parseTabularRow(rowContent, keys, arrayDelimiter);
@@ -655,9 +655,17 @@ private Object parseListItem(String content, int depth) {
655655
String key = StringEscaper.unescape(itemContent.substring(0, colonIdx).trim());
656656
String value = itemContent.substring(colonIdx + 1).trim();
657657

658+
currentLine++;
659+
658660
Map<String, Object> item = new LinkedHashMap<>();
659-
// List item is at depth + 1, so pass depth + 1 to parseObjectItemValue
660-
Object parsedValue = parseObjectItemValue(value, depth + 1);
661+
Object parsedValue;
662+
// If no next line exists, handle simple case
663+
if (currentLine >= lines.length) {
664+
parsedValue = value.trim().isEmpty() ? new LinkedHashMap<>() : PrimitiveDecoder.parse(value);
665+
} else {
666+
// List item is at depth + 1, so pass depth + 1 to parseObjectItemValue
667+
parsedValue = parseObjectItemValue(value, depth + 1);
668+
}
661669
item.put(key, parsedValue);
662670
parseListItemFields(item, depth);
663671

@@ -668,20 +676,14 @@ private Object parseListItem(String content, int depth) {
668676
* Parses the value portion of an object item in a list, handling nested
669677
* objects,
670678
* empty values, and primitives.
671-
*
679+
*
672680
* @param value the value string to parse
673681
* @param depth the depth of the list item
674682
* @return the parsed value (Map, List, or primitive)
675683
*/
676684
private Object parseObjectItemValue(String value, int depth) {
677-
currentLine++;
678685
boolean isEmpty = value.trim().isEmpty();
679686

680-
// If no next line exists, handle simple case
681-
if (currentLine >= lines.length) {
682-
return isEmpty ? new LinkedHashMap<>() : PrimitiveDecoder.parse(value);
683-
}
684-
685687
// Find next non-blank line and its depth
686688
Integer nextDepth = findNextNonBlankLineDepth();
687689
if (nextDepth == null) {
@@ -703,7 +705,7 @@ private Object parseObjectItemValue(String value, int depth) {
703705

704706
/**
705707
* Finds the depth of the next non-blank line, skipping blank lines.
706-
*
708+
*
707709
* @return the depth of the next non-blank line, or null if none exists
708710
*/
709711
private Integer findNextNonBlankLineDepth() {
@@ -721,7 +723,7 @@ private Integer findNextNonBlankLineDepth() {
721723

722724
/**
723725
* Parses a field value, handling nested objects, empty values, and primitives.
724-
*
726+
*
725727
* @param fieldValue the value string to parse
726728
* @param fieldDepth the depth at which the field is located
727729
* @return the parsed value (Map, List, or primitive)
@@ -758,7 +760,7 @@ private Object parseFieldValue(String fieldValue, int fieldDepth) {
758760

759761
/**
760762
* Parses a keyed array field and adds it to the item map.
761-
*
763+
*
762764
* @param fieldContent the field content to parse
763765
* @param item the map to add the field to
764766
* @param depth the depth of the list item
@@ -791,7 +793,7 @@ private boolean parseKeyedArrayField(String fieldContent, Map<String, Object> it
791793

792794
/**
793795
* Parses a key-value field and adds it to the item map.
794-
*
796+
*
795797
* @param fieldContent the field content to parse
796798
* @param item the map to add the field to
797799
* @param depth the depth of the list item
@@ -1107,7 +1109,7 @@ private void processDirectChildLine(Map<String, Object> result, String line, int
11071109
* Processes a keyed array line (e.g., "key[3]: value").
11081110
*/
11091111
private void processKeyedArrayLine(Map<String, Object> result, String content, Matcher keyedArray,
1110-
int parentDepth) {
1112+
int parentDepth) {
11111113
String originalKey = keyedArray.group(1).trim();
11121114
String key = StringEscaper.unescape(originalKey);
11131115
String arrayHeader = content.substring(keyedArray.group(1).length());
@@ -1238,7 +1240,7 @@ private void checkFinalValueConflict(String finalSegment, Object existing, Objec
12381240
/**
12391241
* Parses a key-value string into an Object, handling nested objects, empty
12401242
* values, and primitives.
1241-
*
1243+
*
12421244
* @param value the value string to parse
12431245
* @param depth the depth at which the key-value pair is located
12441246
* @return the parsed value (Map, List, or primitive)
@@ -1277,15 +1279,15 @@ private Object parseKeyValue(String value, int depth) {
12771279

12781280
/**
12791281
* Puts a key-value pair into a map, handling path expansion.
1280-
*
1282+
*
12811283
* @param map the map to put the key-value pair into
12821284
* @param originalKey the original key before being unescaped (used for path
12831285
* expansion check)
12841286
* @param unescapedKey the unescaped key
12851287
* @param value the value to put
12861288
*/
12871289
private void putKeyValueIntoMap(Map<String, Object> map, String originalKey, String unescapedKey,
1288-
Object value) {
1290+
Object value) {
12891291
// Handle path expansion
12901292
if (shouldExpandKey(originalKey)) {
12911293
expandPathIntoMap(map, unescapedKey, value);
@@ -1439,4 +1441,4 @@ private void validateIndentation(String line) {
14391441
}
14401442
}
14411443
}
1442-
}
1444+
}

src/main/java/com/felipestanzani/jtoon/encoder/PrimitiveEncoder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static String encodePrimitive(JsonNode value, String delimiter) {
3939
*/
4040
private static String encodeNumber(JsonNode value) {
4141
if (value.isIntegralNumber()) {
42-
return value.asText();
42+
return value.asString();
4343
}
4444

4545
double doubleValue = value.asDouble();

src/main/java/com/felipestanzani/jtoon/util/StringValidator.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
public final class StringValidator {
1313
private static final Pattern NUMERIC_PATTERN = Pattern.compile("^-?\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?$",
1414
Pattern.CASE_INSENSITIVE);
15-
private static final Pattern OCTAL_PATTERN = Pattern.compile("^0\\d+$");
15+
private static final Pattern OCTAL_PATTERN = Pattern.compile("^0[0-7]+$");
1616
private static final Pattern UNQUOTED_KEY_PATTERN = Pattern.compile("^[A-Z_][\\w.]*$", Pattern.CASE_INSENSITIVE);
1717
private static final Pattern STRUCTURAL_CHARS = Pattern.compile("[\\[\\]{}]");
1818
private static final Pattern CONTROL_CHARS = Pattern.compile("[\\n\\r\\t]");
@@ -87,8 +87,7 @@ private static boolean looksLikeKeyword(String value) {
8787
}
8888

8989
private static boolean looksLikeNumber(String value) {
90-
return NUMERIC_PATTERN.matcher(value).matches()
91-
|| OCTAL_PATTERN.matcher(value).matches();
90+
return OCTAL_PATTERN.matcher(value).matches() || NUMERIC_PATTERN.matcher(value).matches();
9291
}
9392

9493
private static boolean containsColon(String value) {

0 commit comments

Comments
 (0)