From 4afb7f4b573ddc4320a2b11d7f2d26f439815476 Mon Sep 17 00:00:00 2001 From: Werner Dietl Date: Sat, 10 Oct 2026 04:45:44 -0400 Subject: [PATCH] Add unit tests for the site generator The generator had no tests, so the CI build only checked that it compiles. Its helpers that do not need the network -- the date formatting, the conversion of the release-shipped page into a Jekyll page, the lookup of the packaged templates, and the filling in of the Annotation File Utilities placeholders -- could regress unnoticed until a scheduled publish ran against the live site. Add JUnit 5 and tests for those helpers. One test checks that the version marker that the Checker Framework release scripts read from the site survives the conversion of the shipped page. Surefire is pinned so that Renovate keeps it up to date. Co-Authored-By: Claude Opus 5.5 --- pom.xml | 12 ++ .../eisop/website/EisopSiteGeneratorTest.java | 112 ++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/test/java/io/github/eisop/website/EisopSiteGeneratorTest.java diff --git a/pom.xml b/pom.xml index 1e6b4d6d..feada252 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,12 @@ zip4j 2.11.6 + + org.junit.jupiter + junit-jupiter + 5.13.4 + test + @@ -54,6 +60,12 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.4 + + com.diffplug.spotless spotless-maven-plugin diff --git a/src/test/java/io/github/eisop/website/EisopSiteGeneratorTest.java b/src/test/java/io/github/eisop/website/EisopSiteGeneratorTest.java new file mode 100644 index 00000000..859134c0 --- /dev/null +++ b/src/test/java/io/github/eisop/website/EisopSiteGeneratorTest.java @@ -0,0 +1,112 @@ +/* (C)2026 */ +package io.github.eisop.website; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +/** Tests for the parts of {@link EisopSiteGenerator} that do not need the network. */ +class EisopSiteGeneratorTest { + + @Test + void readableDateFromGitHubTimestamp() { + assertEquals( + "April 26, 2026", EisopSiteGenerator.getReadableDate("2026-04-26T23:05:21Z")); + assertEquals( + "December 20, 2024", EisopSiteGenerator.getReadableDate("2024-12-20T23:39:45Z")); + assertEquals( + "January 31, 2025", EisopSiteGenerator.getReadableDate("2025-01-31T00:00:00Z")); + } + + @Test + void themeShippedPageKeepsOnlyTheBody() { + String html = + "\n\nT\n" + + "\n

Download: a.zip

\n" + + "\n\n"; + assertEquals( + "---\nlayout: default\n---\n

Download: a.zip

\n", + EisopSiteGenerator.themeShippedPage(html)); + } + + @Test + void themeShippedPageWithoutBodyTagsKeepsEverything() { + assertEquals( + "---\nlayout: default\n---\n

text

\n", + EisopSiteGenerator.themeShippedPage("

text

")); + } + + @Test + void themeShippedPageKeepsTheVersionMarker() { + // The release scripts of the Checker Framework read the current version from this marker. + String marker = + "checker-framework-1.2.3.zip" + + ""; + String themed = + EisopSiteGenerator.themeShippedPage( + "" + marker + ""); + assertTrue(themed.contains(marker), themed); + } + + @Test + void templatesArePackaged() throws IOException { + String cf = EisopSiteGenerator.readTemplate("cf-template.md"); + assertTrue(cf.contains("$LatestCheckerFrameworkReleaseZip")); + assertTrue(cf.contains("$LatestAnnotationFileUtilitiesReleaseDownloadLink")); + String afu = EisopSiteGenerator.readTemplate("/afu-template.md"); + assertTrue(afu.contains("$LatestAnnotationFileUtilitiesReleaseZip")); + } + + @Test + void missingTemplateIsAnError() { + assertThrows(IOException.class, () -> EisopSiteGenerator.readTemplate("no-such.md")); + } + + @Test + void afuPlaceholdersAreFilledOnEveryReleasePage(@TempDir Path cfDir) throws IOException { + String page = + "[$LatestAnnotationFileUtilitiesReleaseZip]" + + "($LatestAnnotationFileUtilitiesReleaseDownloadLink)" + + " ($LatestAnnotationFileUtilitiesReleaseDate)\n"; + String filled = "[afu-1.zip](/afu/afu-1.zip) (May 1, 2026)\n"; + Path top = write(cfDir.resolve("index.md"), page); + Path release = write(cfDir.resolve("checker-framework-1/index.md"), page); + Path other = write(cfDir.resolve("checker-framework-1/other.md"), page); + + for (int run = 0; run < 2; run++) { + // The second run checks that the substitution is idempotent. + EisopSiteGenerator.substituteAfuPlaceholders( + cfDir.toFile(), "afu-1.zip", "/afu/afu-1.zip", "May 1, 2026"); + assertEquals(filled, read(top)); + assertEquals(filled, read(release)); + assertEquals(page, read(other)); + } + } + + @Test + void afuPlaceholdersToleratesMissingPages(@TempDir Path cfDir) throws IOException { + Files.createDirectories(cfDir.resolve("checker-framework-1")); + EisopSiteGenerator.substituteAfuPlaceholders( + cfDir.toFile(), "afu-1.zip", "/afu/afu-1.zip", "May 1, 2026"); + EisopSiteGenerator.substituteAfuPlaceholders( + new File(cfDir.toFile(), "missing"), "afu-1.zip", "/afu/afu-1.zip", "May 1, 2026"); + } + + private static Path write(Path file, String content) throws IOException { + Files.createDirectories(file.getParent()); + return Files.writeString(file, content, StandardCharsets.UTF_8); + } + + private static String read(Path file) throws IOException { + return Files.readString(file, StandardCharsets.UTF_8); + } +}