Skip to content

Commit fc86786

Browse files
Copilotbrunoborges
andcommitted
Add automated proof script for code snippet verification
- Add html-generators/proof.java: JBang script (Java 25) that reads all content YAML files, finds those with an optional proofCode field, and runs each through JShell to verify they compile and execute correctly. Supports --list flag to show which patterns have/lack proofCode. - Add .github/workflows/proof.yml: GitHub Actions workflow that triggers on content changes (push/PR) and runs jbang html-generators/proof.java with Java 25 + JBang. - Add proofCode field to 10 representative patterns across categories: language (var, text-blocks, records, switch, instanceof), collections (immutable-list), streams (stream-tolist), strings (isBlank), concurrency (virtual-threads), datetime (java-time-basics) - Update generate.java and generate.py to exclude proofCode from the search index (snippets.json) since it is CI-only metadata. - Update content/template.json, html-generators/README.md, CONTRIBUTING.md, and copilot-instructions.md to document the new field. Co-authored-by: brunoborges <129743+brunoborges@users.noreply.github.com>
1 parent 6d0fa87 commit fc86786

18 files changed

+287
-6
lines changed

.github/copilot-instructions.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ Each `content/category/slug.json` file has this structure:
100100
],
101101
"docs": [
102102
{ "title": "Javadoc or Guide Title", "href": "https://docs.oracle.com/..." }
103-
]
103+
],
104+
"proofCode": "// Optional: self-contained JShell snippet proving the modern approach works.\n// Run with: jbang html-generators/proof.java"
104105
}
105106
```
106107

@@ -113,6 +114,7 @@ Each `content/category/slug.json` file has this structure:
113114
- `docs` must have at least **1** entry linking to Javadoc or Oracle documentation
114115
- `prev`/`next` are `category/slug` paths or `null` for first/last
115116
- Code in `oldCode`/`modernCode` uses `\n` for newlines
117+
- `proofCode` is optional — a self-contained JShell snippet (with default imports available) that proves the modern approach compiles and runs correctly. Run `jbang html-generators/proof.java` to verify all proof snippets.
116118

117119
## Category Display Names
118120

.github/workflows/proof.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Proof
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'content/**'
8+
- 'html-generators/proof.java'
9+
- '.github/workflows/proof.yml'
10+
pull_request:
11+
paths:
12+
- 'content/**'
13+
- 'html-generators/proof.java'
14+
workflow_dispatch:
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
proof:
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v6
24+
25+
- uses: actions/setup-java@v5
26+
with:
27+
distribution: 'temurin'
28+
java-version: '25'
29+
30+
- uses: jbangdev/setup-jbang@main
31+
32+
- name: Run proof
33+
run: jbang html-generators/proof.java

CONTRIBUTING.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ Contributions are welcome! Content is managed as YAML files — never edit gener
99
3. Copy [`content/template.json`](content/template.json) as a starting point for all required fields (see the [snippet schema](.github/copilot-instructions.md) for details)
1010
4. Update the `prev`/`next` fields in adjacent pattern files to maintain navigation
1111
5. Run `jbang html-generators/generate.java` to verify your changes build correctly
12-
6. Open a pull request
12+
6. Optionally add a `proofCode` field — a self-contained JShell snippet that proves the modern approach works. Run `jbang html-generators/proof.java` to validate it.
13+
7. Open a pull request
1314

1415
Please ensure JDK version labels only reference the version where a feature became **final** (non-preview).
1516

content/collections/immutable-list-creation.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,11 @@ docs:
4747
href: "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/List.html#of()"
4848
- title: "Collections Factory Methods (JEP 269)"
4949
href: "https://openjdk.org/jeps/269"
50+
proofCode: |-
51+
var list = List.of("a", "b", "c");
52+
if (list.size() != 3) throw new AssertionError("expected 3 elements");
53+
try {
54+
list.add("d");
55+
throw new AssertionError("should have thrown UnsupportedOperationException");
56+
} catch (UnsupportedOperationException ok) {}
57+
IO.println("immutable list: OK - " + list);

content/concurrency/virtual-threads.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,13 @@ docs:
4747
href: "https://openjdk.org/jeps/444"
4848
- title: "Thread.ofVirtual()"
4949
href: "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Thread.html#ofVirtual()"
50+
proofCode: |-
51+
var results = new java.util.concurrent.CopyOnWriteArrayList<String>();
52+
var threads = new ArrayList<Thread>();
53+
for (int i = 0; i < 5; i++) {
54+
int idx = i;
55+
threads.add(Thread.startVirtualThread(() -> results.add("vt-" + idx)));
56+
}
57+
for (var t : threads) t.join();
58+
if (results.size() != 5) throw new AssertionError("expected 5 results, got: " + results.size());
59+
IO.println("virtual threads: OK - " + results.size() + " threads ran");

content/datetime/java-time-basics.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,12 @@ docs:
5151
href: "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/LocalTime.html"
5252
- title: "LocalDateTime"
5353
href: "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/LocalDateTime.html"
54+
proofCode: |-
55+
import java.time.*;
56+
var date = LocalDate.of(2025, Month.JANUARY, 15);
57+
var time = LocalTime.of(14, 30);
58+
if (date.getMonthValue() != 1) throw new AssertionError("expected January=1, got: " + date.getMonthValue());
59+
if (time.getHour() != 14) throw new AssertionError("expected hour=14, got: " + time.getHour());
60+
var now = Instant.now();
61+
if (now == null) throw new AssertionError("Instant.now() returned null");
62+
IO.println("java.time basics: OK - " + date + " " + time);

content/language/pattern-matching-instanceof.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,11 @@ related:
4444
docs:
4545
- title: "Pattern Matching for instanceof (JEP 394)"
4646
href: "https://openjdk.org/jeps/394"
47+
proofCode: |-
48+
Object obj = "Hello, pattern matching!";
49+
if (obj instanceof String s) {
50+
IO.println("length=" + s.length());
51+
} else {
52+
throw new AssertionError("should have matched String");
53+
}
54+
IO.println("pattern matching instanceof: OK");

content/language/records-for-data-classes.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,8 @@ docs:
4646
href: "https://openjdk.org/jeps/395"
4747
- title: "Record class"
4848
href: "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Record.html"
49+
proofCode: |-
50+
public record Point(int x, int y) {}
51+
var p = new Point(1, 2);
52+
if (p.x() != 1 || p.y() != 2) throw new AssertionError("accessor mismatch");
53+
IO.println("records: OK - " + p);

content/language/switch-expressions.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,12 @@ related:
5353
docs:
5454
- title: "Switch Expressions (JEP 361)"
5555
href: "https://openjdk.org/jeps/361"
56+
proofCode: |-
57+
var day = "MONDAY";
58+
String msg = switch (day) {
59+
case "MONDAY" -> "Start";
60+
case "FRIDAY" -> "End";
61+
default -> "Mid";
62+
};
63+
if (!"Start".equals(msg)) throw new AssertionError("unexpected: " + msg);
64+
IO.println("switch expressions: OK - " + msg);

content/language/text-blocks-for-multiline-strings.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,12 @@ docs:
4949
href: "https://openjdk.org/jeps/378"
5050
- title: "Text Blocks Guide"
5151
href: "https://docs.oracle.com/en/java/javase/25/language/text-blocks.html"
52+
proofCode: |-
53+
String json = """
54+
{
55+
"name": "Duke",
56+
"age": 30
57+
}""";
58+
if (!json.contains("Duke")) throw new AssertionError("text block content missing");
59+
if (json.contains("\\n")) throw new AssertionError("should not contain literal \\n");
60+
IO.println("text blocks: OK");

0 commit comments

Comments
 (0)